357234: Add UI buttons to remove reviewers from change

Task-Url: https://bugs.eclipse.org/bugs/show_bug.cgi?id=357234

Change-Id: Iac12676d3b4d817e81274cb8c2e2fc60dc967ae8
Signed-off-by: RileyDavidson <rd12hq@brocku.ca>
diff --git a/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/operations/Messages.java b/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/operations/Messages.java
index 2b7d24d..8c19ec4 100644
--- a/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/operations/Messages.java
+++ b/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/operations/Messages.java
@@ -38,6 +38,8 @@
 
 	public static String GerritOperation_Discarding_Draft;
 
+	public static String RemoveReviewerRequest_Remove_Reviewer;
+
 	static {
 		// initialize resource bundle
 		NLS.initializeMessages(BUNDLE_NAME, Messages.class);
diff --git a/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/operations/RemoveReviewerRequest.java b/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/operations/RemoveReviewerRequest.java
new file mode 100644
index 0000000..c216d55
--- /dev/null
+++ b/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/operations/RemoveReviewerRequest.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Tasktop Technologies and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Tasktop Technologies - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.gerrit.core.operations;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.mylyn.internal.gerrit.core.client.GerritClient;
+import org.eclipse.mylyn.internal.gerrit.core.client.GerritException;
+
+import com.google.gerrit.common.data.ReviewerResult;
+
+public class RemoveReviewerRequest extends AbstractRequest<ReviewerResult> {
+
+	private final String reviewer;
+
+	private final String reviewId;
+
+	public RemoveReviewerRequest(String reviewId, String reviewer) {
+		Assert.isNotNull(reviewId);
+		Assert.isNotNull(reviewer);
+		this.reviewId = reviewId;
+		this.reviewer = reviewer;
+	}
+
+	public String getReviewerToRemove() {
+		return this.reviewer;
+	}
+
+	public String getReviewId() {
+		return this.reviewId;
+	}
+
+	@Override
+	protected ReviewerResult execute(GerritClient client, IProgressMonitor monitor) throws GerritException {
+		return client.removeReviewer(getReviewId(), getReviewerToRemove(), monitor);
+	}
+
+	@Override
+	public String getOperationName() {
+		return Messages.RemoveReviewerRequest_Remove_Reviewer;
+	}
+
+}
diff --git a/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/operations/messages.properties b/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/operations/messages.properties
index 272b8cc..394a2c8 100644
--- a/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/operations/messages.properties
+++ b/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/operations/messages.properties
@@ -19,3 +19,4 @@
 GerritOperation_Saving_Draft=Saving Draft
 GerritOperation_Submitting_Change=Submitting Change
 GerritOperation_Discarding_Draft=Discarding Draft
+RemoveReviewerRequest_Remove_Reviewer=Removing Reviewer
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/GerritReviewDetailSection.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/GerritReviewDetailSection.java
index 18154c4..be8f0d8 100644
--- a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/GerritReviewDetailSection.java
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/GerritReviewDetailSection.java
@@ -13,15 +13,17 @@
 
 package org.eclipse.mylyn.internal.gerrit.ui.editor;
 
+import org.eclipse.mylyn.internal.gerrit.ui.factories.GerritReviewerUiFactoryProvider;
 import org.eclipse.mylyn.internal.gerrit.ui.factories.ReviewUiFactoryProvider;
 import org.eclipse.mylyn.reviews.core.model.IReview;
+import org.eclipse.mylyn.reviews.core.model.IUser;
 import org.eclipse.mylyn.reviews.core.model.ReviewStatus;
 import org.eclipse.mylyn.reviews.ui.spi.editor.ReviewDetailSection;
 import org.eclipse.mylyn.reviews.ui.spi.factories.AbstractUiFactoryProvider;
 
 /**
  * Displays basic information about a given review corresponding to top sections of Gerrit web interface.
- * 
+ *
  * @author Steffen Pingel
  * @author Miles Parker
  */
@@ -33,7 +35,13 @@
 	}
 
 	@Override
+	protected AbstractUiFactoryProvider<IUser> getReviewerUiFactoryProvider() {
+		return new GerritReviewerUiFactoryProvider();
+	}
+
+	@Override
 	protected boolean canAddReviewers() {
 		return getReview().getState() == ReviewStatus.DRAFT || getReview().getState() == ReviewStatus.NEW;
 	}
+
 }
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/factories/GerritReviewerUiFactoryProvider.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/factories/GerritReviewerUiFactoryProvider.java
new file mode 100644
index 0000000..c818cdd
--- /dev/null
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/factories/GerritReviewerUiFactoryProvider.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Tasktop Technologies and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Tasktop Technologies - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.gerrit.ui.factories;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.mylyn.reviews.core.model.IUser;
+import org.eclipse.mylyn.reviews.ui.spi.factories.AbstractUiFactory;
+import org.eclipse.mylyn.reviews.ui.spi.factories.AbstractUiFactoryProvider;
+import org.eclipse.mylyn.reviews.ui.spi.factories.IUiContext;
+
+public class GerritReviewerUiFactoryProvider extends AbstractUiFactoryProvider<IUser> {
+
+	@Override
+	public List<AbstractUiFactory<IUser>> createFactories(IUiContext context, IUser type) {
+		List<AbstractUiFactory<IUser>> factories = new ArrayList<AbstractUiFactory<IUser>>();
+		factories.add(new RemoveReviewerUiFactory(context, type));
+		return factories;
+	}
+
+}
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/factories/Messages.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/factories/Messages.java
index 64fe965..aa96331 100644
--- a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/factories/Messages.java
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/factories/Messages.java
@@ -60,6 +60,10 @@
 
 	public static String RebaseUiFactory_Rebase;
 
+	public static String RemoveReviewerUiFactory_Remove_Reviewer;
+
+	public static String RemoveReviewerUiFactory_Remove_Reviewer_Name;
+
 	public static String RestoreUiFactory_Restore;
 
 	public static String SubmitUiFactory_Submit;
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/factories/RemoveReviewerUiFactory.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/factories/RemoveReviewerUiFactory.java
new file mode 100644
index 0000000..4f1fb35
--- /dev/null
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/factories/RemoveReviewerUiFactory.java
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Tasktop Technologies and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Tasktop Technologies - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.gerrit.ui.factories;
+
+import org.eclipse.mylyn.commons.ui.CommonImages;
+import org.eclipse.mylyn.internal.gerrit.ui.operations.RemoveReviewerDialog;
+import org.eclipse.mylyn.reviews.core.model.IReview;
+import org.eclipse.mylyn.reviews.core.model.IUser;
+import org.eclipse.mylyn.reviews.core.model.ReviewStatus;
+import org.eclipse.mylyn.reviews.ui.spi.editor.ReviewDetailSection;
+import org.eclipse.mylyn.reviews.ui.spi.factories.AbstractUiFactory;
+import org.eclipse.mylyn.reviews.ui.spi.factories.IUiContext;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.ui.forms.events.HyperlinkAdapter;
+import org.eclipse.ui.forms.events.HyperlinkEvent;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.ImageHyperlink;
+
+public class RemoveReviewerUiFactory extends AbstractUiFactory<IUser> {
+
+	public RemoveReviewerUiFactory(IUiContext context, IUser object) {
+		super(Messages.RemoveReviewerUiFactory_Remove_Reviewer, context, object);
+	}
+
+	@Override
+	public Control createControl(IUiContext context, Composite parent, FormToolkit toolkit) {
+		ImageHyperlink removeLink = toolkit.createImageHyperlink(parent, SWT.TOP);
+		removeLink.setToolTipText(
+				NLS.bind(Messages.RemoveReviewerUiFactory_Remove_Reviewer_Name, getModelObject().getDisplayName()));
+		removeLink.setImage(CommonImages.getImage(CommonImages.REMOVE));
+		removeLink.addHyperlinkListener(new HyperlinkAdapter() {
+			@Override
+			public void linkActivated(HyperlinkEvent e) {
+				execute();
+			}
+		});
+		removeLink.setEnabled(!isExecutableStateKnown() || isExecutable());
+		return removeLink;
+	}
+
+	@Override
+	protected boolean isExecutableStateKnown() {
+		return true;
+	}
+
+	@Override
+	public boolean isExecutable() {
+		ReviewStatus currentStatus = getReview().getState();
+		return getModelObject() != null && getModelObject().getId() != null && currentStatus != ReviewStatus.ABANDONED
+				&& currentStatus != ReviewStatus.MERGED;
+	}
+
+	@Override
+	public void execute() {
+		new RemoveReviewerDialog(getShell(), getTask(), getModelObject()).open(getEditor());
+	}
+
+	private IReview getReview() {
+		return ((ReviewDetailSection) getContext()).getReview();
+	}
+
+}
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/factories/messages.properties b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/factories/messages.properties
index ee705ee..08fbdc8 100644
--- a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/factories/messages.properties
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/factories/messages.properties
@@ -30,5 +30,7 @@
 PublishUiFactory_Error_while_clearing_status=Error while clearing task status.
 PublishUiFactory_Publish_Comments=Publish Comments...
 RebaseUiFactory_Rebase=Rebase
+RemoveReviewerUiFactory_Remove_Reviewer=Remove Reviewer
+RemoveReviewerUiFactory_Remove_Reviewer_Name=Remove {0}
 RestoreUiFactory_Restore=Restore...
 SubmitUiFactory_Submit=Submit
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/operations/Messages.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/operations/Messages.java
index bf26fd7..bf37a0e 100644
--- a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/operations/Messages.java
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/operations/Messages.java
@@ -44,6 +44,10 @@
 
 	public static String RebaseDialog_Rebase_patch_set_X;
 
+	public static String RemoveReviewerDialog_Are_You_Sure_You_Want_To_Remove;
+
+	public static String RemoveReviewerDialog_Remove_Reviewer;
+
 	public static String RestoreDialog_Enter_message;
 
 	public static String RestoreDialog_Restore_Change;
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/operations/RemoveReviewerDialog.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/operations/RemoveReviewerDialog.java
new file mode 100644
index 0000000..4e50eef
--- /dev/null
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/operations/RemoveReviewerDialog.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Tasktop Technologies and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Tasktop Technologies - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.gerrit.ui.operations;
+
+import org.eclipse.mylyn.internal.gerrit.core.operations.GerritOperation;
+import org.eclipse.mylyn.internal.gerrit.core.operations.RemoveReviewerRequest;
+import org.eclipse.mylyn.internal.gerrit.ui.GerritUiPlugin;
+import org.eclipse.mylyn.reviews.core.model.IUser;
+import org.eclipse.mylyn.tasks.core.ITask;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+
+import com.google.gerrit.common.data.ReviewerResult;
+
+public class RemoveReviewerDialog extends GerritOperationDialog {
+
+	private final IUser userToRemove;
+
+	public RemoveReviewerDialog(Shell parentShell, ITask task, IUser user) {
+		super(parentShell, task);
+		this.userToRemove = user;
+	}
+
+	@Override
+	public GerritOperation<ReviewerResult> createOperation() {
+		RemoveReviewerRequest request = new RemoveReviewerRequest(getTask().getTaskId(), userToRemove.getId());
+		return GerritUiPlugin.getDefault().getOperationFactory().createOperation(task, request);
+	}
+
+	@Override
+	protected Control createPageControls(Composite parent) {
+
+		setTitle(Messages.RemoveReviewerDialog_Remove_Reviewer);
+		Composite composite = new Composite(parent, SWT.NONE);
+		composite.setLayout(new GridLayout(1, false));
+
+		Label label = new Label(composite, SWT.NONE);
+		label.setText(
+				NLS.bind(Messages.RemoveReviewerDialog_Are_You_Sure_You_Want_To_Remove, userToRemove.getDisplayName()));
+
+		return parent;
+	}
+
+	@Override
+	protected boolean processOperationResult(GerritOperation<?> operation) {
+		Object result = operation.getOperationResult();
+		if (result instanceof ReviewerResult) {
+			ReviewerResult reviewerResult = (ReviewerResult) result;
+			if (reviewerResult.getErrors() != null && reviewerResult.getErrors().size() > 0) {
+				setErrorMessage(reviewerResult.getErrors().toString());
+				return false;
+			}
+		}
+		return super.processOperationResult(operation);
+	}
+
+}
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/operations/messages.properties b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/operations/messages.properties
index 249c804..a939f84 100644
--- a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/operations/messages.properties
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/operations/messages.properties
@@ -22,6 +22,8 @@
 CherryPickDialog_Cherry_Pick_Commit_Message=Cherry Pick Commit Message:
 RebaseDialog_Rebase_Patch_Set=Rebase Patch Set
 RebaseDialog_Rebase_patch_set_X=Rebase patch set {0}
+RemoveReviewerDialog_Are_You_Sure_You_Want_To_Remove= This will remove "{0}" from this review. Proceed?
+RemoveReviewerDialog_Remove_Reviewer=Remove Reviewer
 RestoreDialog_Enter_message=Enter an optional message.
 RestoreDialog_Restore_Change=Restore Change
 SubmitDialog_Submit_Change=Submit Change
diff --git a/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/editor/ReviewDetailSection.java b/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/editor/ReviewDetailSection.java
index 4cf4163..dcd103b 100644
--- a/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/editor/ReviewDetailSection.java
+++ b/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/editor/ReviewDetailSection.java
@@ -48,7 +48,7 @@
 
 /**
  * Displays basic information about a given review corresponding to top sections of Gerrit web interface.
- * 
+ *
  * @author Steffen Pingel
  * @author Miles Parker
  */
@@ -62,8 +62,10 @@
 	protected Control createContent(FormToolkit toolkit, Composite parent) {
 		Control content = super.createContent(toolkit, parent);
 		createReviewersSubSection(composite);
-		createDependenciesSubSection(toolkit, composite, Messages.ReviewDetailSection_Depends_On, getReview().getParents());
-		createDependenciesSubSection(toolkit, composite, Messages.ReviewDetailSection_Needed_By, getReview().getChildren());
+		createDependenciesSubSection(toolkit, composite, Messages.ReviewDetailSection_Depends_On,
+				getReview().getParents());
+		createDependenciesSubSection(toolkit, composite, Messages.ReviewDetailSection_Needed_By,
+				getReview().getChildren());
 		return content;
 	}
 
@@ -142,11 +144,12 @@
 				}
 			}
 
-			for (Entry<IUser, IReviewerEntry> entry : getReview().getReviewerApprovals().entrySet()) {
+			AbstractUiFactoryProvider<IUser> reviewerUiFactoryProvider = getReviewerUiFactoryProvider();
 
-				Label reviewerRowLabel = new Label(composite, SWT.NONE);
-				reviewerRowLabel.setForeground(toolkit.getColors().getColor(IFormColors.TITLE));
-				reviewerRowLabel.setText(entry.getKey().getDisplayName());
+			for (Entry<IUser, IReviewerEntry> entry : getReview().getReviewerApprovals().entrySet()) {
+				IUser currentUser = entry.getKey();
+
+				createReviewerLabelAndControls(composite, reviewerUiFactoryProvider, currentUser);
 
 				for (IApprovalType approvalType : approvalTypesWithLabel) {
 					Integer value = entry.getValue().getApprovals().get(approvalType);
@@ -168,7 +171,7 @@
 				if (names.length() > 0) {
 					names.append(", "); //$NON-NLS-1$
 				}
-				names.append(entry.getKey().getDisplayName());
+				names.append(currentUser.getDisplayName());
 			}
 
 			String headerText = names.toString();
@@ -184,6 +187,31 @@
 					getReview());
 			GridDataFactory.fillDefaults().span(2, 1).applyTo(actionComposite);
 		}
+
+	}
+
+	private void createReviewerLabelAndControls(Composite parent,
+			AbstractUiFactoryProvider<IUser> reviewerUiFactoryProvider, IUser user) {
+		Composite reviewerComp = toolkit.createComposite(parent, SWT.NONE);
+		GridLayoutFactory.fillDefaults().numColumns(2).equalWidth(false).applyTo(reviewerComp);
+
+		if (reviewerUiFactoryProvider != null) {
+			createReviewerControls(reviewerComp, user, reviewerUiFactoryProvider);
+		}
+		createReviewerLabel(reviewerComp, user);
+	}
+
+	private void createReviewerLabel(Composite reviewerComposite, IUser user) {
+		Label reviewerRowLabel = new Label(reviewerComposite, SWT.NONE);
+		reviewerRowLabel.setForeground(toolkit.getColors().getColor(IFormColors.TITLE));
+		reviewerRowLabel.setText(user.getDisplayName());
+	}
+
+	private void createReviewerControls(Composite reviewerComposite, IUser user,
+			AbstractUiFactoryProvider<IUser> reviewerUiFactoryProvider) {
+		Composite controlComposite = reviewerUiFactoryProvider.createControls(this, reviewerComposite, getToolkit(),
+				user);
+		GridDataFactory.fillDefaults().applyTo(controlComposite);
 	}
 
 	protected boolean canAddReviewers() {
@@ -209,11 +237,13 @@
 
 		for (final IChange change : changes) {
 			Link link = new Link(composite, SWT.NONE);
-			String changeStatus = change.getState() != null ? NLS.bind(Messages.ReviewDetailSection_Bracket_X_bracket,
-					String.valueOf(change.getState().getName())) : " "; //$NON-NLS-1$
+			String changeStatus = change.getState() != null
+					? NLS.bind(Messages.ReviewDetailSection_Bracket_X_bracket,
+							String.valueOf(change.getState().getName()))
+					: " "; //$NON-NLS-1$
 			String ownerName = change.getOwner().getDisplayName();
-			link.setText(NLS.bind(Messages.ReviewDetailSection_Link_W_X_Y_by_Z, new String[] { StringUtils.left(change.getKey(), 9),
-					change.getSubject(), ownerName, changeStatus }));
+			link.setText(NLS.bind(Messages.ReviewDetailSection_Link_W_X_Y_by_Z, new String[] {
+					StringUtils.left(change.getKey(), 9), change.getSubject(), ownerName, changeStatus }));
 			link.addSelectionListener(new SelectionAdapter() {
 				@Override
 				public void widgetSelected(SelectionEvent e) {
@@ -225,6 +255,10 @@
 
 	protected abstract AbstractUiFactoryProvider<IReview> getUiFactoryProvider();
 
+	protected AbstractUiFactoryProvider<IUser> getReviewerUiFactoryProvider() {
+		return null;
+	};
+
 	@Override
 	protected boolean shouldExpandOnCreate() {
 		return true;