Bug 577564 - add priority attribute to variableValueEditor

New "priority" attribute is optional integer number 0 to 10 describing
priority of this extension, with 0 being the lowest priority. If
multiple contributions have the same priority, an arbitrary contribution
among them will be selected.

Change-Id: Ib787799214f31423adb66ec1c5bd56f555f97dde
Signed-off-by: Andrey Loskutov <loskutov@gmx.de>
Reviewed-on: https://git.eclipse.org/r/c/platform/eclipse.platform.debug/+/188423
Tested-by: Platform Bot <platform-bot@eclipse.org>
Reviewed-by: Simeon Andreev <simeon.danailov.andreev@gmail.com>
diff --git a/org.eclipse.debug.tests/META-INF/MANIFEST.MF b/org.eclipse.debug.tests/META-INF/MANIFEST.MF
index baca5dc..856c28e 100644
--- a/org.eclipse.debug.tests/META-INF/MANIFEST.MF
+++ b/org.eclipse.debug.tests/META-INF/MANIFEST.MF
@@ -29,6 +29,7 @@
  org.eclipse.debug.tests.sourcelookup,
  org.eclipse.debug.tests.statushandlers,
  org.eclipse.debug.tests.stepfilters,
+ org.eclipse.debug.tests.ui,
  org.eclipse.debug.tests.view.memory,
  org.eclipse.debug.tests.viewer.model
 Eclipse-BundleShape: dir
diff --git a/org.eclipse.debug.tests/plugin.xml b/org.eclipse.debug.tests/plugin.xml
index a350e91..4503162 100644
--- a/org.eclipse.debug.tests/plugin.xml
+++ b/org.eclipse.debug.tests/plugin.xml
@@ -142,4 +142,19 @@
             modelIdentifier="org.eclipse.debug.tests">
       </logicalStructureType>
    </extension>
+   <extension
+       point="org.eclipse.debug.ui.variableValueEditors">
+     <variableValueEditor
+         modelId="testModel"
+         priority="x"
+         class="org.eclipse.debug.tests.ui.TestVariableValueEditor1"/>
+     <variableValueEditor
+         modelId="testModel"
+         priority="10"
+         class="org.eclipse.debug.tests.ui.TestVariableValueEditor2"/>
+     <variableValueEditor
+         modelId="testModel"
+         priority="-1"
+         class="org.eclipse.debug.tests.ui.TestVariableValueEditor3"/>
+   </extension>
 </plugin>
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java
index 3692256..be2dc61 100644
--- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java
@@ -44,6 +44,7 @@
 import org.eclipse.debug.tests.sourcelookup.SourceLookupFacilityTests;
 import org.eclipse.debug.tests.statushandlers.StatusHandlerTests;
 import org.eclipse.debug.tests.stepfilters.StepFiltersTests;
+import org.eclipse.debug.tests.ui.VariableValueEditorManagerTests;
 import org.eclipse.debug.tests.view.memory.MemoryRenderingTests;
 import org.eclipse.debug.tests.view.memory.TableRenderingTests;
 import org.eclipse.debug.tests.viewer.model.ChildrenUpdateTests;
@@ -89,6 +90,7 @@
 		FilterTransformTests.class,
 		ChildrenUpdateTests.class,
 		PresentationContextTests.class,
+		VariableValueEditorManagerTests.class,
 
 		// Memory view
 		MemoryRenderingTests.class,
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/ui/TestVariableValueEditor1.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/ui/TestVariableValueEditor1.java
new file mode 100644
index 0000000..eb2a93f
--- /dev/null
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/ui/TestVariableValueEditor1.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2021 Andrey Loskutov and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Andrey Loskutov (loskutov@gmx.de) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.debug.tests.ui;
+
+import org.eclipse.debug.core.model.IVariable;
+import org.eclipse.debug.ui.actions.IVariableValueEditor;
+import org.eclipse.swt.widgets.Shell;
+
+public class TestVariableValueEditor1 implements IVariableValueEditor {
+
+	@Override
+	public boolean editVariable(IVariable variable, Shell shell) {
+		return false;
+	}
+
+	@Override
+	public boolean saveVariable(IVariable variable, String expression, Shell shell) {
+		return false;
+	}
+
+}
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/ui/TestVariableValueEditor2.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/ui/TestVariableValueEditor2.java
new file mode 100644
index 0000000..658968f
--- /dev/null
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/ui/TestVariableValueEditor2.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2021 Andrey Loskutov and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Andrey Loskutov (loskutov@gmx.de) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.debug.tests.ui;
+
+import org.eclipse.debug.core.model.IVariable;
+import org.eclipse.debug.ui.actions.IVariableValueEditor;
+import org.eclipse.swt.widgets.Shell;
+
+public class TestVariableValueEditor2 implements IVariableValueEditor {
+
+	@Override
+	public boolean editVariable(IVariable variable, Shell shell) {
+		return false;
+	}
+
+	@Override
+	public boolean saveVariable(IVariable variable, String expression, Shell shell) {
+		return false;
+	}
+
+}
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/ui/TestVariableValueEditor3.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/ui/TestVariableValueEditor3.java
new file mode 100644
index 0000000..09c9cfa
--- /dev/null
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/ui/TestVariableValueEditor3.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2021 Andrey Loskutov and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Andrey Loskutov (loskutov@gmx.de) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.debug.tests.ui;
+
+import org.eclipse.debug.core.model.IVariable;
+import org.eclipse.debug.ui.actions.IVariableValueEditor;
+import org.eclipse.swt.widgets.Shell;
+
+public class TestVariableValueEditor3 implements IVariableValueEditor {
+
+	@Override
+	public boolean editVariable(IVariable variable, Shell shell) {
+		return false;
+	}
+
+	@Override
+	public boolean saveVariable(IVariable variable, String expression, Shell shell) {
+		return false;
+	}
+
+}
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/ui/VariableValueEditorManagerTests.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/ui/VariableValueEditorManagerTests.java
new file mode 100644
index 0000000..bbb07de
--- /dev/null
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/ui/VariableValueEditorManagerTests.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2021 Andrey Loskutov and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Andrey Loskutov (loskutov@gmx.de) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.debug.tests.ui;
+
+import static org.junit.Assert.assertEquals;
+
+import org.eclipse.debug.internal.ui.VariableValueEditorManager;
+import org.eclipse.debug.tests.AbstractDebugTest;
+import org.eclipse.debug.ui.actions.IVariableValueEditor;
+import org.junit.Test;
+
+/**
+ * Tests status handlers
+ */
+public class VariableValueEditorManagerTests extends AbstractDebugTest {
+
+	@Test
+	public void testHighestPriorityEditorUsed() {
+		IVariableValueEditor editor = VariableValueEditorManager.getDefault().getVariableValueEditor("testModel");
+		assertEquals("Not the editor with highest priority used by VariableValueEditorManager", TestVariableValueEditor2.class, editor.getClass());
+	}
+
+
+
+}
diff --git a/org.eclipse.debug.ui/schema/variableValueEditors.exsd b/org.eclipse.debug.ui/schema/variableValueEditors.exsd
index 76d5854..77d8b01 100644
--- a/org.eclipse.debug.ui/schema/variableValueEditors.exsd
+++ b/org.eclipse.debug.ui/schema/variableValueEditors.exsd
@@ -2,18 +2,23 @@
 <!-- Schema file written by PDE -->
 <schema targetNamespace="org.eclipse.debug.ui" xmlns="http://www.w3.org/2001/XMLSchema">
 <annotation>
-      <appinfo>
+      <appInfo>
          <meta.schema plugin="org.eclipse.debug.ui" id="variableValueEditors" name="Variable Value Editors"/>
-      </appinfo>
+      </appInfo>
       <documentation>
          This extension point provides a mechanism for contributing variable value editors for a particular debug model. Variable value editors are used to edit/save variable values. When the user invokes the &quot;Change Value...&quot; or &quot;Assign Value&quot; (details pane) actions on a variable in the Variables view, the contributed &lt;code&gt;org.eclipse.debug.ui.actions.IVariableValueEditor&lt;/code&gt; will be invoked to change the value.
       </documentation>
    </annotation>
 
    <element name="extension">
+      <annotation>
+         <appInfo>
+            <meta.element />
+         </appInfo>
+      </annotation>
       <complexType>
          <sequence>
-            <element ref="variableValueEditor"/>
+            <element ref="variableValueEditor" minOccurs="1" maxOccurs="unbounded"/>
          </sequence>
          <attribute name="point" type="string" use="required">
             <annotation>
@@ -46,9 +51,9 @@
                <documentation>
                   Implementation of &lt;code&gt;org.eclipse.debug.ui.actions.IVariableValueEditor&lt;/code&gt;
                </documentation>
-               <appinfo>
+               <appInfo>
                   <meta.attribute kind="java" basedOn=":org.eclipse.debug.ui.actions.IVariableValueEditor"/>
-               </appinfo>
+               </appInfo>
             </annotation>
          </attribute>
          <attribute name="modelId" type="string" use="required">
@@ -58,22 +63,31 @@
                </documentation>
             </annotation>
          </attribute>
+         <attribute name="priority" type="string">
+            <annotation>
+               <documentation>
+                  Optional integer number 0 to 10 describing priority of this extension, with 0 being the lowest priority. 
+If values outside of [0,10] range are specified, they are replaced with appropriate limits, any non integer values are ignored and zero (lowest priority) is used.
+If multiple contributions have the same priority, an arbitrary contribution among them will be selected.
+               </documentation>
+            </annotation>
+         </attribute>
       </complexType>
    </element>
 
    <annotation>
-      <appinfo>
+      <appInfo>
          <meta.section type="since"/>
-      </appinfo>
+      </appInfo>
       <documentation>
          3.1
       </documentation>
    </annotation>
 
    <annotation>
-      <appinfo>
+      <appInfo>
          <meta.section type="examples"/>
-      </appinfo>
+      </appInfo>
       <documentation>
          &lt;pre&gt;
    &lt;extension
@@ -87,20 +101,19 @@
    </annotation>
 
    <annotation>
-      <appinfo>
+      <appInfo>
          <meta.section type="apiInfo"/>
-      </appinfo>
+      </appInfo>
       <documentation>
          Value of the attribute &lt;b&gt;class&lt;/b&gt; must be a fully qualified name of a Java class that implements the interface &lt;b&gt;org.eclipse.debug.ui.actions.IVariableValueEditor&lt;/b&gt;.
       </documentation>
    </annotation>
 
 
-
    <annotation>
-      <appinfo>
+      <appInfo>
          <meta.section type="copyright"/>
-      </appinfo>
+      </appInfo>
       <documentation>
          Copyright (c) 2004, 2005 IBM Corporation and others.&lt;br&gt;
 
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/VariableValueEditorManager.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/VariableValueEditorManager.java
index 2608bf0..80fa4dd 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/VariableValueEditorManager.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/VariableValueEditorManager.java
@@ -33,6 +33,14 @@
  */
 public class VariableValueEditorManager {
 
+	private static final int MAX_PRIORITY = 10;
+
+	private static final int DEFAULT_PRIORITY = 0;
+
+	private static final String MODEL_ID = "modelId"; //$NON-NLS-1$
+
+	private static final String PRIORITY = "priority";//$NON-NLS-1$
+
 	/**
 	 * Mapping of debug model identifiers to variable value editors.
 	 * The keys in this map are always Strings (model ids).
@@ -101,10 +109,39 @@
 		IExtensionPoint ep = Platform.getExtensionRegistry().getExtensionPoint(DebugUIPlugin.getUniqueIdentifier(), IDebugUIConstants.EXTENSION_POINT_VARIABLE_VALUE_EDITORS);
 		IConfigurationElement[] elements = ep.getConfigurationElements();
 		for (IConfigurationElement element : elements) {
-			String modelId = element.getAttribute("modelId"); //$NON-NLS-1$
+			String modelId = element.getAttribute(MODEL_ID);
 			if (modelId != null) {
-				fEditorMap.put(modelId, element);
+				Object original = fEditorMap.put(modelId, element);
+				// Check if the original contribution had higher priority
+				if (original instanceof IConfigurationElement) {
+					int newPrio = getPriority(element);
+					int oldPrio = getPriority((IConfigurationElement) original);
+					if (oldPrio > newPrio) {
+						// restore
+						fEditorMap.put(modelId, original);
+					}
+				}
 			}
 		}
 	}
+
+	private static int getPriority(IConfigurationElement element) {
+		String attribute = element.getAttribute(PRIORITY);
+		int priority = DEFAULT_PRIORITY;
+		if (attribute != null) {
+			try {
+				priority = Integer.parseInt(attribute);
+			} catch (Exception e) {
+				String error = "Unexpected value: '" + attribute + //$NON-NLS-1$
+						"' for '" + PRIORITY + "' attribute " //$NON-NLS-1$//$NON-NLS-2$
+						+ " in extension point '" + IDebugUIConstants.EXTENSION_POINT_VARIABLE_VALUE_EDITORS //$NON-NLS-1$
+						+ "' was specified by " + element.getNamespaceIdentifier(); //$NON-NLS-1$
+				IllegalArgumentException ie = new IllegalArgumentException(error, e);
+				DebugUIPlugin.log(ie);
+			}
+			priority = Math.max(DEFAULT_PRIORITY, priority);
+			priority = Math.min(MAX_PRIORITY, priority);
+		}
+		return priority;
+	}
 }