Tuleap # 788 - adjust the dynamic column for the reply dialog

Adjust the reply dialog re-sizing
Read labels information from model
Adjust the reply menu dynamically
Create method to get the last user votes
Add an option to select all allowed menu which can be set to
the maximum review value for the user.

In the editor, if there is no dynamic reply option, selecting the
"Reply" button will automatically open the reply dialogue

Change-Id: I693adf9bfbdf43d96bd44826d8176a8e93355b55
Signed-off-by: Jacques Bouthillier <lmcbout@gmail.com>
diff --git a/org.eclipse.egerrit.core/src/org/eclipse/egerrit/internal/core/rest/ReviewInput.java b/org.eclipse.egerrit.core/src/org/eclipse/egerrit/internal/core/rest/ReviewInput.java
index 4c47d7a..8bff6b9 100644
--- a/org.eclipse.egerrit.core/src/org/eclipse/egerrit/internal/core/rest/ReviewInput.java
+++ b/org.eclipse.egerrit.core/src/org/eclipse/egerrit/internal/core/rest/ReviewInput.java
@@ -30,7 +30,7 @@
 	private String message;
 
 	// The labels of the review as a map that maps the label names to the voting values.
-	private Map<String, String> labels;
+	private Map<String, Integer> labels;
 
 	// The comments that should be added as a map that maps a file path to a list of
 	// CommentInput entities
@@ -84,16 +84,16 @@
 	/**
 	 * @return the labels
 	 */
-	public Map<String, String> getLabels() {
+	public Map<String, Integer> getLabels() {
 		return labels;
 	}
 
 	/**
-	 * @param labels
+	 * @param map
 	 *            the labels to set
 	 */
-	public void setLabels(Map<String, String> labels) {
-		this.labels = labels;
+	public void setLabels(Map<String, Integer> map) {
+		this.labels = map;
 	}
 
 	/**
diff --git a/org.eclipse.egerrit.dashboard.ui/plugin.xml b/org.eclipse.egerrit.dashboard.ui/plugin.xml
index 24854ff..1b1c815 100644
--- a/org.eclipse.egerrit.dashboard.ui/plugin.xml
+++ b/org.eclipse.egerrit.dashboard.ui/plugin.xml
@@ -46,7 +46,7 @@
           <dynamic
                 class="org.eclipse.egerrit.internal.dashboard.ui.menus.DynamicMenuAddition"
                 id="org.eclipse.egerrit.dashboard.ui.dynamic1">
-          </dynamic>
+           </dynamic>
           <separator
                 name="additions"
                 visible="true">
@@ -264,13 +264,12 @@
                 style="push"
                 tooltip="%command.tooltipReply">
              </command>
-             <command
-                commandId="org.eclipse.egerrit.dashboard.ui.replyHandlerPlus2"
-                id="org.eclipse.egerrit.dashboard.ui.replyHandlerPlus2"
-                label="%command.name.replyPlus2"
-                style="push"
-                tooltip="%command.tooltipReplyPlus2">
-             </command>
+             <separator
+                  name="additions"
+                  visible="true">
+            </separator>
+             <dynamic class="org.eclipse.egerrit.internal.dashboard.ui.utils.VoteMenuGenerator"
+                    id="org.eclipse.egerrit.dashboard.ui.votes.generator" />
           </menu>
        </menuContribution>
        <menuContribution
@@ -396,6 +395,20 @@
             	id="org.eclipse.egerrit.dashboard.ui.adjustMyStarred"
             	name="%command.name.starReview">
        </command>
+       <command
+             id="org.eclipse.egerrit.dashboard.ui.vote"
+             name="vote">
+          <commandParameter
+                id="org.eclipse.egerrit.dashboard.ui.vote.label"
+                name="voteLabel"
+                optional="false">
+          </commandParameter>
+          <commandParameter
+                id="org.eclipse.egerrit.dashboard.ui.vote.value"
+                name="voteValue"
+                optional="false">
+          </commandParameter>
+       </command>
    </extension>
 
    <extension
@@ -503,10 +516,10 @@
 				commandId="org.eclipse.egerrit.dashboard.ui.replyDialog">
        </handler>
 
-		<!-- Reply Plus 2 -->
-	      <handler
-				class="org.eclipse.egerrit.internal.dashboard.ui.commands.table.ReplyPlusTwoHandler" 
-				commandId="org.eclipse.egerrit.dashboard.ui.replyHandlerPlus2">
+       <!-- Reply voting handler -->
+       <handler
+             class="org.eclipse.egerrit.internal.dashboard.ui.utils.VoteHandler"
+             commandId="org.eclipse.egerrit.dashboard.ui.vote">
        </handler>
 
 	      <!-- Submit sub-menu-->
diff --git a/org.eclipse.egerrit.dashboard.ui/src/org/eclipse/egerrit/internal/dashboard/ui/commands/table/ReplyPlusTwoHandler.java b/org.eclipse.egerrit.dashboard.ui/src/org/eclipse/egerrit/internal/dashboard/ui/commands/table/ReplyPlusTwoHandler.java
deleted file mode 100644
index 72eeb2e..0000000
--- a/org.eclipse.egerrit.dashboard.ui/src/org/eclipse/egerrit/internal/dashboard/ui/commands/table/ReplyPlusTwoHandler.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*******************************************************************************

- * Copyright (c) 2016 Ericsson AB.

- * 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:

- *     Ericsson - initial API and implementation

- *******************************************************************************/

-

-package org.eclipse.egerrit.internal.dashboard.ui.commands.table;

-

-import java.util.Map;

-

-import org.eclipse.core.commands.ExecutionEvent;

-import org.eclipse.core.commands.ExecutionException;

-import org.eclipse.egerrit.internal.process.ReplyProcess;

-import org.eclipse.egerrit.internal.ui.utils.Messages;

-import org.eclipse.osgi.util.NLS;

-import org.eclipse.ui.menus.UIElement;

-

-/**

- * This class implements a handler to Reply +2 for the latest branch of the selected review

- */

-public class ReplyPlusTwoHandler extends DashboardFactoryHandler {

-

-	private ReplyProcess replyProcess = new ReplyProcess();

-

-	@Override

-	public Object execute(ExecutionEvent event) throws ExecutionException {

-		// Execute the reply +2 if we have the information

-		replyProcess.handleReplyPlus2(getLatestRevision());

-		return null;

-	}

-

-	@Override

-	public void setEnabled(Object evaluationContext) {

-		super.setEnabled(evaluationContext);

-		if (getChangeInfo() != null && getGerritClient() != null) {

-			setBaseEnabled(

-					replyProcess.isCRPlusTwoAllowed(getChangeInfo(), getGerritClient(), getChangeInfo().getRevision()));

-		} else {

-			setBaseEnabled(false);

-		}

-	}

-

-	@Override

-	public void updateElement(UIElement element, Map parameters) {

-		super.updateElement(element, parameters);

-		String message = Messages.ReplyPlusTwoHandler_generalMessage;

-		if (getChangeInfo() != null && getGerritClient() != null) {

-			message = NLS.bind(Messages.ReplyPlusTwoHandler_specificMessage,

-					new Object[] { getChangeInfo().get_number(), getLatestRevision().get_number() });

-		}

-		element.setTooltip(message);

-	}

-}

diff --git a/org.eclipse.egerrit.dashboard.ui/src/org/eclipse/egerrit/internal/dashboard/ui/utils/UIConstants.java b/org.eclipse.egerrit.dashboard.ui/src/org/eclipse/egerrit/internal/dashboard/ui/utils/UIConstants.java
index ca0e8a9..65badca 100644
--- a/org.eclipse.egerrit.dashboard.ui/src/org/eclipse/egerrit/internal/dashboard/ui/utils/UIConstants.java
+++ b/org.eclipse.egerrit.dashboard.ui/src/org/eclipse/egerrit/internal/dashboard/ui/utils/UIConstants.java
@@ -25,4 +25,11 @@
 
 	public static final String SELECT_SERVER_COMMAND_ID = "org.eclipse.egerrit.dashboard.ui.selectCurrentGerrit"; //$NON-NLS-1$
 
+	public static final String REPLY_COMMAND_ID = "org.eclipse.egerrit.internal.dashboard.ui.commands.table.replyHandlerDynamic"; //$NON-NLS-1$
+
+	public static final String REPLY_COMMAND_VOTE_ID = "org.eclipse.egerrit.dashboard.ui.vote"; //$NON-NLS-1$
+
+	public static final String REPLY_COMMAND_ID_LABEL_PARAM = "org.eclipse.egerrit.dashboard.ui.vote.label"; //$NON-NLS-1$
+
+	public static final String REPLY_COMMAND_ID_VALUE_PARAM = "org.eclipse.egerrit.dashboard.ui.vote.value"; //$NON-NLS-1$
 }
diff --git a/org.eclipse.egerrit.dashboard.ui/src/org/eclipse/egerrit/internal/dashboard/ui/utils/VoteHandler.java b/org.eclipse.egerrit.dashboard.ui/src/org/eclipse/egerrit/internal/dashboard/ui/utils/VoteHandler.java
new file mode 100644
index 0000000..eff4208
--- /dev/null
+++ b/org.eclipse.egerrit.dashboard.ui/src/org/eclipse/egerrit/internal/dashboard/ui/utils/VoteHandler.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Ericsson AB.
+ * 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:
+ *     Ericsson - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.egerrit.internal.dashboard.ui.utils;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.egerrit.internal.dashboard.ui.commands.table.DashboardFactoryHandler;
+import org.eclipse.egerrit.internal.model.ChangeInfo;
+import org.eclipse.egerrit.internal.process.ReplyProcess;
+
+/**
+ * This handler is used to manipulate the dynamic menu items related to the reply option
+ */
+public class VoteHandler extends DashboardFactoryHandler {
+
+	@Override
+	public Object execute(ExecutionEvent event) throws ExecutionException {
+
+		TreeMap<String, Integer> mapLabels = fillMapMenuLabels(
+				event.getParameter("org.eclipse.egerrit.dashboard.ui.vote.label"), //$NON-NLS-1$
+				event.getParameter("org.eclipse.egerrit.dashboard.ui.vote.value"), //$NON-NLS-1$
+				getChangeInfo());
+
+		if (!mapLabels.isEmpty()) {
+			ReplyProcess replyProcess = new ReplyProcess();
+			replyProcess.handleReplyVotes(getChangeInfo(), mapLabels, getGerritClient());
+		}
+		return null;
+	}
+
+	/**
+	 * Fill the map with selected label from the dynamic reply menu
+	 *
+	 * @param label
+	 * @param value
+	 * @param changeInfo
+	 * @return TreeMap
+	 */
+	private TreeMap<String, Integer> fillMapMenuLabels(String label, String value, ChangeInfo changeInfo) {
+		TreeMap<String, Integer> mapLabels = new TreeMap<>();
+
+		if (label.equals(ReplyProcess.REPLY_ALL_BUTTONS)) {
+			String loginUser = getGerritClient().getRepository().getServerInfo().getUserName();
+			Map<String, Integer> labelsToSet = changeInfo.getLabelsNotAtMax(loginUser);
+
+			//Loop to include all the potential buttons
+			Iterator<Entry<String, Integer>> iter = labelsToSet.entrySet().iterator();
+			while (iter.hasNext()) {
+				Entry<String, Integer> entry = iter.next();
+				mapLabels.put(entry.getKey(), entry.getValue());
+			}
+		} else {
+			mapLabels.put(label, Integer.parseInt(value));
+		}
+		return mapLabels;
+	}
+}
diff --git a/org.eclipse.egerrit.dashboard.ui/src/org/eclipse/egerrit/internal/dashboard/ui/utils/VoteMenuGenerator.java b/org.eclipse.egerrit.dashboard.ui/src/org/eclipse/egerrit/internal/dashboard/ui/utils/VoteMenuGenerator.java
new file mode 100644
index 0000000..a655a6b
--- /dev/null
+++ b/org.eclipse.egerrit.dashboard.ui/src/org/eclipse/egerrit/internal/dashboard/ui/utils/VoteMenuGenerator.java
@@ -0,0 +1,127 @@
+/*******************************************************************************

+ * Copyright (c) 2017 Ericsson AB.

+ * 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:

+ *     Ericsson - initial API and implementation

+ *******************************************************************************/

+

+package org.eclipse.egerrit.internal.dashboard.ui.utils;

+

+import java.util.ArrayList;

+import java.util.HashMap;

+import java.util.Iterator;

+import java.util.List;

+import java.util.Map;

+import java.util.Map.Entry;

+

+import org.eclipse.egerrit.internal.core.GerritClient;

+import org.eclipse.egerrit.internal.dashboard.ui.GerritUi;

+import org.eclipse.egerrit.internal.dashboard.ui.views.GerritTableView;

+import org.eclipse.egerrit.internal.model.ChangeInfo;

+import org.eclipse.egerrit.internal.process.ReplyProcess;

+import org.eclipse.jface.action.ContributionItem;

+import org.eclipse.jface.action.IContributionItem;

+import org.eclipse.jface.viewers.ISelection;

+import org.eclipse.jface.viewers.IStructuredSelection;

+import org.eclipse.ui.ISelectionService;

+import org.eclipse.ui.IViewPart;

+import org.eclipse.ui.IWorkbench;

+import org.eclipse.ui.IWorkbenchPage;

+import org.eclipse.ui.actions.CompoundContributionItem;

+import org.eclipse.ui.menus.CommandContributionItem;

+import org.eclipse.ui.menus.CommandContributionItemParameter;

+import org.eclipse.ui.menus.IWorkbenchContribution;

+import org.eclipse.ui.services.IServiceLocator;

+

+/**

+ * This class creates contribution items used in the dynamic reply menu option

+ */

+public class VoteMenuGenerator extends CompoundContributionItem implements IWorkbenchContribution {

+

+	public static final String VIEW_ID = "org.eclipse.egerrit.dashboard.ui.views.GerritTableView"; //$NON-NLS-1$

+

+	private GerritClient fGerritClient;

+

+	private IServiceLocator fServiceLocator;

+

+	@Override

+	protected IContributionItem[] getContributionItems() {

+		IContributionItem[] contributionItems = null;

+		IWorkbench workbench = GerritUi.getDefault().getWorkbench();

+		IWorkbenchPage page = workbench.getActiveWorkbenchWindow().getActivePage();

+		IViewPart viewPart = page.findView(VIEW_ID);

+		if (viewPart != null) {

+			if (viewPart instanceof GerritTableView) {

+				GerritTableView gerritTable = (GerritTableView) viewPart;

+				fGerritClient = gerritTable.getGerritClient();

+				contributionItems = getData();

+			}

+		}

+		return contributionItems;

+	}

+

+	@Override

+	public void initialize(IServiceLocator serviceLocator) {

+		fServiceLocator = serviceLocator;

+	}

+

+	private IContributionItem[] getData() {

+		ISelection selection = fServiceLocator.getService(ISelectionService.class).getSelection();

+		if (!(selection instanceof IStructuredSelection)) {

+			return new IContributionItem[0];

+		}

+		IStructuredSelection structuredSelection = (IStructuredSelection) selection;

+		Iterator<?> selections = structuredSelection.iterator();

+		while (selections.hasNext()) {

+			Object element = selections.next();

+			if (!(element instanceof ChangeInfo)) {

+				continue;

+			}

+

+			ChangeInfo changeInfo = (ChangeInfo) element;

+			//Adjust the list of label which the current user can set to a maximum value

+			String loginUser = fGerritClient.getRepository().getServerInfo().getUserName();

+			Map<String, Integer> labelsToCreateEntryFor = changeInfo.getLabelsNotAtMax(loginUser);

+			if (labelsToCreateEntryFor.isEmpty()) {

+				return new IContributionItem[0];

+			}

+

+			List<IContributionItem> menusForVotes = new ArrayList<>();

+			for (Entry<String, Integer> label : labelsToCreateEntryFor.entrySet()) {

+				menusForVotes.add(createItem(label.getKey(), String.valueOf(label.getValue()),

+						label.getKey() + "  +" + label.getValue())); //$NON-NLS-1$

+			}

+

+			//Add a selection for all buttons when there is more than one selection

+			if (menusForVotes.size() > 1) {

+				menusForVotes.add(createItem(ReplyProcess.REPLY_ALL_BUTTONS, null, ReplyProcess.REPLY_ALL_BUTTONS));

+			}

+			return menusForVotes.toArray(new IContributionItem[menusForVotes.size()]);

+		}

+		return new IContributionItem[0];

+	}

+

+	/**

+	 * Create each contribution item

+	 *

+	 * @param key

+	 * @param value

+	 * @param menuLabel

+	 * @return CommandContributionItem

+	 */

+	private ContributionItem createItem(String key, String value, String menuLabel) {

+		//Add the label value as parameters

+		Map<String, String> params = new HashMap<String, String>();

+		params.put(UIConstants.REPLY_COMMAND_ID_LABEL_PARAM, key);

+		params.put(UIConstants.REPLY_COMMAND_ID_VALUE_PARAM, value);

+		CommandContributionItemParameter menuEntry = new CommandContributionItemParameter(fServiceLocator, menuLabel,

+				UIConstants.REPLY_COMMAND_VOTE_ID, params, null, null, null, menuLabel, menuLabel, menuLabel,

+				CommandContributionItem.STYLE_PUSH, null, true);

+		return new CommandContributionItem(menuEntry);

+	}

+

+}

diff --git a/org.eclipse.egerrit.model/model/egerrit.ecore b/org.eclipse.egerrit.model/model/egerrit.ecore
index f28ae4f..637598a 100644
--- a/org.eclipse.egerrit.model/model/egerrit.ecore
+++ b/org.eclipse.egerrit.model/model/egerrit.ecore
@@ -201,6 +201,37 @@
     <eOperations name="getLabelMaxValue" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt">
       <eParameters name="label" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
     </eOperations>
+    <eOperations name="getPermittedMaxValue" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt">
+      <eParameters name="label" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+    </eOperations>
+    <eOperations name="getSortedPermittedLabels">
+      <eGenericType eClassifier="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EMap">
+        <eTypeArguments eClassifier="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+        <eTypeArguments eClassifier="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EEList">
+          <eTypeArguments eClassifier="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+        </eTypeArguments>
+      </eGenericType>
+    </eOperations>
+    <eOperations name="getAllowedLabelsMaxValue">
+      <eGenericType eClassifier="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EMap">
+        <eTypeArguments eClassifier="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+        <eTypeArguments eClassifier="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/>
+      </eGenericType>
+    </eOperations>
+    <eOperations name="getUserLastLabelSet">
+      <eGenericType eClassifier="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EMap">
+        <eTypeArguments eClassifier="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+        <eTypeArguments eClassifier="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/>
+      </eGenericType>
+      <eParameters name="user" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+    </eOperations>
+    <eOperations name="getLabelsNotAtMax">
+      <eGenericType eClassifier="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EMap">
+        <eTypeArguments eClassifier="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+        <eTypeArguments eClassifier="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/>
+      </eGenericType>
+      <eParameters name="loginUser" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+    </eOperations>
     <eStructuralFeatures xsi:type="ecore:EAttribute" name="kind" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
     <eStructuralFeatures xsi:type="ecore:EAttribute" name="id" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
     <eStructuralFeatures xsi:type="ecore:EAttribute" name="project" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
diff --git a/org.eclipse.egerrit.model/src-gen/org/eclipse/egerrit/internal/model/ChangeInfo.java b/org.eclipse.egerrit.model/src-gen/org/eclipse/egerrit/internal/model/ChangeInfo.java
index 0f15698..231da2d 100644
--- a/org.eclipse.egerrit.model/src-gen/org/eclipse/egerrit/internal/model/ChangeInfo.java
+++ b/org.eclipse.egerrit.model/src-gen/org/eclipse/egerrit/internal/model/ChangeInfo.java
@@ -11,6 +11,7 @@
  */
 package org.eclipse.egerrit.internal.model;
 
+import java.util.Map;
 import org.eclipse.emf.common.util.EList;
 import org.eclipse.emf.common.util.EMap;
 
@@ -1014,6 +1015,46 @@
 	int getLabelMaxValue(String label);
 
 	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @model
+	 * @generated
+	 */
+	int getPermittedMaxValue(String label);
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @model kind="operation"
+	 * @generated
+	 */
+	Map<String, EList<String>> getSortedPermittedLabels();
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @model kind="operation"
+	 * @generated
+	 */
+	Map<String, Integer> getAllowedLabelsMaxValue();
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @model
+	 * @generated
+	 */
+	Map<String, Integer> getUserLastLabelSet(String user);
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @model
+	 * @generated
+	 */
+	Map<String, Integer> getLabelsNotAtMax(String loginUser);
+
+	/**
 	 * Returns the value of the '<em><b>Revertable</b></em>' attribute.
 	 * The default value is <code>"false"</code>.
 	 * <!-- begin-user-doc -->
diff --git a/org.eclipse.egerrit.model/src-gen/org/eclipse/egerrit/internal/model/FetchInfo.java b/org.eclipse.egerrit.model/src-gen/org/eclipse/egerrit/internal/model/FetchInfo.java
index f6d5a01..f46c50a 100644
--- a/org.eclipse.egerrit.model/src-gen/org/eclipse/egerrit/internal/model/FetchInfo.java
+++ b/org.eclipse.egerrit.model/src-gen/org/eclipse/egerrit/internal/model/FetchInfo.java
@@ -98,7 +98,7 @@
 	 * <!-- end-user-doc -->
 	 * @return the value of the '<em>Commands</em>' map.
 	 * @see org.eclipse.egerrit.internal.model.ModelPackage#getFetchInfo_Commands()
-	 * @model mapType="org.eclipse.egerrit.internal.model.StringToString&lt;org.eclipse.emf.ecore.EString, org.eclipse.emf.ecore.EString&gt;"
+	 * @model mapType="org.eclipse.egerrit.internal.model.StringToString<org.eclipse.emf.ecore.EString, org.eclipse.emf.ecore.EString>"
 	 * @generated
 	 */
 	EMap<String, String> getCommands();
diff --git a/org.eclipse.egerrit.model/src-gen/org/eclipse/egerrit/internal/model/ModelPackage.java b/org.eclipse.egerrit.model/src-gen/org/eclipse/egerrit/internal/model/ModelPackage.java
index 9c692f6..0b2f5cf 100644
--- a/org.eclipse.egerrit.model/src-gen/org/eclipse/egerrit/internal/model/ModelPackage.java
+++ b/org.eclipse.egerrit.model/src-gen/org/eclipse/egerrit/internal/model/ModelPackage.java
@@ -2154,13 +2154,58 @@
 	int CHANGE_INFO___GET_LABEL_MAX_VALUE__STRING = 4;
 
 	/**
+	 * The operation id for the '<em>Get Permitted Max Value</em>' operation.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 * @ordered
+	 */
+	int CHANGE_INFO___GET_PERMITTED_MAX_VALUE__STRING = 5;
+
+	/**
+	 * The operation id for the '<em>Get Sorted Permitted Labels</em>' operation.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 * @ordered
+	 */
+	int CHANGE_INFO___GET_SORTED_PERMITTED_LABELS = 6;
+
+	/**
+	 * The operation id for the '<em>Get Allowed Labels Max Value</em>' operation.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 * @ordered
+	 */
+	int CHANGE_INFO___GET_ALLOWED_LABELS_MAX_VALUE = 7;
+
+	/**
+	 * The operation id for the '<em>Get User Last Label Set</em>' operation.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 * @ordered
+	 */
+	int CHANGE_INFO___GET_USER_LAST_LABEL_SET__STRING = 8;
+
+	/**
+	 * The operation id for the '<em>Get Labels Not At Max</em>' operation.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 * @ordered
+	 */
+	int CHANGE_INFO___GET_LABELS_NOT_AT_MAX__STRING = 9;
+
+	/**
 	 * The number of operations of the '<em>Change Info</em>' class.
 	 * <!-- begin-user-doc -->
 	 * <!-- end-user-doc -->
 	 * @generated
 	 * @ordered
 	 */
-	int CHANGE_INFO_OPERATION_COUNT = 5;
+	int CHANGE_INFO_OPERATION_COUNT = 10;
 
 	/**
 	 * The meta object id for the '{@link org.eclipse.egerrit.internal.model.impl.ProblemInfoImpl <em>Problem Info</em>}' class.
@@ -4897,6 +4942,56 @@
 	EOperation getChangeInfo__GetLabelMaxValue__String();
 
 	/**
+	 * Returns the meta object for the '{@link org.eclipse.egerrit.internal.model.ChangeInfo#getPermittedMaxValue(java.lang.String) <em>Get Permitted Max Value</em>}' operation.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @return the meta object for the '<em>Get Permitted Max Value</em>' operation.
+	 * @see org.eclipse.egerrit.internal.model.ChangeInfo#getPermittedMaxValue(java.lang.String)
+	 * @generated
+	 */
+	EOperation getChangeInfo__GetPermittedMaxValue__String();
+
+	/**
+	 * Returns the meta object for the '{@link org.eclipse.egerrit.internal.model.ChangeInfo#getSortedPermittedLabels() <em>Get Sorted Permitted Labels</em>}' operation.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @return the meta object for the '<em>Get Sorted Permitted Labels</em>' operation.
+	 * @see org.eclipse.egerrit.internal.model.ChangeInfo#getSortedPermittedLabels()
+	 * @generated
+	 */
+	EOperation getChangeInfo__GetSortedPermittedLabels();
+
+	/**
+	 * Returns the meta object for the '{@link org.eclipse.egerrit.internal.model.ChangeInfo#getAllowedLabelsMaxValue() <em>Get Allowed Labels Max Value</em>}' operation.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @return the meta object for the '<em>Get Allowed Labels Max Value</em>' operation.
+	 * @see org.eclipse.egerrit.internal.model.ChangeInfo#getAllowedLabelsMaxValue()
+	 * @generated
+	 */
+	EOperation getChangeInfo__GetAllowedLabelsMaxValue();
+
+	/**
+	 * Returns the meta object for the '{@link org.eclipse.egerrit.internal.model.ChangeInfo#getUserLastLabelSet(java.lang.String) <em>Get User Last Label Set</em>}' operation.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @return the meta object for the '<em>Get User Last Label Set</em>' operation.
+	 * @see org.eclipse.egerrit.internal.model.ChangeInfo#getUserLastLabelSet(java.lang.String)
+	 * @generated
+	 */
+	EOperation getChangeInfo__GetUserLastLabelSet__String();
+
+	/**
+	 * Returns the meta object for the '{@link org.eclipse.egerrit.internal.model.ChangeInfo#getLabelsNotAtMax(java.lang.String) <em>Get Labels Not At Max</em>}' operation.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @return the meta object for the '<em>Get Labels Not At Max</em>' operation.
+	 * @see org.eclipse.egerrit.internal.model.ChangeInfo#getLabelsNotAtMax(java.lang.String)
+	 * @generated
+	 */
+	EOperation getChangeInfo__GetLabelsNotAtMax__String();
+
+	/**
 	 * Returns the meta object for class '{@link org.eclipse.egerrit.internal.model.ProblemInfo <em>Problem Info</em>}'.
 	 * <!-- begin-user-doc -->
 	 * <!-- end-user-doc -->
@@ -7097,6 +7192,48 @@
 		EOperation CHANGE_INFO___GET_LABEL_MAX_VALUE__STRING = eINSTANCE.getChangeInfo__GetLabelMaxValue__String();
 
 		/**
+		 * The meta object literal for the '<em><b>Get Permitted Max Value</b></em>' operation.
+		 * <!-- begin-user-doc -->
+		 * <!-- end-user-doc -->
+		 * @generated
+		 */
+		EOperation CHANGE_INFO___GET_PERMITTED_MAX_VALUE__STRING = eINSTANCE
+				.getChangeInfo__GetPermittedMaxValue__String();
+
+		/**
+		 * The meta object literal for the '<em><b>Get Sorted Permitted Labels</b></em>' operation.
+		 * <!-- begin-user-doc -->
+		 * <!-- end-user-doc -->
+		 * @generated
+		 */
+		EOperation CHANGE_INFO___GET_SORTED_PERMITTED_LABELS = eINSTANCE.getChangeInfo__GetSortedPermittedLabels();
+
+		/**
+		 * The meta object literal for the '<em><b>Get Allowed Labels Max Value</b></em>' operation.
+		 * <!-- begin-user-doc -->
+		 * <!-- end-user-doc -->
+		 * @generated
+		 */
+		EOperation CHANGE_INFO___GET_ALLOWED_LABELS_MAX_VALUE = eINSTANCE.getChangeInfo__GetAllowedLabelsMaxValue();
+
+		/**
+		 * The meta object literal for the '<em><b>Get User Last Label Set</b></em>' operation.
+		 * <!-- begin-user-doc -->
+		 * <!-- end-user-doc -->
+		 * @generated
+		 */
+		EOperation CHANGE_INFO___GET_USER_LAST_LABEL_SET__STRING = eINSTANCE
+				.getChangeInfo__GetUserLastLabelSet__String();
+
+		/**
+		 * The meta object literal for the '<em><b>Get Labels Not At Max</b></em>' operation.
+		 * <!-- begin-user-doc -->
+		 * <!-- end-user-doc -->
+		 * @generated
+		 */
+		EOperation CHANGE_INFO___GET_LABELS_NOT_AT_MAX__STRING = eINSTANCE.getChangeInfo__GetLabelsNotAtMax__String();
+
+		/**
 		 * The meta object literal for the '{@link org.eclipse.egerrit.internal.model.impl.ProblemInfoImpl <em>Problem Info</em>}' class.
 		 * <!-- begin-user-doc -->
 		 * <!-- end-user-doc -->
diff --git a/org.eclipse.egerrit.model/src-gen/org/eclipse/egerrit/internal/model/RevisionInfo.java b/org.eclipse.egerrit.model/src-gen/org/eclipse/egerrit/internal/model/RevisionInfo.java
index 7a19fad..e4be9cc 100644
--- a/org.eclipse.egerrit.model/src-gen/org/eclipse/egerrit/internal/model/RevisionInfo.java
+++ b/org.eclipse.egerrit.model/src-gen/org/eclipse/egerrit/internal/model/RevisionInfo.java
@@ -164,7 +164,7 @@
 	 * <!-- end-user-doc -->
 	 * @return the value of the '<em>Fetch</em>' map.
 	 * @see org.eclipse.egerrit.internal.model.ModelPackage#getRevisionInfo_Fetch()
-	 * @model mapType="org.eclipse.egerrit.internal.model.StringToFetchInfo&lt;org.eclipse.emf.ecore.EString, org.eclipse.egerrit.internal.model.FetchInfo&gt;"
+	 * @model mapType="org.eclipse.egerrit.internal.model.StringToFetchInfo<org.eclipse.emf.ecore.EString, org.eclipse.egerrit.internal.model.FetchInfo>"
 	 * @generated
 	 */
 	EMap<String, FetchInfo> getFetch();
@@ -207,7 +207,7 @@
 	 * <!-- end-user-doc -->
 	 * @return the value of the '<em>Files</em>' map.
 	 * @see org.eclipse.egerrit.internal.model.ModelPackage#getRevisionInfo_Files()
-	 * @model mapType="org.eclipse.egerrit.internal.model.StringToFileInfo&lt;org.eclipse.emf.ecore.EString, org.eclipse.egerrit.internal.model.FileInfo&gt;"
+	 * @model mapType="org.eclipse.egerrit.internal.model.StringToFileInfo<org.eclipse.emf.ecore.EString, org.eclipse.egerrit.internal.model.FileInfo>"
 	 * @generated
 	 */
 	EMap<String, FileInfo> getFiles();
@@ -224,7 +224,7 @@
 	 * <!-- end-user-doc -->
 	 * @return the value of the '<em>Actions</em>' map.
 	 * @see org.eclipse.egerrit.internal.model.ModelPackage#getRevisionInfo_Actions()
-	 * @model mapType="org.eclipse.egerrit.internal.model.StringToActionInfo&lt;org.eclipse.emf.ecore.EString, org.eclipse.egerrit.internal.model.ActionInfo&gt;"
+	 * @model mapType="org.eclipse.egerrit.internal.model.StringToActionInfo<org.eclipse.emf.ecore.EString, org.eclipse.egerrit.internal.model.ActionInfo>"
 	 * @generated
 	 */
 	EMap<String, ActionInfo> getActions();
diff --git a/org.eclipse.egerrit.model/src-gen/org/eclipse/egerrit/internal/model/impl/ChangeInfoImpl.java b/org.eclipse.egerrit.model/src-gen/org/eclipse/egerrit/internal/model/impl/ChangeInfoImpl.java
index b21afa8..36a55af 100644
--- a/org.eclipse.egerrit.model/src-gen/org/eclipse/egerrit/internal/model/impl/ChangeInfoImpl.java
+++ b/org.eclipse.egerrit.model/src-gen/org/eclipse/egerrit/internal/model/impl/ChangeInfoImpl.java
@@ -13,6 +13,7 @@
 
 import java.lang.reflect.InvocationTargetException;
 import java.util.Collection;
+import java.util.Map;
 import org.eclipse.egerrit.internal.model.AccountInfo;
 import org.eclipse.egerrit.internal.model.ActionInfo;
 import org.eclipse.egerrit.internal.model.ApprovalInfo;
@@ -1766,6 +1767,66 @@
 	 * @generated
 	 */
 	@Override
+	public int getPermittedMaxValue(String label) {
+		// TODO: implement this method
+		// Ensure that you remove @generated or mark it @generated NOT
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	@Override
+	public Map<String, EList<String>> getSortedPermittedLabels() {
+		// TODO: implement this method
+		// Ensure that you remove @generated or mark it @generated NOT
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	@Override
+	public Map<String, Integer> getAllowedLabelsMaxValue() {
+		// TODO: implement this method
+		// Ensure that you remove @generated or mark it @generated NOT
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	@Override
+	public Map<String, Integer> getUserLastLabelSet(String user) {
+		// TODO: implement this method
+		// Ensure that you remove @generated or mark it @generated NOT
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	@Override
+	public Map<String, Integer> getLabelsNotAtMax(String loginUser) {
+		// TODO: implement this method
+		// Ensure that you remove @generated or mark it @generated NOT
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	@Override
 	public boolean isRevertable() {
 		// TODO: implement this method to return the 'Revertable' attribute
 		// Ensure that you remove @generated or mark it @generated NOT
@@ -2362,6 +2423,16 @@
 			return getLabelMinValue((String) arguments.get(0));
 		case ModelPackage.CHANGE_INFO___GET_LABEL_MAX_VALUE__STRING:
 			return getLabelMaxValue((String) arguments.get(0));
+		case ModelPackage.CHANGE_INFO___GET_PERMITTED_MAX_VALUE__STRING:
+			return getPermittedMaxValue((String) arguments.get(0));
+		case ModelPackage.CHANGE_INFO___GET_SORTED_PERMITTED_LABELS:
+			return getSortedPermittedLabels();
+		case ModelPackage.CHANGE_INFO___GET_ALLOWED_LABELS_MAX_VALUE:
+			return getAllowedLabelsMaxValue();
+		case ModelPackage.CHANGE_INFO___GET_USER_LAST_LABEL_SET__STRING:
+			return getUserLastLabelSet((String) arguments.get(0));
+		case ModelPackage.CHANGE_INFO___GET_LABELS_NOT_AT_MAX__STRING:
+			return getLabelsNotAtMax((String) arguments.get(0));
 		}
 		return super.eInvoke(operationID, arguments);
 	}
diff --git a/org.eclipse.egerrit.model/src-gen/org/eclipse/egerrit/internal/model/impl/ModelPackageImpl.java b/org.eclipse.egerrit.model/src-gen/org/eclipse/egerrit/internal/model/impl/ModelPackageImpl.java
index fcfb486..5d9bb1c 100644
--- a/org.eclipse.egerrit.model/src-gen/org/eclipse/egerrit/internal/model/impl/ModelPackageImpl.java
+++ b/org.eclipse.egerrit.model/src-gen/org/eclipse/egerrit/internal/model/impl/ModelPackageImpl.java
@@ -49,6 +49,7 @@
 import org.eclipse.emf.ecore.EAttribute;
 import org.eclipse.emf.ecore.EClass;
 import org.eclipse.emf.ecore.EEnum;
+import org.eclipse.emf.ecore.EGenericType;
 import org.eclipse.emf.ecore.EOperation;
 import org.eclipse.emf.ecore.EPackage;
 import org.eclipse.emf.ecore.EReference;
@@ -2157,6 +2158,56 @@
 	 * @generated
 	 */
 	@Override
+	public EOperation getChangeInfo__GetPermittedMaxValue__String() {
+		return changeInfoEClass.getEOperations().get(5);
+	}
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	@Override
+	public EOperation getChangeInfo__GetSortedPermittedLabels() {
+		return changeInfoEClass.getEOperations().get(6);
+	}
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	@Override
+	public EOperation getChangeInfo__GetAllowedLabelsMaxValue() {
+		return changeInfoEClass.getEOperations().get(7);
+	}
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	@Override
+	public EOperation getChangeInfo__GetUserLastLabelSet__String() {
+		return changeInfoEClass.getEOperations().get(8);
+	}
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	@Override
+	public EOperation getChangeInfo__GetLabelsNotAtMax__String() {
+		return changeInfoEClass.getEOperations().get(9);
+	}
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	@Override
 	public EClass getProblemInfo() {
 		return problemInfoEClass;
 	}
@@ -3041,6 +3092,11 @@
 		createEOperation(changeInfoEClass, CHANGE_INFO___GET_MOST_RELEVANT_VOTE__STRING);
 		createEOperation(changeInfoEClass, CHANGE_INFO___GET_LABEL_MIN_VALUE__STRING);
 		createEOperation(changeInfoEClass, CHANGE_INFO___GET_LABEL_MAX_VALUE__STRING);
+		createEOperation(changeInfoEClass, CHANGE_INFO___GET_PERMITTED_MAX_VALUE__STRING);
+		createEOperation(changeInfoEClass, CHANGE_INFO___GET_SORTED_PERMITTED_LABELS);
+		createEOperation(changeInfoEClass, CHANGE_INFO___GET_ALLOWED_LABELS_MAX_VALUE);
+		createEOperation(changeInfoEClass, CHANGE_INFO___GET_USER_LAST_LABEL_SET__STRING);
+		createEOperation(changeInfoEClass, CHANGE_INFO___GET_LABELS_NOT_AT_MAX__STRING);
 
 		problemInfoEClass = createEClass(PROBLEM_INFO);
 		createEAttribute(problemInfoEClass, PROBLEM_INFO__MESSAGE);
@@ -3618,6 +3674,50 @@
 				IS_UNIQUE, IS_ORDERED);
 		addEParameter(op, ecorePackage.getEString(), "label", 0, 1, IS_UNIQUE, IS_ORDERED); //$NON-NLS-1$
 
+		op = initEOperation(getChangeInfo__GetPermittedMaxValue__String(), ecorePackage.getEInt(),
+				"getPermittedMaxValue", 0, 1, IS_UNIQUE, IS_ORDERED); //$NON-NLS-1$
+		addEParameter(op, ecorePackage.getEString(), "label", 0, 1, IS_UNIQUE, IS_ORDERED); //$NON-NLS-1$
+
+		op = initEOperation(getChangeInfo__GetSortedPermittedLabels(), null, "getSortedPermittedLabels", 0, 1, //$NON-NLS-1$
+				IS_UNIQUE, IS_ORDERED);
+		EGenericType g1 = createEGenericType(ecorePackage.getEMap());
+		EGenericType g2 = createEGenericType(ecorePackage.getEString());
+		g1.getETypeArguments().add(g2);
+		g2 = createEGenericType(ecorePackage.getEEList());
+		g1.getETypeArguments().add(g2);
+		EGenericType g3 = createEGenericType(ecorePackage.getEString());
+		g2.getETypeArguments().add(g3);
+		initEOperation(op, g1);
+
+		op = initEOperation(getChangeInfo__GetAllowedLabelsMaxValue(), null, "getAllowedLabelsMaxValue", 0, 1, //$NON-NLS-1$
+				IS_UNIQUE, IS_ORDERED);
+		g1 = createEGenericType(ecorePackage.getEMap());
+		g2 = createEGenericType(ecorePackage.getEString());
+		g1.getETypeArguments().add(g2);
+		g2 = createEGenericType(ecorePackage.getEIntegerObject());
+		g1.getETypeArguments().add(g2);
+		initEOperation(op, g1);
+
+		op = initEOperation(getChangeInfo__GetUserLastLabelSet__String(), null, "getUserLastLabelSet", 0, 1, IS_UNIQUE, //$NON-NLS-1$
+				IS_ORDERED);
+		addEParameter(op, ecorePackage.getEString(), "user", 0, 1, IS_UNIQUE, IS_ORDERED); //$NON-NLS-1$
+		g1 = createEGenericType(ecorePackage.getEMap());
+		g2 = createEGenericType(ecorePackage.getEString());
+		g1.getETypeArguments().add(g2);
+		g2 = createEGenericType(ecorePackage.getEIntegerObject());
+		g1.getETypeArguments().add(g2);
+		initEOperation(op, g1);
+
+		op = initEOperation(getChangeInfo__GetLabelsNotAtMax__String(), null, "getLabelsNotAtMax", 0, 1, IS_UNIQUE, //$NON-NLS-1$
+				IS_ORDERED);
+		addEParameter(op, ecorePackage.getEString(), "loginUser", 0, 1, IS_UNIQUE, IS_ORDERED); //$NON-NLS-1$
+		g1 = createEGenericType(ecorePackage.getEMap());
+		g2 = createEGenericType(ecorePackage.getEString());
+		g1.getETypeArguments().add(g2);
+		g2 = createEGenericType(ecorePackage.getEIntegerObject());
+		g1.getETypeArguments().add(g2);
+		initEOperation(op, g1);
+
 		initEClass(problemInfoEClass, ProblemInfo.class, "ProblemInfo", !IS_ABSTRACT, !IS_INTERFACE, //$NON-NLS-1$
 				IS_GENERATED_INSTANCE_CLASS);
 		initEAttribute(getProblemInfo_Message(), ecorePackage.getEString(), "message", null, 0, 1, ProblemInfo.class, //$NON-NLS-1$
diff --git a/org.eclipse.egerrit.model/src/org/eclipse/egerrit/internal/model/ModifiedChangeInfoImpl.java b/org.eclipse.egerrit.model/src/org/eclipse/egerrit/internal/model/ModifiedChangeInfoImpl.java
index 173d845..8815d50 100644
--- a/org.eclipse.egerrit.model/src/org/eclipse/egerrit/internal/model/ModifiedChangeInfoImpl.java
+++ b/org.eclipse.egerrit.model/src/org/eclipse/egerrit/internal/model/ModifiedChangeInfoImpl.java
@@ -1,401 +1,508 @@
-package org.eclipse.egerrit.internal.model;
-/**
- *   Copyright (c) 2015 Ericsson AB
- *  
- *   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:
- *     Ericsson AB - Initial API and implementation
- */
-
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.eclipse.egerrit.internal.model.impl.ChangeInfoImpl;
-import org.eclipse.egerrit.internal.model.impl.StringToActionInfoImpl;
-import org.eclipse.emf.common.notify.Notification;
-import org.eclipse.emf.common.util.EList;
-import org.eclipse.emf.common.util.EMap;
-import org.eclipse.emf.ecore.EObject;
-import org.eclipse.emf.ecore.EStructuralFeature;
-import org.eclipse.emf.ecore.InternalEObject;
-import org.eclipse.emf.ecore.impl.ENotificationImpl;
-import org.eclipse.emf.ecore.util.EContentAdapter;
-
-public class ModifiedChangeInfoImpl extends ChangeInfoImpl {
-	public ModifiedChangeInfoImpl() {
-		eAdapters().add(new EContentAdapter() {
-			{
-				ModifiedChangeInfoImpl.this.eAdapters().add(this);
-			}
-
-			@Override
-			public void notifyChanged(Notification msg) {
-				super.notifyChanged(msg);
-
-				if (msg.getFeature() == null)
-					return;
-
-				if (msg.getFeature().equals(ModelPackage.Literals.CHANGE_INFO__CURRENT_REVISION)) {
-					InternalEObject modifiedChangeInfo = (InternalEObject) msg.getNotifier();
-					notifySet(modifiedChangeInfo, ModelPackage.Literals.CHANGE_INFO__REVISION);
-				}
-				if (msg.getFeature().equals(ModelPackage.Literals.CHANGE_INFO__REVISIONS)) {
-					deriveCommentPresence();
-				}
-
-				if (msg.getFeature().equals(ModelPackage.Literals.CHANGE_INFO__MESSAGES)) {
-					deriveCommentPresence();
-				}
-
-				// Notify that the value of latest patch set could have changed
-				if (msg.getFeature().equals(ModelPackage.Literals.FILE_INFO__DRAFT_COMMENTS)) {
-					InternalEObject modifiedFileInfo = (InternalEObject) msg.getNotifier();
-					if (eNotificationRequired()) {
-						modifiedFileInfo.eNotify(new ENotificationImpl(modifiedFileInfo, Notification.SET,
-								ModelPackage.Literals.FILE_INFO__DRAFTS_COUNT, null,
-								modifiedFileInfo.eGet(ModelPackage.Literals.FILE_INFO__DRAFTS_COUNT)));
-					}
-				}
-
-				if (msg.getFeature().equals(ModelPackage.Literals.FILE_INFO__COMMENTS)) {
-					InternalEObject modifiedFileInfo = (InternalEObject) msg.getNotifier();
-					if (eNotificationRequired()) {
-						modifiedFileInfo.eNotify(new ENotificationImpl(modifiedFileInfo, Notification.SET,
-								ModelPackage.Literals.FILE_INFO__COMMENTS_COUNT, null,
-								modifiedFileInfo.eGet(ModelPackage.Literals.FILE_INFO__COMMENTS_COUNT)));
-					}
-				}
-
-				if (msg.getFeature().equals(ModelPackage.Literals.REVISION_INFO__ACTIONS)) {
-					recomputeRevisionInfoActions(msg);
-				}
-
-				if (msg.getFeature().equals(ModelPackage.Literals.CHANGE_INFO__ACTIONS)) {
-					recomputeChangeInfoActions(msg);
-				}
-				if (msg.getFeature().equals(ModelPackage.Literals.CHANGE_INFO__REMOVABLE_REVIEWERS)) {
-					recomputeChangeInfoRemovalReviewer(msg);
-				}
-			}
-
-			private void recomputeChangeInfoRemovalReviewer(Notification msg) {
-				InternalEObject modifiedChangeInfo = (InternalEObject) msg.getNotifier();
-				if ((msg.getEventType() == Notification.ADD) || (msg.getEventType() == Notification.ADD_MANY)) {
-					deriveReviewerRemovalPresence();
-				}
-			}
-
-			private void recomputeChangeInfoActions(Notification msg) {
-				InternalEObject modifiedChangeInfo = (InternalEObject) msg.getNotifier();
-				if (msg.getEventType() == Notification.REMOVE_MANY || msg.getEventType() == Notification.ADD_MANY) {
-					notifySet(modifiedChangeInfo, ModelPackage.Literals.CHANGE_INFO__REVERTABLE);
-					notifySet(modifiedChangeInfo, ModelPackage.Literals.CHANGE_INFO__ABANDONABLE);
-					notifySet(modifiedChangeInfo, ModelPackage.Literals.CHANGE_INFO__RESTOREABLE);
-					notifySet(modifiedChangeInfo, ModelPackage.Literals.CHANGE_INFO__DELETEABLE);
-				}
-
-				if (msg.getEventType() == Notification.ADD) {
-					String actionName = ((StringToActionInfoImpl) msg.getNewValue()).getKey();
-					if (actionName.equals(ActionConstants.REVERT.getName())) {
-						notifySet(modifiedChangeInfo, ModelPackage.Literals.CHANGE_INFO__REVERTABLE);
-					}
-					if (actionName.equals(ActionConstants.ABANDON.getName())) {
-						notifySet(modifiedChangeInfo, ModelPackage.Literals.CHANGE_INFO__ABANDONABLE);
-					}
-					if (actionName.equals(ActionConstants.RESTORE.getName())) {
-						notifySet(modifiedChangeInfo, ModelPackage.Literals.CHANGE_INFO__RESTOREABLE);
-					}
-					if (actionName.equals("/")) {
-						notifySet(modifiedChangeInfo, ModelPackage.Literals.CHANGE_INFO__DELETEABLE);
-					}
-				}
-			}
-
-			private void recomputeRevisionInfoActions(Notification msg) {
-				InternalEObject modifiedRevision = (InternalEObject) msg.getNotifier();
-				if (msg.getEventType() == Notification.REMOVE_MANY || msg.getEventType() == Notification.ADD_MANY) {
-					notifySet(modifiedRevision, ModelPackage.Literals.REVISION_INFO__REBASEABLE);
-					notifySet(modifiedRevision, ModelPackage.Literals.REVISION_INFO__SUBMITABLE);
-					notifySet(modifiedRevision, ModelPackage.Literals.REVISION_INFO__CHERRYPICKABLE);
-					notifySet(modifiedRevision, ModelPackage.Literals.REVISION_INFO__DELETEABLE);
-					notifySet(modifiedRevision, ModelPackage.Literals.REVISION_INFO__PUBLISHABLE);
-				}
-				if (msg.getEventType() == Notification.ADD) {
-					String actionName = ((StringToActionInfoImpl) msg.getNewValue()).getKey();
-					if (actionName.equals(ActionConstants.REBASE.getName())) {
-						notifySet(modifiedRevision, ModelPackage.Literals.REVISION_INFO__REBASEABLE);
-					}
-					if (actionName.equals(ActionConstants.SUBMIT.getName())) {
-						notifySet(modifiedRevision, ModelPackage.Literals.REVISION_INFO__SUBMITABLE);
-					}
-					if (actionName.equals(ActionConstants.CHERRYPICK.getName())) {
-						notifySet(modifiedRevision, ModelPackage.Literals.REVISION_INFO__CHERRYPICKABLE);
-					}
-					if (actionName.equals("/")) {
-						notifySet(modifiedRevision, ModelPackage.Literals.REVISION_INFO__DELETEABLE);
-					}
-					if (actionName.equals(ActionConstants.PUBLISH.getName())) {
-						notifySet(modifiedRevision, ModelPackage.Literals.REVISION_INFO__PUBLISHABLE);
-					}
-				}
-			}
-
-			private InternalEObject getModifiedChangeInfo(Object object) {
-				if (object instanceof ChangeInfo) {
-					return (InternalEObject) object;
-				}
-				return getModifiedChangeInfo(((EObject) object).eContainer());
-			}
-
-		});
-	}
-
-	private void notifySet(InternalEObject modifiedObject, EStructuralFeature attr) {
-		if (eNotificationRequired()) {
-			modifiedObject.eNotify(
-					new ENotificationImpl(modifiedObject, Notification.SET, attr, null, modifiedObject.eGet(attr)));
-		}
-	}
-
-	@Override
-	public boolean equals(Object obj) {
-		if (this == obj) {
-			return true;
-		}
-		if (obj == null) {
-			return false;
-		}
-		if (getClass() != obj.getClass()) {
-			return false;
-		}
-
-		ChangeInfo other = (ChangeInfo) obj;
-		if (getId() == null) {
-			if (other.getId() != null) {
-				return false;
-			}
-		} else if (!getId().equals(other.getId())) {
-			return false;
-		}
-		return true;
-	}
-
-	@Override
-	public int hashCode() {
-		return getId().hashCode();
-	}
-
-	@Override
-	public RevisionInfo basicGetRevision() {
-		return getRevisions().get(getCurrent_revision());
-	}
-
-	@Override
-	public RevisionInfo basicGetLatestPatchSet() {
-		Collection<RevisionInfo> revisions = getRevisions().values();
-		RevisionInfo match = null;
-		int topNumber = 0;
-		for (RevisionInfo candidate : revisions) {
-			if (candidate.get_number() > topNumber) {
-				topNumber = candidate.get_number();
-				match = candidate;
-			}
-		}
-		return match;
-	}
-
-	@Override
-	public RevisionInfo getRevisionByNumber(int revisionNumber) {
-		Collection<RevisionInfo> revisions = getRevisions().values();
-		for (RevisionInfo candidate : revisions) {
-			if (candidate.get_number() == revisionNumber) {
-				return candidate;
-			}
-		}
-		return null;
-	}
-
-	@Override
-	public boolean isActionAllowed(String action) {
-		EMap<String, ActionInfo> actionsAvailable = getActions();
-
-		if (actionsAvailable != null) {
-			ActionInfo actionInfo = actionsAvailable.get(action);
-
-			if (actionInfo != null) {
-				return actionInfo.isEnabled();
-			}
-		}
-		return false;
-	}
-
-	public boolean isRevertable() {
-		return isActionAllowed(ActionConstants.REVERT.getName());
-	}
-
-	@Override
-	public boolean isAbandonable() {
-		return isActionAllowed(ActionConstants.ABANDON.getName());
-	}
-
-	@Override
-	public boolean isRestoreable() {
-		return isActionAllowed(ActionConstants.RESTORE.getName());
-	}
-
-	@Override
-	public boolean isDeleteable() {
-		// We can't use a constant here because EMF does not allow to have a
-		// constant for '/'
-		return isActionAllowed("/");
-	}
-
-	@Override
-	public void setUserSelectedRevision(RevisionInfo newUserSelectedRevision) {
-		if (newUserSelectedRevision == null)
-			return;
-		synchronized (this) {
-
-			if (userSelectedRevision == null) {
-				super.setUserSelectedRevision(newUserSelectedRevision);
-				return;
-			}
-			if (userSelectedRevision.get_number() != newUserSelectedRevision.get_number()) {
-				super.setUserSelectedRevision(newUserSelectedRevision);
-			}
-		}
-	}
-
-	@Override
-	public void setUpdated(String newUpdated) {
-		if (updated == null) {
-			super.setUpdated(newUpdated);
-			return;
-		}
-		if (!updated.equals(newUpdated)) {
-			super.setUpdated(newUpdated);
-		}
-	}
-
-	private static ApprovalInfo NO_VOTE = ModelFactory.eINSTANCE.createApprovalInfo();
-
-	@Override
-	public ApprovalInfo getMostRelevantVote(String label) {
-		if (labels == null) {
-			return NO_VOTE;
-		}
-		LabelInfo labelInfo = labels.get(label);
-		if (labelInfo == null) {
-			return NO_VOTE;
-		}
-		//As per the gerrit doc, the votes are always considered in this order of priority.
-		//https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#get-change-detail
-		//The combined label vote is calculated in the following order (from highest to lowest): REJECTED > APPROVED > DISLIKED > RECOMMENDED.
-		if (labelInfo.getRejected() != null) {
-			return fromVoteToValue(labelInfo, labelInfo.getRejected());
-		}
-		if (labelInfo.getApproved() != null) {
-			return fromVoteToValue(labelInfo, labelInfo.getApproved());
-		}
-		if (labelInfo.getDisliked() != null) {
-			return fromVoteToValue(labelInfo, labelInfo.getDisliked());
-		}
-		if (labelInfo.getRecommended() != null) {
-			return fromVoteToValue(labelInfo, labelInfo.getRecommended());
-		}
-		return NO_VOTE;
-	}
-
-	private ApprovalInfo fromVoteToValue(LabelInfo match, AccountInfo voter) {
-		for (ApprovalInfo candidate : match.getAll()) {
-			if (candidate.get_account_id() == voter.get_account_id())
-				return candidate;
-		}
-		return NO_VOTE;
-	}
-
-	//Helper method setting if a comment is contained in a message
-	//Also update the comment flag on a revision
-	private void deriveCommentPresence() {
-		EList<ChangeMessageInfo> messages = getMessages();
-		for (ChangeMessageInfo m : messages) {
-			m.setComment(hasComments(m.getMessage()));
-			if (m.isComment()) {
-				RevisionInfo rev = ((ChangeInfo) m.eContainer()).getRevisionByNumber(m.get_revision_number());
-				if (rev != null)
-					rev.setCommented(m.isComment());
-			}
-		}
-	}
-
-	//Helper method setting if a reviewer is removeable or not
-	//Also update the isDeleatable in Revisioninfo
-	private void deriveReviewerRemovalPresence() {
-		EList<AccountInfo> removalList = getRemovable_reviewers();
-		EList<ReviewerInfo> reviewersInfo = getComputedReviewers();
-		for (int i = 0; i < removalList.size(); i++) {
-			for (ReviewerInfo revInfo : reviewersInfo) {
-				if (revInfo.get_account_id() == removalList.get(i).get_account_id()) {
-					revInfo.setDeleteable(true);
-				}
-			}
-		}
-	}
-
-	private static final Pattern COMMENT_PATTERN = Pattern.compile("(\\d+ comme[nt])(\\w+)"); // $NON-NLS-0$
-
-	private boolean hasComments(String msg) {
-		if (msg == null) {
-			return false;
-		}
-		Matcher matcher = COMMENT_PATTERN.matcher(msg.toLowerCase());
-		return matcher.find(0);
-	}
-
-	@Override
-	public int getLabelMinValue(String label) {
-		int min = 0;
-		EMap<String, LabelInfo> allLabels = getLabels();
-		if (allLabels != null) {
-			LabelInfo info = getLabels().get(label);
-			if (info == null)
-				return min;
-			Set<String> possibleValues = info.getValues().keySet();
-			for (String value : possibleValues) {
-				min = Math.min(min, Integer.parseInt(value.trim()));
-			}
-		}
-		return min;
-	}
-
-	@Override
-	public int getLabelMaxValue(String label) {
-		int max = 0;
-		EMap<String, LabelInfo> mapLabels = getLabels();
-		if (mapLabels != null) {
-			LabelInfo info = mapLabels.get(label);
-			if (info == null)
-				return max;
-			Set<String> possibleValues = info.getValues().keySet();
-			for (String value : possibleValues) {
-				max = Math.max(max, Integer.parseInt(value.trim()));
-			}
-		}
-		return max;
-	}
-
-	@Override
-	public RevisionInfo getUserSelectedRevision() {
-		synchronized (this) {
-			return super.getUserSelectedRevision();
-		}
-	}
-}
+package org.eclipse.egerrit.internal.model;

+/**

+ *   Copyright (c) 2015 Ericsson AB

+ *  

+ *   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:

+ *     Ericsson AB - Initial API and implementation

+ */

+

+import java.util.Collection;

+import java.util.Iterator;

+import java.util.LinkedHashMap;

+import java.util.List;

+import java.util.Map;

+import java.util.TreeMap;

+import java.util.Map.Entry;

+import java.util.Set;

+import java.util.regex.Matcher;

+import java.util.regex.Pattern;

+

+import org.eclipse.egerrit.internal.model.impl.ChangeInfoImpl;

+import org.eclipse.egerrit.internal.model.impl.StringToActionInfoImpl;

+import org.eclipse.emf.common.notify.Notification;

+import org.eclipse.emf.common.util.EList;

+import org.eclipse.emf.common.util.EMap;

+import org.eclipse.emf.ecore.EObject;

+import org.eclipse.emf.ecore.EStructuralFeature;

+import org.eclipse.emf.ecore.InternalEObject;

+import org.eclipse.emf.ecore.impl.ENotificationImpl;

+import org.eclipse.emf.ecore.util.EContentAdapter;

+

+public class ModifiedChangeInfoImpl extends ChangeInfoImpl {

+	public ModifiedChangeInfoImpl() {

+		eAdapters().add(new EContentAdapter() {

+			{

+				ModifiedChangeInfoImpl.this.eAdapters().add(this);

+			}

+

+			@Override

+			public void notifyChanged(Notification msg) {

+				super.notifyChanged(msg);

+

+				if (msg.getFeature() == null)

+					return;

+

+				if (msg.getFeature().equals(ModelPackage.Literals.CHANGE_INFO__CURRENT_REVISION)) {

+					InternalEObject modifiedChangeInfo = (InternalEObject) msg.getNotifier();

+					notifySet(modifiedChangeInfo, ModelPackage.Literals.CHANGE_INFO__REVISION);

+				}

+				if (msg.getFeature().equals(ModelPackage.Literals.CHANGE_INFO__REVISIONS)) {

+					deriveCommentPresence();

+				}

+

+				if (msg.getFeature().equals(ModelPackage.Literals.CHANGE_INFO__MESSAGES)) {

+					deriveCommentPresence();

+				}

+

+				// Notify that the value of latest patch set could have changed

+				if (msg.getFeature().equals(ModelPackage.Literals.FILE_INFO__DRAFT_COMMENTS)) {

+					InternalEObject modifiedFileInfo = (InternalEObject) msg.getNotifier();

+					if (eNotificationRequired()) {

+						modifiedFileInfo.eNotify(new ENotificationImpl(modifiedFileInfo, Notification.SET,

+								ModelPackage.Literals.FILE_INFO__DRAFTS_COUNT, null,

+								modifiedFileInfo.eGet(ModelPackage.Literals.FILE_INFO__DRAFTS_COUNT)));

+					}

+				}

+

+				if (msg.getFeature().equals(ModelPackage.Literals.FILE_INFO__COMMENTS)) {

+					InternalEObject modifiedFileInfo = (InternalEObject) msg.getNotifier();

+					if (eNotificationRequired()) {

+						modifiedFileInfo.eNotify(new ENotificationImpl(modifiedFileInfo, Notification.SET,

+								ModelPackage.Literals.FILE_INFO__COMMENTS_COUNT, null,

+								modifiedFileInfo.eGet(ModelPackage.Literals.FILE_INFO__COMMENTS_COUNT)));

+					}

+				}

+

+				if (msg.getFeature().equals(ModelPackage.Literals.REVISION_INFO__ACTIONS)) {

+					recomputeRevisionInfoActions(msg);

+				}

+

+				if (msg.getFeature().equals(ModelPackage.Literals.CHANGE_INFO__ACTIONS)) {

+					recomputeChangeInfoActions(msg);

+				}

+				if (msg.getFeature().equals(ModelPackage.Literals.CHANGE_INFO__REMOVABLE_REVIEWERS)) {

+					recomputeChangeInfoRemovalReviewer(msg);

+				}

+			}

+

+			private void recomputeChangeInfoRemovalReviewer(Notification msg) {

+				InternalEObject modifiedChangeInfo = (InternalEObject) msg.getNotifier();

+				if ((msg.getEventType() == Notification.ADD) || (msg.getEventType() == Notification.ADD_MANY)) {

+					deriveReviewerRemovalPresence();

+				}

+			}

+

+			private void recomputeChangeInfoActions(Notification msg) {

+				InternalEObject modifiedChangeInfo = (InternalEObject) msg.getNotifier();

+				if (msg.getEventType() == Notification.REMOVE_MANY || msg.getEventType() == Notification.ADD_MANY) {

+					notifySet(modifiedChangeInfo, ModelPackage.Literals.CHANGE_INFO__REVERTABLE);

+					notifySet(modifiedChangeInfo, ModelPackage.Literals.CHANGE_INFO__ABANDONABLE);

+					notifySet(modifiedChangeInfo, ModelPackage.Literals.CHANGE_INFO__RESTOREABLE);

+					notifySet(modifiedChangeInfo, ModelPackage.Literals.CHANGE_INFO__DELETEABLE);

+				}

+

+				if (msg.getEventType() == Notification.ADD) {

+					String actionName = ((StringToActionInfoImpl) msg.getNewValue()).getKey();

+					if (actionName.equals(ActionConstants.REVERT.getName())) {

+						notifySet(modifiedChangeInfo, ModelPackage.Literals.CHANGE_INFO__REVERTABLE);

+					}

+					if (actionName.equals(ActionConstants.ABANDON.getName())) {

+						notifySet(modifiedChangeInfo, ModelPackage.Literals.CHANGE_INFO__ABANDONABLE);

+					}

+					if (actionName.equals(ActionConstants.RESTORE.getName())) {

+						notifySet(modifiedChangeInfo, ModelPackage.Literals.CHANGE_INFO__RESTOREABLE);

+					}

+					if (actionName.equals("/")) {

+						notifySet(modifiedChangeInfo, ModelPackage.Literals.CHANGE_INFO__DELETEABLE);

+					}

+				}

+			}

+

+			private void recomputeRevisionInfoActions(Notification msg) {

+				InternalEObject modifiedRevision = (InternalEObject) msg.getNotifier();

+				if (msg.getEventType() == Notification.REMOVE_MANY || msg.getEventType() == Notification.ADD_MANY) {

+					notifySet(modifiedRevision, ModelPackage.Literals.REVISION_INFO__REBASEABLE);

+					notifySet(modifiedRevision, ModelPackage.Literals.REVISION_INFO__SUBMITABLE);

+					notifySet(modifiedRevision, ModelPackage.Literals.REVISION_INFO__CHERRYPICKABLE);

+					notifySet(modifiedRevision, ModelPackage.Literals.REVISION_INFO__DELETEABLE);

+					notifySet(modifiedRevision, ModelPackage.Literals.REVISION_INFO__PUBLISHABLE);

+				}

+				if (msg.getEventType() == Notification.ADD) {

+					String actionName = ((StringToActionInfoImpl) msg.getNewValue()).getKey();

+					if (actionName.equals(ActionConstants.REBASE.getName())) {

+						notifySet(modifiedRevision, ModelPackage.Literals.REVISION_INFO__REBASEABLE);

+					}

+					if (actionName.equals(ActionConstants.SUBMIT.getName())) {

+						notifySet(modifiedRevision, ModelPackage.Literals.REVISION_INFO__SUBMITABLE);

+					}

+					if (actionName.equals(ActionConstants.CHERRYPICK.getName())) {

+						notifySet(modifiedRevision, ModelPackage.Literals.REVISION_INFO__CHERRYPICKABLE);

+					}

+					if (actionName.equals("/")) {

+						notifySet(modifiedRevision, ModelPackage.Literals.REVISION_INFO__DELETEABLE);

+					}

+					if (actionName.equals(ActionConstants.PUBLISH.getName())) {

+						notifySet(modifiedRevision, ModelPackage.Literals.REVISION_INFO__PUBLISHABLE);

+					}

+				}

+			}

+

+			private InternalEObject getModifiedChangeInfo(Object object) {

+				if (object instanceof ChangeInfo) {

+					return (InternalEObject) object;

+				}

+				return getModifiedChangeInfo(((EObject) object).eContainer());

+			}

+

+		});

+	}

+

+	private void notifySet(InternalEObject modifiedObject, EStructuralFeature attr) {

+		if (eNotificationRequired()) {

+			modifiedObject.eNotify(

+					new ENotificationImpl(modifiedObject, Notification.SET, attr, null, modifiedObject.eGet(attr)));

+		}

+	}

+

+	@Override

+	public boolean equals(Object obj) {

+		if (this == obj) {

+			return true;

+		}

+		if (obj == null) {

+			return false;

+		}

+		if (getClass() != obj.getClass()) {

+			return false;

+		}

+

+		ChangeInfo other = (ChangeInfo) obj;

+		if (getId() == null) {

+			if (other.getId() != null) {

+				return false;

+			}

+		} else if (!getId().equals(other.getId())) {

+			return false;

+		}

+		return true;

+	}

+

+	@Override

+	public int hashCode() {

+		return getId().hashCode();

+	}

+

+	@Override

+	public RevisionInfo basicGetRevision() {

+		return getRevisions().get(getCurrent_revision());

+	}

+

+	@Override

+	public RevisionInfo basicGetLatestPatchSet() {

+		Collection<RevisionInfo> revisions = getRevisions().values();

+		RevisionInfo match = null;

+		int topNumber = 0;

+		for (RevisionInfo candidate : revisions) {

+			if (candidate.get_number() > topNumber) {

+				topNumber = candidate.get_number();

+				match = candidate;

+			}

+		}

+		return match;

+	}

+

+	@Override

+	public RevisionInfo getRevisionByNumber(int revisionNumber) {

+		Collection<RevisionInfo> revisions = getRevisions().values();

+		for (RevisionInfo candidate : revisions) {

+			if (candidate.get_number() == revisionNumber) {

+				return candidate;

+			}

+		}

+		return null;

+	}

+

+	@Override

+	public boolean isActionAllowed(String action) {

+		EMap<String, ActionInfo> actionsAvailable = getActions();

+

+		if (actionsAvailable != null) {

+			ActionInfo actionInfo = actionsAvailable.get(action);

+

+			if (actionInfo != null) {

+				return actionInfo.isEnabled();

+			}

+		}

+		return false;

+	}

+

+	public boolean isRevertable() {

+		return isActionAllowed(ActionConstants.REVERT.getName());

+	}

+

+	@Override

+	public boolean isAbandonable() {

+		return isActionAllowed(ActionConstants.ABANDON.getName());

+	}

+

+	@Override

+	public boolean isRestoreable() {

+		return isActionAllowed(ActionConstants.RESTORE.getName());

+	}

+

+	@Override

+	public boolean isDeleteable() {

+		// We can't use a constant here because EMF does not allow to have a

+		// constant for '/'

+		return isActionAllowed("/");

+	}

+

+	@Override

+	public void setUserSelectedRevision(RevisionInfo newUserSelectedRevision) {

+		if (newUserSelectedRevision == null)

+			return;

+		synchronized (this) {

+

+			if (userSelectedRevision == null) {

+				super.setUserSelectedRevision(newUserSelectedRevision);

+				return;

+			}

+			if (userSelectedRevision.get_number() != newUserSelectedRevision.get_number()) {

+				super.setUserSelectedRevision(newUserSelectedRevision);

+			}

+		}

+	}

+

+	@Override

+	public void setUpdated(String newUpdated) {

+		if (updated == null) {

+			super.setUpdated(newUpdated);

+			return;

+		}

+		if (!updated.equals(newUpdated)) {

+			super.setUpdated(newUpdated);

+		}

+	}

+

+	private static ApprovalInfo NO_VOTE = ModelFactory.eINSTANCE.createApprovalInfo();

+

+	@Override

+	public ApprovalInfo getMostRelevantVote(String label) {

+		if (labels == null) {

+			return NO_VOTE;

+		}

+		LabelInfo labelInfo = labels.get(label);

+		if (labelInfo == null) {

+			return NO_VOTE;

+		}

+		//As per the gerrit doc, the votes are always considered in this order of priority.

+		//https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#get-change-detail

+		//The combined label vote is calculated in the following order (from highest to lowest): REJECTED > APPROVED > DISLIKED > RECOMMENDED.

+		if (labelInfo.getRejected() != null) {

+			return fromVoteToValue(labelInfo, labelInfo.getRejected());

+		}

+		if (labelInfo.getApproved() != null) {

+			return fromVoteToValue(labelInfo, labelInfo.getApproved());

+		}

+		if (labelInfo.getDisliked() != null) {

+			return fromVoteToValue(labelInfo, labelInfo.getDisliked());

+		}

+		if (labelInfo.getRecommended() != null) {

+			return fromVoteToValue(labelInfo, labelInfo.getRecommended());

+		}

+		return NO_VOTE;

+	}

+

+	private ApprovalInfo fromVoteToValue(LabelInfo match, AccountInfo voter) {

+		for (ApprovalInfo candidate : match.getAll()) {

+			if (candidate.get_account_id() == voter.get_account_id())

+				return candidate;

+		}

+		return NO_VOTE;

+	}

+

+	//Helper method setting if a comment is contained in a message

+	//Also update the comment flag on a revision

+	private void deriveCommentPresence() {

+		EList<ChangeMessageInfo> messages = getMessages();

+		for (ChangeMessageInfo m : messages) {

+			m.setComment(hasComments(m.getMessage()));

+			if (m.isComment()) {

+				RevisionInfo rev = ((ChangeInfo) m.eContainer()).getRevisionByNumber(m.get_revision_number());

+				if (rev != null)

+					rev.setCommented(m.isComment());

+			}

+		}

+	}

+

+	//Helper method setting if a reviewer is removeable or not

+	//Also update the isDeleatable in Revisioninfo

+	private void deriveReviewerRemovalPresence() {

+		EList<AccountInfo> removalList = getRemovable_reviewers();

+		EList<ReviewerInfo> reviewersInfo = getComputedReviewers();

+		for (int i = 0; i < removalList.size(); i++) {

+			for (ReviewerInfo revInfo : reviewersInfo) {

+				if (revInfo.get_account_id() == removalList.get(i).get_account_id()) {

+					revInfo.setDeleteable(true);

+				}

+			}

+		}

+	}

+

+	private static final Pattern COMMENT_PATTERN = Pattern.compile("(\\d+ comme[nt])(\\w+)"); // $NON-NLS-0$

+

+	private boolean hasComments(String msg) {

+		if (msg == null) {

+			return false;

+		}

+		Matcher matcher = COMMENT_PATTERN.matcher(msg.toLowerCase());

+		return matcher.find(0);

+	}

+

+	@Override

+	public int getLabelMinValue(String label) {

+		int min = 0;

+		EMap<String, LabelInfo> allLabels = getLabels();

+		if (allLabels != null) {

+			LabelInfo info = getLabels().get(label);

+			if (info == null)

+				return min;

+			Set<String> possibleValues = info.getValues().keySet();

+			for (String value : possibleValues) {

+				min = Math.min(min, Integer.parseInt(value.trim()));

+			}

+		}

+		return min;

+	}

+

+	@Override

+	public int getLabelMaxValue(String label) {

+		int max = 0;

+		EMap<String, LabelInfo> mapLabels = getLabels();

+		if (mapLabels != null) {

+			LabelInfo info = mapLabels.get(label);

+			if (info == null)

+				return max;

+			Set<String> possibleValues = info.getValues().keySet();

+			for (String value : possibleValues) {

+				max = Math.max(max, Integer.parseInt(value.trim()));

+			}

+		}

+		return max;

+	}

+

+	@Override

+	public RevisionInfo getUserSelectedRevision() {

+		synchronized (this) {

+			return super.getUserSelectedRevision();

+		}

+	}

+

+	@Override

+	public int getPermittedMaxValue(String label) {

+		int maxPermitted = Integer.MIN_VALUE;

+		EMap<String, EList<String>> listLabels = getPermitted_labels();

+		if (!listLabels.isEmpty()) {

+			EList<String> listPermitted = listLabels.get(label);

+			if (listPermitted != null && !listPermitted.isEmpty()) {

+				Iterator<String> iterator = listPermitted.iterator();

+				//Get the structure having all the possible options

+				while (iterator.hasNext()) {

+					String value = iterator.next();

+					maxPermitted = Math.max(maxPermitted, new Integer(value.trim()));

+				}

+			}

+		}

+		return maxPermitted;

+	}

+

+	@Override

+	public Map<String, EList<String>> getSortedPermittedLabels() {

+		TreeMap<String, EList<String>> sortedPermitted = new TreeMap<>();

+		Iterator<Entry<String, EList<String>>> iterator = getPermitted_labels().iterator();

+		while (iterator.hasNext()) {

+			Entry<String, EList<String>> permittedlabel = iterator.next();

+			sortedPermitted.put(permittedlabel.getKey(), permittedlabel.getValue());

+		}

+		return sortedPermitted;

+	}

+

+	@Override

+	public Map<String, Integer> getAllowedLabelsMaxValue() {

+		//Maintain the list of potential label and their max value for the current changeInfo

+		TreeMap<String, Integer> mapLabels = new TreeMap<>();

+		Iterator<Map.Entry<String, EList<String>>> iterator = getPermitted_labels().entrySet().iterator();

+		while (iterator.hasNext()) {

+			Entry<String, EList<String>> permittedlabel = iterator.next();

+			int maxPermitted = getPermittedMaxValue(permittedlabel.getKey());

+			if (maxPermitted > 0) {

+				mapLabels.put(permittedlabel.getKey(), maxPermitted);

+			}

+		}

+		return mapLabels;

+	}

+

+	@Override

+	public Map<String, Integer> getUserLastLabelSet(String user) {

+		//Collect all votes from a user for every potential labels defined for this project

+		LinkedHashMap<String, Integer> userVotes = new LinkedHashMap<String, Integer>();

+		EMap<String, LabelInfo> labelsInfo = getLabels();

+

+		if (labelsInfo != null && !labelsInfo.isEmpty()) {

+			Iterator<Map.Entry<String, LabelInfo>> labelIter = labelsInfo.entrySet().iterator();

+			//Iterate through all defined labels and keep only the one for the specified user

+			while (labelIter.hasNext()) {

+				Entry<String, LabelInfo> entrylabel = labelIter.next();

+				LabelInfo labelInfo = entrylabel.getValue();

+				List<ApprovalInfo> listApproval = labelInfo.getAll();

+				if (listApproval != null && !listApproval.isEmpty()) {

+					ApprovalInfo candidate = null;

+					//Find the most recent vote for the current user

+					for (ApprovalInfo oneApproval : listApproval) {

+						//Test either the e-mail or the username

+						if (user.equals(oneApproval.getEmail()) || user.equals(oneApproval.getUsername())) {

+							if (candidate == null) {

+								candidate = oneApproval;

+								break;

+							}

+						}

+					}

+					if (candidate != null) {

+						//Collect the label and the associated last value for the current user

+						userVotes.put(entrylabel.getKey(), candidate.getValue());

+					}

+				}

+			}

+		}

+		return userVotes;

+	}

+

+	/**

+	 * Find all potential labels not set to its maximum value for a login user

+	 */

+	@Override

+	public Map<String, Integer> getLabelsNotAtMax(String loginUser) {

+		Map<String, Integer> modifiedAllowed = new TreeMap<>();

+

+		Iterator<Entry<String, Integer>> iter = getAllowedLabelsMaxValue().entrySet().iterator();

+		Map<String, Integer> userVotes = getUserLastLabelSet(loginUser);

+		while (iter.hasNext()) {

+			Entry<String, Integer> entry = iter.next();

+			int currentValue = -1;

+			if (!userVotes.isEmpty()) {

+				currentValue = userVotes.get(entry.getKey());

+			}

+			int permittedMax = getPermittedMaxValue(entry.getKey());

+			if (!(currentValue == permittedMax)) {

+				//Only copy the one not at the maximum yet

+				modifiedAllowed.put(entry.getKey(), entry.getValue());

+			}

+		}

+		return modifiedAllowed;

+	}

+}

diff --git a/org.eclipse.egerrit.ui.rcptt.tests/InitialSubmit.ctx b/org.eclipse.egerrit.ui.rcptt.tests/InitialSubmit.ctx
index 4ace162..eacff1a 100644
--- a/org.eclipse.egerrit.ui.rcptt.tests/InitialSubmit.ctx
+++ b/org.eclipse.egerrit.ui.rcptt.tests/InitialSubmit.ctx
@@ -6,7 +6,7 @@
 Element-Version: 2.0
 Id: _faIt4LvJEeawKoti9_0qpQ
 Runtime-Version: 2.1.0.201605312320
-Save-Time: 12/8/16 10:16 AM
+Save-Time: 3/17/17 1:06 PM
 
 ------=_.description-216f885c-d591-38ce-8ea2-e4f8cb4d6ffa
 Content-Type: text/plain
@@ -25,7 +25,8 @@
 
 /* +2 the review and submits it to the server */
 get-editor | get-button "Reply..." | click
-get-menu "Code-Review+2" | click
+	get-menu "Code-Review  +2" | click 
+
 wait -ms $defaultWaitTime
 
 get-editor | get-button Submit | click
diff --git a/org.eclipse.egerrit.ui.rcptt.tests/dashboard/ContextMenu.test b/org.eclipse.egerrit.ui.rcptt.tests/dashboard/ContextMenu.test
index 750072d..6c40017 100644
--- a/org.eclipse.egerrit.ui.rcptt.tests/dashboard/ContextMenu.test
+++ b/org.eclipse.egerrit.ui.rcptt.tests/dashboard/ContextMenu.test
@@ -6,7 +6,7 @@
 External-Reference: 
 Id: _MvE9AHkaEea0t7_C5oFwIQ
 Runtime-Version: 2.1.0.201605312320
-Save-Time: 9/12/16 3:56 PM
+Save-Time: 3/17/17 1:02 PM
 Testcase-Type: ecl
 
 ------=_.description-216f885c-d591-38ce-8ea2-e4f8cb4d6ffa
@@ -38,7 +38,7 @@
 assert-context-menu -contains "Download/Cherry Pick..."
 assert-context-menu -contains "Reply"
 assert-context-menu -contains "Reply/Reply..."
-assert-context-menu -contains "Reply/Code-Review +2"
+assert-context-menu -contains "Reply/Code-Review  +2"
 assert-context-menu -contains "Open Compare"
 assert-context-menu -contains "Submit"
 
diff --git a/org.eclipse.egerrit.ui.rcptt.tests/reviewEditor/ReplyToReview.test b/org.eclipse.egerrit.ui.rcptt.tests/reviewEditor/ReplyToReview.test
index 3414ac6..1dd253b 100644
--- a/org.eclipse.egerrit.ui.rcptt.tests/reviewEditor/ReplyToReview.test
+++ b/org.eclipse.egerrit.ui.rcptt.tests/reviewEditor/ReplyToReview.test
@@ -6,7 +6,7 @@
 External-Reference: 
 Id: _8b_jwF20Eea0aLCZHBxUOw
 Runtime-Version: 2.1.0.201605312320
-Save-Time: 12/8/16 10:16 AM
+Save-Time: 3/17/17 2:07 PM
 Testcase-Type: ecl
 
 ------=_.content-0a7243a0-75d3-3d5f-9791-539de0e5b7ac
@@ -29,7 +29,7 @@
 
 // testing reply +2
 get-editor | get-button "Reply..." | click
-get-menu "Code-Review+2" | click
+get-menu "Code-Review  +2" | click
 wait -ms $defaultWaitTime
 
 assert-that-first-entry-in-history-table -atColumn 3 -is "Code-Review+2" -in [get-editor]
diff --git a/org.eclipse.egerrit.ui.rcptt.tests/reviewEditor/SubmitRevertReview.test b/org.eclipse.egerrit.ui.rcptt.tests/reviewEditor/SubmitRevertReview.test
index 5f5d8f7..8058458 100644
--- a/org.eclipse.egerrit.ui.rcptt.tests/reviewEditor/SubmitRevertReview.test
+++ b/org.eclipse.egerrit.ui.rcptt.tests/reviewEditor/SubmitRevertReview.test
@@ -6,7 +6,7 @@
 External-Reference: 
 Id: _qu9iQF5tEeaLErCO3SKPNg
 Runtime-Version: 2.1.0.201605312320
-Save-Time: 12/8/16 10:16 AM
+Save-Time: 3/17/17 2:07 PM
 Testcase-Type: ecl
 
 ------=_.content-0a7243a0-75d3-3d5f-9791-539de0e5b7ac
@@ -19,7 +19,7 @@
 
 // first do a +2
 get-editor | get-button "Reply..." | click
-get-menu "Code-Review+2" | click
+get-menu "Code-Review  +2" | click
 wait -ms $defaultWaitTime
 
 assert-that-first-entry-in-history-table -atColumn 3 -is "Code-Review+2" -in [get-editor]
diff --git a/org.eclipse.egerrit.ui/src/org/eclipse/egerrit/internal/process/ReplyProcess.java b/org.eclipse.egerrit.ui/src/org/eclipse/egerrit/internal/process/ReplyProcess.java
index 4f13dae..ab4476c 100644
--- a/org.eclipse.egerrit.ui/src/org/eclipse/egerrit/internal/process/ReplyProcess.java
+++ b/org.eclipse.egerrit.ui/src/org/eclipse/egerrit/internal/process/ReplyProcess.java
@@ -23,13 +23,10 @@
 import org.eclipse.egerrit.internal.core.exception.EGerritException;
 import org.eclipse.egerrit.internal.core.rest.ReviewInput;
 import org.eclipse.egerrit.internal.model.ChangeInfo;
-import org.eclipse.egerrit.internal.model.LabelInfo;
 import org.eclipse.egerrit.internal.model.RevisionInfo;
 import org.eclipse.egerrit.internal.ui.editors.QueryHelpers;
 import org.eclipse.egerrit.internal.ui.utils.Messages;
 import org.eclipse.egerrit.internal.ui.utils.UIUtils;
-import org.eclipse.emf.common.util.EList;
-import org.eclipse.emf.common.util.EMap;
 import org.eclipse.swt.widgets.Shell;
 
 /**
@@ -37,7 +34,7 @@
  */
 public class ReplyProcess {
 
-	private static final String CODE_REVIEW = "Code-Review"; //$NON-NLS-1$
+	public static final String REPLY_ALL_BUTTONS = Messages.ReplyProcess_AllButtons;
 
 	private ChangeInfo changeInfo = null;
 
@@ -56,85 +53,33 @@
 				.thenRun(() -> changeInfo.setUserSelectedRevision(selectedRevision));
 	}
 
-	public void handleReplyPlus2(RevisionInfo selectedRevision) {
+	/**
+	 * Handles the reply votes set to its maximum
+	 *
+	 * @param changeInfo
+	 * @param allowedButton
+	 * @param gerritClient
+	 */
+	public void handleReplyVotes(ChangeInfo changeInfo, Map<String, Integer> allowedButton, GerritClient gerritClient) {
+		this.gerritClient = gerritClient;
+		this.changeInfo = changeInfo;
+		RevisionInfo selectedRevision = changeInfo.getRevision();
 		ReviewInput reviewInput = new ReviewInput();
 		reviewInput.setDrafts(ReviewInput.DRAFT_PUBLISH);
-		Map<String, String> obj = new HashMap<String, String>();
-		obj.put(CODE_REVIEW, Messages.ReplyProcess_PlusTwo);
+		Map<String, Integer> obj = new HashMap<String, Integer>();
+
+		//Loop to include all the potential buttons
+		Iterator<Entry<String, Integer>> iter = allowedButton.entrySet().iterator();
+		while (iter.hasNext()) {
+			Entry<String, Integer> entry = iter.next();
+			obj.put(entry.getKey(), entry.getValue());
+		}
 
 		reviewInput.setLabels(obj);
 		postReply(reviewInput, selectedRevision);
 	}
 
 	/**
-	 * Test if we should allow the CR +2 button to be available
-	 *
-	 * @param changeInfo
-	 * @param gerritClient
-	 * @return boolean
-	 */
-	public boolean isCRPlusTwoAllowed(ChangeInfo changeInfo, GerritClient gerritClient, RevisionInfo revision) {
-		//Test if we should allow the +2 button or not
-		//Condition:
-		//     - User is a committer (maxCRDefined == maxCRPermitted)
-		//     - If user permitted, the maxCRPermitted > 0 if not Abandoned
-		//     - Selected patch set is the latest
-		//     - User cannot submit yet
-		this.changeInfo = changeInfo;
-		this.gerritClient = gerritClient;
-
-		int maxCRPermitted = findMaxPermitted(CODE_REVIEW);
-		int maxCRDefined = findMaxDefinedLabelValue(CODE_REVIEW);
-		boolean allowPlus2 = false;
-		if (maxCRDefined == maxCRPermitted && maxCRPermitted > 0) {
-			allowPlus2 = true;
-		}
-		//Verify the patchset if we are allowed only, no need to check if not allowed
-		if (allowPlus2) {
-			String psSelectedID = revision.getId();
-			if (!(psSelectedID != null && changeInfo.getLatestPatchSet().getId().compareTo(psSelectedID) == 0)) {
-				allowPlus2 = false;
-			}
-		}
-		return (!revision.isSubmitable() && allowPlus2);
-	}
-
-	private int findMaxPermitted(String label) {
-		int maxPermitted = 0;
-		EList<String> listPermitted = null;
-		EMap<String, EList<String>> mapLabels = changeInfo.getPermitted_labels();
-		if (mapLabels != null) {
-			Iterator<Entry<String, EList<String>>> iterator = mapLabels.entrySet().iterator();
-			//Get the structure having all the possible options
-			while (iterator.hasNext()) {
-				Entry<String, EList<String>> permittedlabel = iterator.next();
-				listPermitted = permittedlabel.getValue();
-				if (permittedlabel.getKey().compareTo(label) == 0) {
-					for (String element2 : listPermitted) {
-						maxPermitted = Math.max(maxPermitted, new Integer(element2.trim()));
-					}
-				}
-			}
-		}
-		return maxPermitted;
-	}
-
-	private int findMaxDefinedLabelValue(String label) {
-		Iterator<Entry<String, LabelInfo>> iterator = changeInfo.getLabels().entrySet().iterator();
-		//Get the structure having all the possible options
-		int maxDefined = 0;
-		while (iterator.hasNext()) {
-			Entry<String, LabelInfo> definedlabel = iterator.next();
-			if (definedlabel.getKey().compareTo(label) == 0) {
-				for (String element2 : definedlabel.getValue().getValues().keySet()) {
-					maxDefined = Math.max(maxDefined, new Integer(element2.trim()));
-				}
-			}
-		}
-		return maxDefined;
-	}
-
-	/**
 	 * Post the reply information to the Gerrit server
 	 *
 	 * @param reviewInput
diff --git a/org.eclipse.egerrit.ui/src/org/eclipse/egerrit/internal/ui/editors/ChangeDetailEditor.java b/org.eclipse.egerrit.ui/src/org/eclipse/egerrit/internal/ui/editors/ChangeDetailEditor.java
index 61b50a0..12eeb57 100644
--- a/org.eclipse.egerrit.ui/src/org/eclipse/egerrit/internal/ui/editors/ChangeDetailEditor.java
+++ b/org.eclipse.egerrit.ui/src/org/eclipse/egerrit/internal/ui/editors/ChangeDetailEditor.java
@@ -15,6 +15,9 @@
 package org.eclipse.egerrit.internal.ui.editors;

 

 import java.lang.reflect.InvocationTargetException;

+import java.util.Iterator;

+import java.util.Map;

+import java.util.Map.Entry;

 import java.util.concurrent.CompletableFuture;

 

 import org.eclipse.core.databinding.DataBindingContext;

@@ -59,6 +62,7 @@
 import org.eclipse.emf.databinding.EMFProperties;

 import org.eclipse.emf.ecore.util.EContentAdapter;

 import org.eclipse.jface.action.MenuManager;

+import org.eclipse.jface.action.Separator;

 import org.eclipse.jface.databinding.swt.WidgetProperties;

 import org.eclipse.jface.dialogs.IInputValidator;

 import org.eclipse.jface.dialogs.InputDialog;

@@ -82,7 +86,6 @@
 import org.eclipse.swt.widgets.Label;

 import org.eclipse.swt.widgets.Listener;

 import org.eclipse.swt.widgets.MenuItem;

-import org.eclipse.swt.widgets.Shell;

 import org.eclipse.swt.widgets.TabFolder;

 import org.eclipse.swt.widgets.Text;

 import org.eclipse.swt.widgets.ToolTip;

@@ -652,49 +655,19 @@
 

 			@Override

 			public void widgetSelected(SelectionEvent e) {

-				final Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();

-				org.eclipse.swt.widgets.Menu menu = new org.eclipse.swt.widgets.Menu(shell, SWT.POP_UP);

-				ReplyProcess replyProcess = new ReplyProcess();

-

-				final MenuItem itemReply = new MenuItem(menu, SWT.PUSH);

-				itemReply.setText(ActionConstants.REPLY.getLiteral());

-				itemReply.addSelectionListener(new SelectionListener() {

-

-					@Override

-					public void widgetSelected(SelectionEvent e) {

-						replyProcess.handleReplyDialog(replyButton.getShell(), fChangeInfo, fGerritClient,

-								fChangeInfo.getUserSelectedRevision());

-					}

-

-					@Override

-					public void widgetDefaultSelected(SelectionEvent e) {

-						// ignore

-					}

-				});

-

-				MenuItem itemCRPlus2 = new MenuItem(menu, SWT.PUSH);

-				itemCRPlus2.setText(Messages.ChangeDetailEditor_25);

-				itemCRPlus2.setEnabled(replyProcess.isCRPlusTwoAllowed(fChangeInfo, fGerritClient,

-						fChangeInfo.getUserSelectedRevision()));

-

-				if (itemCRPlus2.isEnabled()) {

-					itemCRPlus2.addSelectionListener(new SelectionAdapter() {

-						@Override

-						public void widgetSelected(SelectionEvent e) {

-							super.widgetSelected(e);

-							replyProcess.handleReplyPlus2(fChangeInfo.getUserSelectedRevision());

-						}

-					});

+				//Adjust the list of label which the current user can set to a maximum value

+				String loginUser = fGerritClient.getRepository().getServerInfo().getUserName();

+				Map<String, Integer> adjustedAllowedButton = fChangeInfo.getLabelsNotAtMax(loginUser);

+				if (adjustedAllowedButton.isEmpty()) {

+					//Should open directly the reply dialog, no sub-menu

+					ReplyProcess replyProcess = new ReplyProcess();

+					replyProcess.handleReplyDialog(replyButton.getShell(), fChangeInfo, fGerritClient,

+							fChangeInfo.getUserSelectedRevision());

+				} else {

+					MenuManager mgr = new MenuManager();

+					buildReplyDynamicMenu(adjustedAllowedButton, mgr);

+					mgr.createContextMenu(replyButton).setVisible(true);

 				}

-

-				Point loc = replyButton.getLocation();

-				Rectangle rect = replyButton.getBounds();

-

-				Point mLoc = new Point(loc.x - 1, loc.y + rect.height);

-

-				menu.setLocation(shell.getDisplay().map(replyButton.getParent(), null, mLoc));

-

-				menu.setVisible(true);

 			}

 		});

 

@@ -706,6 +679,7 @@
 		bindingContext.bindValue(WidgetProperties.enabled().observe(draftButton), observeDeleteable,

 				new UpdateValueStrategy(UpdateValueStrategy.POLICY_NEVER), null);

 		draftButton.addSelectionListener(new SelectionAdapter() {

+

 			@Override

 			public void widgetSelected(SelectionEvent e) {

 				org.eclipse.swt.widgets.Menu menu = new org.eclipse.swt.widgets.Menu(draftButton.getShell(),

@@ -771,12 +745,35 @@
 

 				menu.setVisible(true);

 			}

+

 		});

 

 		c.setSize(c.computeSize(SWT.DEFAULT, SWT.DEFAULT));

 		return c;

 	}

 

+	private void buildReplyDynamicMenu(Map<String, Integer> adjustedAllowedButton, MenuManager mgr) {

+		mgr.add(new ReplyHandler(fChangeInfo, fGerritClient, ActionConstants.REPLY.getLiteral())); //Add the Reply option

+

+		//Adjust the list of label which the current user can set to a maximum value

+		int size = adjustedAllowedButton.size();

+

+		//If there is some menu to add, then lets create a separator

+		if (size > 0) {

+			mgr.add(new Separator());

+		}

+		//Add all potential menu items

+		Iterator<Entry<String, Integer>> iter = adjustedAllowedButton.entrySet().iterator();

+		while (iter.hasNext()) {

+			Entry<String, Integer> entry = iter.next();

+			mgr.add(new ReplyHandler(fChangeInfo, fGerritClient, entry.getKey()));

+		}

+		//Add a selection for all buttons when there is more than one selection

+		if (size > 1) {

+			mgr.add(new ReplyHandler(fChangeInfo, fGerritClient, ReplyProcess.REPLY_ALL_BUTTONS));

+		}

+	}

+

 	private SelectionListener downloadButtonListener(Composite parent) {

 		return new SelectionListener() {

 			@Override

diff --git a/org.eclipse.egerrit.ui/src/org/eclipse/egerrit/internal/ui/editors/ReplyDialog.java b/org.eclipse.egerrit.ui/src/org/eclipse/egerrit/internal/ui/editors/ReplyDialog.java
index d02bc94..85ba912 100644
--- a/org.eclipse.egerrit.ui/src/org/eclipse/egerrit/internal/ui/editors/ReplyDialog.java
+++ b/org.eclipse.egerrit.ui/src/org/eclipse/egerrit/internal/ui/editors/ReplyDialog.java
@@ -14,12 +14,10 @@
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
-import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 
 import org.eclipse.egerrit.internal.core.GerritClient;
-import org.eclipse.egerrit.internal.model.ApprovalInfo;
 import org.eclipse.egerrit.internal.model.CommentInfo;
 import org.eclipse.egerrit.internal.model.FileInfo;
 import org.eclipse.egerrit.internal.model.LabelInfo;
@@ -30,7 +28,6 @@
 import org.eclipse.emf.common.util.EList;
 import org.eclipse.emf.common.util.EMap;
 import org.eclipse.jface.dialogs.InputDialog;
-import org.eclipse.jface.resource.StringConverter;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.ScrolledComposite;
 import org.eclipse.swt.events.ControlEvent;
@@ -68,7 +65,7 @@
 	// A map of the permitted labels that maps a label name to the list of
 	// values that are allowed for that label. Only set if 'detailed labels' are
 	// requested.
-	private EMap<String, EList<String>> permitted_labels;
+	private Map<String, EList<String>> permitted_labels;
 
 	private Composite keyComposite;
 
@@ -80,14 +77,12 @@
 
 	private final String DISPLAYWIDGET = "displayWidget"; //$NON-NLS-1$
 
-	private LinkedHashMap<String, String> previousVotes = new LinkedHashMap<String, String>();
+	private Map<String, Integer> lastUserVotes = new LinkedHashMap<String, Integer>();
 
 	private RevisionInfo fRevisionInfo;
 
 	private GerritClient fGerritClient;
 
-	private static final int WIDTH = 650;
-
 	/**
 	 * The constructor.
 	 */
@@ -95,7 +90,7 @@
 		super(shell, Messages.ReplyDialog_0, buildMessage(reason, revisionToReplyTo), null, null);
 		fRevisionInfo = revisionToReplyTo;
 		fGerritClient = gerritClient;
-		permitted_labels = revisionToReplyTo.getChangeInfo().getPermitted_labels();
+		permitted_labels = revisionToReplyTo.getChangeInfo().getSortedPermittedLabels();
 		labelsInfo = revisionToReplyTo.getChangeInfo().getLabels();
 
 		boolean isVoteAllowed = revisionToReplyTo.getId()
@@ -140,6 +135,10 @@
 		((GridData) this.getText().getLayoutData()).verticalAlignment = SWT.FILL;
 		//Create area to display the draft comments
 		createMessageArea(composite);
+		GridLayout compositeLayout = (GridLayout) composite.getLayout();
+		compositeLayout.verticalSpacing = 0;
+		compositeLayout.marginHeight = 0;
+		composite.setLayout(compositeLayout);
 
 		//Create the section handling the radio buttons
 		if (labelsInfo != null) {
@@ -151,9 +150,27 @@
 
 			@Override
 			public void controlResized(ControlEvent e) {
-				Point size = parent.getShell().computeSize(WIDTH, SWT.DEFAULT);
-				parent.getShell().setMinimumSize(size);
-				parent.getShell().setSize(size);
+				if (keyComposite != null) { //Null when we do not create this section.
+					int widthKey = getMinimalWidth(keyComposite, -1);
+					int widthRadio = getMinimalWidth(radioButtonComposite, -1);
+					int widthdetail = getMinimalWidth(detailTextComposite, 150);
+					int widthTotal = 0;
+					if (widthKey < 25 || widthRadio < 25 || widthdetail < 25) {
+						widthTotal = 250;
+					} else {
+						widthTotal = widthKey + widthRadio + widthdetail;
+					}
+
+					Point size = parent.getShell().computeSize(widthTotal, SWT.DEFAULT);
+					parent.getShell().setMinimumSize(size);
+					parent.getShell().setSize(size);
+
+				} else {
+					//set a minimum size, there is no review labels
+					Point size = parent.getShell().computeSize(SWT.DEFAULT, SWT.DEFAULT);
+					parent.getShell().setMinimumSize(size);
+					parent.getShell().setSize(size);
+				}
 
 				if (hasDrafts(fRevisionInfo)) {
 					//Re-layout the DRAFT AREA
@@ -175,11 +192,67 @@
 				}
 			}
 		});
+		//Reset the field from the inputDialog
+		resetErrorSection(composite);
+
 		return parent;
 
 	}
 
 	/**
+	 * Adjust the fields inside the input dialog
+	 *
+	 * @param composite
+	 */
+	private void resetErrorSection(Composite composite) {
+		Control[] compChild = composite.getChildren();
+
+		if (compChild != null) {
+			//Adjust the Label not to expand when resizing, but keep the new height to the scroll text
+			if (compChild[0] instanceof Label) {
+				Label lbl = (Label) compChild[0];
+				GridData gridData = (GridData) lbl.getLayoutData();
+				gridData.grabExcessVerticalSpace = false;
+				lbl.setLayoutData(gridData);
+				lbl.setSize(lbl.computeSize(SWT.DEFAULT, SWT.DEFAULT));
+			}
+
+			//Lets adjust the error text not shown here
+			if (compChild.length > 2 && compChild[2] instanceof Text) {
+				Text txt = (Text) compChild[2];
+				GridData gridData = (GridData) txt.getLayoutData();
+				gridData.minimumHeight = 0;
+				gridData.grabExcessVerticalSpace = false;
+				txt.setLayoutData(gridData);
+				txt.setSize(txt.computeSize(SWT.DEFAULT, SWT.DEFAULT));
+			}
+		}
+	}
+
+	/**
+	 * Compute the minimal width to set for a composite
+	 *
+	 * @param composite
+	 * @param minWidth
+	 * @return int
+	 */
+	private int getMinimalWidth(Composite composite, int minWidth) {
+		int min = 0;
+		Point sizeComposite = composite.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+		//If min width <0, it means, we take whatever the value from the computation
+		if (minWidth < 0) {
+			min = sizeComposite.x;
+		} else {
+			if ((sizeComposite.x > minWidth) || (sizeComposite.x == 0)) {
+				min = minWidth;
+			} else {
+				min = sizeComposite.x;
+			}
+		}
+		return min;
+	}
+
+	/**
 	 * Test if there are any DRAFTS comments in this revision
 	 *
 	 * @param revInfo
@@ -204,7 +277,7 @@
 			scrolledDraftArea = new ScrolledComposite(parent, SWT.V_SCROLL | SWT.H_SCROLL);
 
 			GridData grid = new GridData(GridData.FILL_BOTH);
-			grid.heightHint = 100;
+			grid.heightHint = 50;
 			scrolledDraftArea.setLayoutData(grid);
 			Composite composite = new Composite(scrolledDraftArea, SWT.NONE);
 
@@ -222,6 +295,7 @@
 					// ignore
 					Rectangle size = composite.getParent().getClientArea();
 					composite.setSize(composite.computeSize(size.width, SWT.DEFAULT));
+
 					scrolledDraftArea.setMinWidth(size.x);
 					Rectangle compoHeight = composite.getBounds();
 					scrolledDraftArea.setMinHeight(compoHeight.height);
@@ -294,9 +368,11 @@
 			sepGrid.grabExcessHorizontalSpace = true;
 			separator.setLayoutData(sepGrid);
 		}
-
+		int maxRadio = getMaxCountLabels();
+		if (maxRadio <= 0) {
+			return;//no need to create the composite
+		}
 		Composite composite = new Composite(parent, SWT.NONE);
-
 		GridLayout gl_composite = new GridLayout(3, false);
 		gl_composite.marginTop = 3;
 		composite.setLayout(gl_composite);
@@ -305,21 +381,20 @@
 		gd_composite.minimumHeight = height;
 		gd_composite.heightHint = height;
 		composite.setLayoutData(gd_composite);
-		keyComposite = createComposite(composite, SWT.LEFT, 1);
-		radioButtonComposite = createComposite(composite, SWT.CENTER, 1);
-		detailTextComposite = createComposite(composite, SWT.LEFT, 1);
+		keyComposite = createComposite(composite, SWT.LEFT, 1, false);
+		radioButtonComposite = createComposite(composite, SWT.CENTER, 1, false);
+		detailTextComposite = createComposite(composite, SWT.LEFT, 1, true);
 
 		//Create the dynamic selection
 		createRadioButtonSelection();
 	}
 
-	private Composite createComposite(Composite parent, int horizontalSwt, int numColumn) {
+	private Composite createComposite(Composite parent, int horizontalSwt, int numColumn, boolean grabHorizon) {
 		Composite composite = new Composite(parent, SWT.NONE);
-		GridLayout gl_composite = new GridLayout(numColumn, true);
+		GridLayout gl_composite = new GridLayout(numColumn, false);
 		gl_composite.marginTop = 3;
 		composite.setLayout(gl_composite);
-		GridData gd_composite = new GridData(horizontalSwt, SWT.CENTER, true, false);
-		gd_composite.minimumHeight = 117;
+		GridData gd_composite = new GridData(horizontalSwt, SWT.CENTER, grabHorizon, false);
 		composite.setLayoutData(gd_composite);
 		return composite;
 	}
@@ -355,65 +430,22 @@
 		Composite headerLabels = getRadioButtonHeaderLabels(maxRadio);
 		Point sizeRadio = null;
 		Point fontSize = UIUtils.computeFontSize(detailTextComposite);
-		//Set into the variable the last setting of the radio
-		getLastLabelSet();
 
+		//Get the last votes for all labels according to a user
+		String loginUser = fGerritClient.getRepository().getServerInfo().getUserName();
+		lastUserVotes = fRevisionInfo.getChangeInfo().getUserLastLabelSet(loginUser);
+
+		//Set the radio buttons
 		Iterator<Map.Entry<String, EList<String>>> iterator = permitted_labels.entrySet().iterator();
 		while (iterator.hasNext()) {
 			Entry<String, EList<String>> permittedlabel = iterator.next();
-			EList<String> listPermitted = permittedlabel.getValue();
-			Label rowLabel = new Label(keyComposite, SWT.BOLD);
-			rowLabel.setText(permittedlabel.getKey());
-
-			String valueSet = previousVotes.get(permittedlabel.getKey());
-			if (valueSet == null) {
-				valueSet = "0"; //$NON-NLS-1$
-			}
-
-			//fetch the last value set for this key
-			int lastValue = Integer.parseInt(valueSet);
-
-			//Create the text data for the selection
-			Label detailLabel = new Label(detailTextComposite, SWT.NONE);
-			GridData grid = new GridData(SWT.FILL, SWT.LEFT, true, false);
-			grid.minimumWidth = fontSize.x * 50;
-			detailLabel.setLayoutData(grid);
-
-			Composite radioComposite = createButtonComposite(radioButtonComposite, maxRadio);
-
-			radioComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-			//Fill the dummy space
-			int numEmpty = (maxRadio - listPermitted.size()) / 2;//Only half the number at the beginning
-			createFillerRadioButtons(numEmpty, radioComposite);
-
-			//Fill the radio buttons
-			Button[] radios = new Button[listPermitted.size()];
-			for (int i = 0; i < listPermitted.size(); i++) {
-				//Create a radio and store extra data into the structure
-				radios[i] = new Button(radioComposite, SWT.RADIO);
-				radios[i].setData(listPermitted.get(i));
-				radios[i].setData(listPermitted.get(i), permittedlabel.getKey());
-				radios[i].setData(DISPLAYWIDGET, detailLabel);//set the display label widget
-
-				String description = labelSelectionDescription(permittedlabel.getKey(), listPermitted.get(i));
-				radios[i].setToolTipText(description);
-				if (Integer.parseInt(listPermitted.get(i).trim()) == lastValue) {
-					radios[i].setSelection(true);
-					detailLabel.setText(description);
-				}
-
-				radios[i].addListener(SWT.Selection, radioGroupListener());
-			}
-
-			//Complete to fill the dummy space
-			createFillerRadioButtons(numEmpty, radioComposite);
-			radioComposite.pack();
-			sizeRadio = radioComposite.getSize();
+			sizeRadio = createARowRadioLabel(permittedlabel, maxRadio, fontSize.x);
 		}
 
 		//Need to adjust the label position to align it with the buttons
 		Point headerSize = headerLabels.getSize();
 		GridLayout layout = (GridLayout) headerLabels.getLayout();
+		int radioHeight = fontSize.y; //default height
 
 		//Set the margin for the radio button top labels
 		if (sizeRadio != null) {
@@ -421,7 +453,99 @@
 			layout.marginWidth = space / 2;
 			layout.horizontalSpacing = space;
 			headerLabels.setLayout(layout);
+			radioHeight = sizeRadio.y;
 		}
+		reAdjustRadioLayout(fontSize, radioHeight);
+	}
+
+	private Point createARowRadioLabel(Entry<String, EList<String>> permittedlabel, int maxRadio, int fontWidth) {
+		EList<String> listPermitted = permittedlabel.getValue();
+		Point sizeRadio = null;
+		Label rowLabel = new Label(keyComposite, SWT.BOLD);
+		rowLabel.setText(permittedlabel.getKey());
+
+		//Create the text data for the selection
+		Label detailLabel = new Label(detailTextComposite, SWT.NONE);
+		GridData grid = new GridData(SWT.FILL, SWT.LEFT, true, false);
+		grid.minimumWidth = fontWidth * 500;
+		detailLabel.setLayoutData(grid);
+
+		Composite radioComposite = createButtonComposite(radioButtonComposite, maxRadio);
+
+		radioComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+		//Fill the dummy space
+		int middlePoint = (maxRadio + 1) / 2; //button position for the column = 0;
+		int minValue = Integer.parseInt(listPermitted.get(0)); //get the first minimum value
+		int numEmptyBefore = middlePoint + minValue - 1;
+		int numEmptyAfter = middlePoint - Integer.parseInt(listPermitted.get(listPermitted.size() - 1)) - 1;//compute the number of empty space to add at the end
+		createFillerRadioButtons(numEmptyBefore, radioComposite);
+
+		//Fill the radio buttons
+		Button[] radios = new Button[listPermitted.size()];
+		for (int i = 0; i < listPermitted.size(); i++) {
+			//Create a radio and store extra data into the structure
+			radios[i] = new Button(radioComposite, SWT.RADIO);
+			radios[i].setData(listPermitted.get(i));
+			radios[i].setData(listPermitted.get(i), permittedlabel.getKey());
+			radios[i].setData(DISPLAYWIDGET, detailLabel);//set the display label widget
+
+			String description = labelSelectionDescription(permittedlabel.getKey(), listPermitted.get(i));
+			radios[i].setToolTipText(description);
+			int userVotes = lastUserVotes.get(permittedlabel.getKey()) != null
+					? lastUserVotes.get(permittedlabel.getKey())
+					: 0;
+			if (Integer.parseInt(listPermitted.get(i).trim()) == userVotes) {
+				radios[i].setSelection(true);
+				detailLabel.setText(description);
+			}
+
+			radios[i].addListener(SWT.Selection, radioGroupListener());
+		}
+
+		//Complete to fill the dummy space
+		createFillerRadioButtons(numEmptyAfter, radioComposite);
+		radioComposite.pack();
+		sizeRadio = radioComposite.getSize();
+		return sizeRadio;
+	}
+
+	/**
+	 * Re-set the layout data for the radio area in the dialog
+	 *
+	 * @param fontSize
+	 * @param radioHeight
+	 */
+	private void reAdjustRadioLayout(Point fontSize, int radioHeight) {
+		int topMargin = 0;
+		int verticalSpacing = radioHeight - fontSize.y + 4;
+		Point compSize = radioButtonComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+		GridData radioData = (GridData) radioButtonComposite.getLayoutData();
+		radioData.minimumWidth = compSize.x;
+		radioButtonComposite.setLayoutData(radioData);
+
+		//Reset the layout for Labels area
+		GridData gridData = (GridData) keyComposite.getLayoutData();
+		gridData.heightHint = compSize.y;
+
+		keyComposite.setLayoutData(gridData);
+		GridLayout keyLayout = (GridLayout) keyComposite.getLayout();
+		keyLayout.marginTop = topMargin;
+
+		keyLayout.verticalSpacing = verticalSpacing;
+		keyComposite.setLayout(keyLayout);
+		Point size = keyComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+		keyComposite.setSize(size);
+
+		//Reset the layout for Details
+		GridData gridDataDetail = (GridData) detailTextComposite.getLayoutData();
+		gridDataDetail.heightHint = compSize.y;
+		detailTextComposite.setLayoutData(gridDataDetail);
+
+		GridLayout detailLayout = (GridLayout) detailTextComposite.getLayout();
+		detailLayout.marginTop = topMargin;
+		detailLayout.verticalSpacing = verticalSpacing;
+		detailTextComposite.setLayout(detailLayout);
+		detailTextComposite.setSize(detailTextComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT));
 	}
 
 	/**
@@ -446,7 +570,6 @@
 		Label permittedLabel = new Label(keyComposite, SWT.NONE);
 		//Create the text data display for the selection
 		Label detailLabel = new Label(detailTextComposite, SWT.NONE);
-
 		//Create the radio buttons header labels
 		Composite radioComposite = createButtonComposite(radioButtonComposite, maxRadioChoice);
 		radioComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, maxRadioChoice, 1));
@@ -511,7 +634,11 @@
 				Label toShow = (Label) objWidget;
 				toShow.setText(tootip);
 				toShow.pack();
-				previousVotes.put(keyLabel, (String) obj);
+				String st = (String) obj;
+				if (st.startsWith("+")) { //$NON-NLS-1$
+					st = st.substring(1);//parse the string for the positive value
+				}
+				lastUserVotes.put(keyLabel, Integer.parseInt(st.trim()));
 			}
 		};
 	}
@@ -526,55 +653,19 @@
 	private String labelSelectionDescription(String key, String value) {
 		String ret = ""; //$NON-NLS-1$
 		if (labelsInfo != null && !labelsInfo.isEmpty()) {
-			Iterator<Map.Entry<String, LabelInfo>> labelIter = labelsInfo.entrySet().iterator();
-			while (labelIter.hasNext()) {
-				Entry<String, LabelInfo> entrylabel = labelIter.next();
-				if (entrylabel.getKey().equals(key)) {
-					LabelInfo labelInfo = entrylabel.getValue();
-					EMap<String, String> mapValues = labelInfo.getValues();
-					return mapValues.get(value);
-				}
-			}
+			LabelInfo info = labelsInfo.get(key);
+			return info.getValues().get(value);
 		}
 		return ret;
 	}
 
 	/**
-	 * Read the received labels and keep the last setting for each labels. Will use it to set the radio buttons
-	 * selection
-	 */
-	private void getLastLabelSet() {
-		if (labelsInfo != null && !labelsInfo.isEmpty()) {
-			Iterator<Map.Entry<String, LabelInfo>> labelIter = labelsInfo.entrySet().iterator();
-			while (labelIter.hasNext()) {
-				Entry<String, LabelInfo> entrylabel = labelIter.next();
-				LabelInfo labelInfo = entrylabel.getValue();
-				List<ApprovalInfo> listApproval = labelInfo.getAll();
-				if (listApproval != null && !listApproval.isEmpty()) {
-					ApprovalInfo candidate = null;
-					//Find the most recent vote for the current user
-					for (ApprovalInfo oneApproval : listApproval) {
-						if (fGerritClient.getRepository().getServerInfo().getUserName().equals(
-								oneApproval.getEmail())) {
-							if (candidate == null) {
-								candidate = oneApproval;
-							}
-						}
-					}
-					if (candidate != null) {
-						previousVotes.put(entrylabel.getKey(), StringConverter.asString(candidate.getValue()));
-					}
-				}
-			}
-		}
-	}
-
-	/**
 	 * This method return the hashMap of the selected radio buttons
 	 *
-	 * @return LinkedHashMap<String, String>
+	 * @return LinkedHashMap<String, Integer>
 	 */
-	public Map<String, String> getRadiosSelection() {
-		return previousVotes;
+	public Map<String, Integer> getRadiosSelection() {
+		return lastUserVotes;
 	}
+
 }
diff --git a/org.eclipse.egerrit.ui/src/org/eclipse/egerrit/internal/ui/editors/ReplyHandler.java b/org.eclipse.egerrit.ui/src/org/eclipse/egerrit/internal/ui/editors/ReplyHandler.java
new file mode 100644
index 0000000..c04d8ec
--- /dev/null
+++ b/org.eclipse.egerrit.ui/src/org/eclipse/egerrit/internal/ui/editors/ReplyHandler.java
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Ericsson AB.
+ * 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:
+ *     Ericsson - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.egerrit.internal.ui.editors;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+
+import org.eclipse.egerrit.internal.core.GerritClient;
+import org.eclipse.egerrit.internal.model.ActionConstants;
+import org.eclipse.egerrit.internal.model.ChangeInfo;
+import org.eclipse.egerrit.internal.process.ReplyProcess;
+import org.eclipse.jface.action.Action;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * Code to handle the dynamic reply labels associated to a review for a specific user
+ */
+public class ReplyHandler extends Action {
+
+	private GerritClient fGerritClient;
+
+	private ChangeInfo fChangeInfo;
+
+	private String fLabelText;
+
+	private int maxValue;
+
+	public ReplyHandler(ChangeInfo changeInfo, GerritClient gerritClient, String labelText) {
+		this.fGerritClient = gerritClient;
+		this.fChangeInfo = changeInfo;
+		String actionLabel = labelText;
+		if (labelText != ReplyProcess.REPLY_ALL_BUTTONS && labelText != ActionConstants.REPLY.getLiteral()) {
+			maxValue = fChangeInfo.getPermittedMaxValue(labelText);
+			actionLabel = labelText + "  +" + maxValue; //$NON-NLS-1$
+		}
+		this.fLabelText = labelText;
+		this.setText(actionLabel);
+	}
+
+	@Override
+	public void run() {
+		ReplyProcess replyProcess = new ReplyProcess();
+		TreeMap<String, Integer> mapLabels = new TreeMap<>();
+
+		if (fLabelText.equals(ActionConstants.REPLY.getLiteral())) {
+			//Deal with the reply dialog
+			replyProcess.handleReplyDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), fChangeInfo,
+					fGerritClient, fChangeInfo.getUserSelectedRevision());
+			return;
+		} else if (fLabelText.equals(ReplyProcess.REPLY_ALL_BUTTONS)) {
+			//Adjust the list of labels which the current user can set to a maximum value
+			String loginUser = fGerritClient.getRepository().getServerInfo().getUserName();
+			Map<String, Integer> labelsToSet = fChangeInfo.getLabelsNotAtMax(loginUser);
+
+			//Loop to include all the potential labels
+			Iterator<Entry<String, Integer>> iter = labelsToSet.entrySet().iterator();
+			while (iter.hasNext()) {
+				Entry<String, Integer> entry = iter.next();
+				mapLabels.put(entry.getKey(), entry.getValue());
+			}
+		} else {
+			//Selecting a specific label
+			mapLabels.put(fLabelText, maxValue);
+		}
+		if (!mapLabels.isEmpty()) {
+			replyProcess.handleReplyVotes(fChangeInfo, mapLabels, fGerritClient);
+		}
+	}
+}
diff --git a/org.eclipse.egerrit.ui/src/org/eclipse/egerrit/internal/ui/utils/Messages.java b/org.eclipse.egerrit.ui/src/org/eclipse/egerrit/internal/ui/utils/Messages.java
index 1501fc2..7e59feb 100644
--- a/org.eclipse.egerrit.ui/src/org/eclipse/egerrit/internal/ui/utils/Messages.java
+++ b/org.eclipse.egerrit.ui/src/org/eclipse/egerrit/internal/ui/utils/Messages.java
@@ -508,16 +508,12 @@
 
 	public static String RebaseProcess_notPerform;
 
-	public static String ReplyProcess_PlusTwo;
+	public static String ReplyProcess_AllButtons;
 
 	public static String ReplyHandler_generalMessage;
 
 	public static String ReplyHandler_specificMessage;
 
-	public static String ReplyPlusTwoHandler_generalMessage;
-
-	public static String ReplyPlusTwoHandler_specificMessage;
-
 	public static String SubmitHandler_title;
 
 	public static String SubmitHandler_notAvailable;
diff --git a/org.eclipse.egerrit.ui/src/org/eclipse/egerrit/internal/ui/utils/messages.properties b/org.eclipse.egerrit.ui/src/org/eclipse/egerrit/internal/ui/utils/messages.properties
index 04116b0..a259bb0 100644
--- a/org.eclipse.egerrit.ui/src/org/eclipse/egerrit/internal/ui/utils/messages.properties
+++ b/org.eclipse.egerrit.ui/src/org/eclipse/egerrit/internal/ui/utils/messages.properties
@@ -270,9 +270,7 @@
 RebaseProcess_WouldYouLikeAutomaticRebase=Would you like to run the automatic local rebase process? This will
 ReplyHandler_generalMessage=Open the Reply dialog for the latest patch-set.
 ReplyHandler_specificMessage=Open the Reply dialog for review id {0} with the latest patch-set {1}
-ReplyPlusTwoHandler_generalMessage=Send a +2 as a reply comment.
-ReplyPlusTwoHandler_specificMessage=Send a +2 as a reply comment for review id {0} with the latest patch-set {1}
-ReplyProcess_PlusTwo=2
+ReplyProcess_AllButtons=Reply All Previous
 SubmitHandler_title=Submit
 SubmitHandler_notAvailable=Submit not available for {0}
 SubmitProcess_failed=Submit failed
@@ -283,4 +281,4 @@
 UIHistoryTable_reset=Reset all filters
 UnsupportedInput_Text=EGerrit does not allow comments to be entered when a subset of the file is shown. Please reselect the file in the upper section of the compare editor.
 UnsupportedInput_Title=Comparison of details not supported
-RefreshJobName=Checking for updates in review {0}.
\ No newline at end of file
+RefreshJobName=Checking for updates in review {0}.