Bug 577103 - Set color on watch expressions and inspect results for labelled objects

Change-Id: Iab1fbfe4f7f4a8cb70840cc22fc61d9301584ca6
Reviewed-on: https://git.eclipse.org/r/c/jdt/eclipse.jdt.debug/+/187426
Tested-by: JDT Bot <jdt-bot@eclipse.org>
Reviewed-by: Sarika Sinha <sarika.sinha@in.ibm.com>
diff --git a/org.eclipse.jdt.debug.tests/META-INF/MANIFEST.MF b/org.eclipse.jdt.debug.tests/META-INF/MANIFEST.MF
index 0d2ae5f..8319272 100644
--- a/org.eclipse.jdt.debug.tests/META-INF/MANIFEST.MF
+++ b/org.eclipse.jdt.debug.tests/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %pluginName
 Bundle-SymbolicName: org.eclipse.jdt.debug.tests; singleton:=true
-Bundle-Version: 3.11.1500.qualifier
+Bundle-Version: 3.11.1600.qualifier
 Bundle-ClassPath: javadebugtests.jar
 Bundle-Activator: org.eclipse.jdt.debug.testplugin.JavaTestPlugin
 Bundle-Vendor: %providerName
diff --git a/org.eclipse.jdt.debug.tests/pom.xml b/org.eclipse.jdt.debug.tests/pom.xml
index 0ae9d16..4eea521 100644
--- a/org.eclipse.jdt.debug.tests/pom.xml
+++ b/org.eclipse.jdt.debug.tests/pom.xml
@@ -19,7 +19,7 @@
   </parent>
   <groupId>org.eclipse.jdt</groupId>
   <artifactId>org.eclipse.jdt.debug.tests</artifactId>
-  <version>3.11.1500-SNAPSHOT</version>
+  <version>3.11.1600-SNAPSHOT</version>
   <packaging>eclipse-test-plugin</packaging>
   <properties>
     <code.ignoredWarnings>${tests.ignoredWarnings}</code.ignoredWarnings>
diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ui/presentation/ModelPresentationTests.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ui/presentation/ModelPresentationTests.java
index 89f50cd..29dc48d 100644
--- a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ui/presentation/ModelPresentationTests.java
+++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ui/presentation/ModelPresentationTests.java
@@ -13,6 +13,9 @@
  *******************************************************************************/
 package org.eclipse.jdt.debug.tests.ui.presentation;
 
+import java.util.HashMap;
+import java.util.Map;
+
 import org.eclipse.debug.ui.IDebugModelPresentation;
 import org.eclipse.jdt.core.Signature;
 import org.eclipse.jdt.debug.core.IJavaLineBreakpoint;
@@ -23,6 +26,8 @@
 import org.eclipse.jdt.debug.core.IJavaVariable;
 import org.eclipse.jdt.debug.tests.AbstractDebugTest;
 import org.eclipse.jdt.internal.debug.ui.JDIModelPresentation;
+import org.eclipse.jdt.internal.debug.ui.display.JavaInspectExpression;
+import org.eclipse.swt.graphics.Color;
 
 /**
  * Tests for some of the methods of the model presentation
@@ -31,6 +36,7 @@
  */
 public class ModelPresentationTests extends AbstractDebugTest {
 
+	private Map<String, Color> colors = new HashMap<>();
 	/**
 	 * Constructor
 	 */
@@ -38,6 +44,22 @@
 		super("Model Presentation tests");
 	}
 
+	@Override
+	protected void setUp() throws Exception {
+		super.setUp();
+		colors.clear();
+		colors.put("org.eclipse.jdt.debug.ui.LabeledObject", new Color(255, 0, 0));
+	}
+
+	private JDIModelPresentation mock() {
+		return new JDIModelPresentation() {
+			@Override
+			protected Color getColorFromRegistry(String symbolicName) {
+				return colors.get(symbolicName);
+			}
+		};
+	}
+
 	/**
 	 * Tests that the type signature + value signatures do not cause problems when the values are "&lt;unknown&gt;" - this
 	 * case arises when you manually suspend a VM and try to inspect / view object values
@@ -45,7 +67,7 @@
 	 * @throws Exception
 	 */
 	public void testUnknownValueText() throws Exception {
-		JDIModelPresentation pres = new JDIModelPresentation();
+		JDIModelPresentation pres = mock();
 		try {
 			TestIJavaType type = new TestIJavaType("foo", "<unknown>");
 			TestIJavaValue value = new TestIJavaValue(type, "<unknown>", null, "<unknown>", null);
@@ -65,7 +87,7 @@
 	 * @throws Exception
 	 */
 	public void testAllNullValueText() throws Exception {
-		JDIModelPresentation pres = new JDIModelPresentation();
+		JDIModelPresentation pres = mock();
 		try {
 			TestIJavaType type = new TestIJavaType(null, null);
 			TestIJavaValue value = new TestIJavaValue(type, null, null, null, null);
@@ -86,11 +108,9 @@
 	 * @throws Exception
 	 */
 	public void testSimpleStringValueText() throws Exception {
-		JDIModelPresentation pres = new JDIModelPresentation();
+		JDIModelPresentation pres = mock();
 		try {
-			String sig = Signature.createTypeSignature("java.lang.String", false);
-			TestIJavaType type = new TestIJavaType("foobar", sig);
-			TestIJavaValue value = new TestIJavaValue(type, sig, null, "org.test.MyClass", "test Java value");
+			var value = createJavaObject();
 			String val = pres.getValueText(value);
 			assertNotNull("the value should have been computed", val);
 			assertEquals("The value text should be '\"MyClass test Java value\"'", "MyClass test Java value", val);
@@ -106,11 +126,9 @@
 	 * @throws Exception
 	 */
 	public void testResolvedStringValueText() throws Exception {
-		JDIModelPresentation pres = new JDIModelPresentation();
+		JDIModelPresentation pres = mock();
 		try {
-			String sig = Signature.createTypeSignature("java.lang.String", true);
-			TestIJavaType type = new TestIJavaType("foobar", sig);
-			TestIJavaValue value = new TestIJavaValue(type, sig, null, "org.test.MyClass", "test Java value");
+			var value = createJavaObject();
 			String val = pres.getValueText(value);
 			assertNotNull("the value should have been computed", val);
 			assertEquals("The value text should be '\"MyClass test Java value\"'", "MyClass test Java value", val);
@@ -125,12 +143,10 @@
 	 *
 	 * @throws Exception
 	 */
-	public void testResolvedStringValueTextWithLabel() throws Exception {
-		var pres = new JDIModelPresentation();
+	public void testStringValueTextWithLabel() throws Exception {
+		var pres = mock();
 		try {
-			var sig = Signature.createTypeSignature("java.lang.String", true);
-			var type = new TestIJavaType("foobar", sig);
-			var value = new TestIJavaObjectValue(type, sig, null, "org.test.MyClass", "test Java value");
+			var value = createJavaObject();
 			value.setLabel("myLabel");
 			var valTxt = pres.getValueText(value);
 			assertNotNull("the value should have been computed", valTxt);
@@ -141,6 +157,136 @@
 	}
 
 	/**
+	 * Tests getting the value text for a simple String type
+	 *
+	 * @throws Exception
+	 */
+	public void testStringVariableWithValueText() throws Exception {
+		JDIModelPresentation pres = mock();
+		try {
+			var value = createJavaObject();
+			var variable = new TestIJavaVariable("myVariable", value);
+			String val = pres.getText(variable);
+			assertNotNull("the value should have been computed", val);
+			assertEquals("The value text should be '\"myVariable= MyClass test Java value\"'", "myVariable= MyClass test Java value", val);
+			var foreground = pres.getForeground(variable);
+			assertNull("the foreground color should have been null", foreground);
+		} finally {
+			pres.dispose();
+		}
+	}
+
+	/**
+	 * Tests getting the value text for a simple String type with a label
+	 *
+	 * @throws Exception
+	 */
+	public void testStringVariableWithValueTextWithLabel() throws Exception {
+		var pres = mock();
+		try {
+			var value = createJavaObject();
+			value.setLabel("myLabel");
+			var variable = new TestIJavaVariable("myVariable", value);
+			var valTxt = pres.getText(variable);
+			assertNotNull("the value should have been computed", valTxt);
+			assertEquals("The value text should be '\"myVariable= (myLabel) MyClass test Java value\"'", "myVariable= (myLabel) MyClass test Java value", valTxt);
+			var foreground = pres.getForeground(variable);
+			assertNotNull("the foreground should have been computed", foreground);
+		} finally {
+			pres.dispose();
+		}
+	}
+
+	/**
+	 * Tests for handling IWatchExpression
+	 *
+	 * @throws Exception
+	 */
+	public void testWatchExpression() throws Exception {
+		JDIModelPresentation pres = mock();
+		try {
+			var value = createJavaObject();
+			var variable = new TestIWatchExpression("myVariable", value);
+			String val = pres.getText(variable);
+			assertNotNull("the value should have been computed", val);
+			assertEquals("The value text should be '\"\\\"myVariable\\\"= MyClass test Java value\"'", "\"myVariable\"= MyClass test Java value", val);
+			var foreground = pres.getForeground(variable);
+			assertNull("the foreground color should have been null", foreground);
+		} finally {
+			pres.dispose();
+		}
+	}
+
+	/**
+	 * Tests for handling IWatchExpression with a label
+	 *
+	 * @throws Exception
+	 */
+	public void testWatchExpressionWithLabel() throws Exception {
+		var pres = mock();
+		try {
+			var value = createJavaObject();
+			value.setLabel("myLabel");
+			var variable = new TestIWatchExpression("myVariable", value);
+			var valTxt = pres.getText(variable);
+			assertNotNull("the value should have been computed", valTxt);
+			assertEquals("The value text should be '\"\\\"myVariable\\\"= (myLabel) MyClass test Java value\"'", "\"myVariable\"= (myLabel) MyClass test Java value", valTxt);
+			var foreground = pres.getForeground(variable);
+			assertNotNull("the foreground should have been computed", foreground);
+		} finally {
+			pres.dispose();
+		}
+	}
+
+	/**
+	 * Tests for handling JavaInspectExpression
+	 *
+	 * @throws Exception
+	 */
+	public void testJavaInspectExpression() throws Exception {
+		JDIModelPresentation pres = mock();
+		try {
+			var value = createJavaObject();
+			var variable = new JavaInspectExpression("myVariable", value);
+			String val = pres.getText(variable);
+			assertNotNull("the value should have been computed", val);
+			assertEquals("The value text should be '\"\\\"myVariable\\\"= MyClass test Java value\"'", "\"myVariable\"= MyClass test Java value", val);
+			var foreground = pres.getForeground(variable);
+			assertNull("the foreground color should have been null", foreground);
+		} finally {
+			pres.dispose();
+		}
+	}
+
+	/**
+	 * Tests for handling JavaInspectExpression with a label
+	 *
+	 * @throws Exception
+	 */
+	public void testJavaInspectExpressionWithLabel() throws Exception {
+		var pres = mock();
+		try {
+			var value = createJavaObject();
+			value.setLabel("myLabel");
+			var variable = new JavaInspectExpression("myVariable", value);
+			var valTxt = pres.getText(variable);
+			assertNotNull("the value should have been computed", valTxt);
+			assertEquals("The value text should be '\"\\\"myVariable\\\"= (myLabel) MyClass test Java value\"'", "\"myVariable\"= (myLabel) MyClass test Java value", valTxt);
+			var foreground = pres.getForeground(variable);
+			assertNotNull("the foreground should have been computed", foreground);
+		} finally {
+			pres.dispose();
+		}
+	}
+
+	private TestIJavaObjectValue createJavaObject() {
+		var sig = Signature.createTypeSignature("java.lang.String", true);
+		var type = new TestIJavaType("foobar", sig);
+		var value = new TestIJavaObjectValue(type, sig, null, "org.test.MyClass", "test Java value");
+		return value;
+	}
+
+	/**
 	 * Tests a simple array value text
 	 *
 	 * @throws Exception
@@ -169,7 +315,7 @@
 	public void testShowTypeTest() throws Exception {
 		String typeName = "ModelPresentationTests";
 		IJavaLineBreakpoint bp = createLineBreakpoint(19, typeName);
-		JDIModelPresentation pres = new JDIModelPresentation();
+		JDIModelPresentation pres = mock();
 
 		IJavaThread thread = null;
 		try {
diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ui/presentation/TestIJavaVariable.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ui/presentation/TestIJavaVariable.java
new file mode 100644
index 0000000..68218b9
--- /dev/null
+++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ui/presentation/TestIJavaVariable.java
@@ -0,0 +1,154 @@
+/*******************************************************************************
+ * Copyright (c) 2022 Zsombor Gegesy 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:
+ *     Zsombor Gegesy - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.debug.tests.ui.presentation;
+
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.model.IDebugTarget;
+import org.eclipse.debug.core.model.IValue;
+import org.eclipse.jdt.debug.core.IJavaType;
+import org.eclipse.jdt.debug.core.IJavaVariable;
+
+public class TestIJavaVariable implements IJavaVariable {
+
+	private String name;
+	private IValue value;
+
+	public TestIJavaVariable(String name, IValue value) {
+		this.name = name;
+		this.value = value;
+	}
+
+	@Override
+	public IValue getValue() throws DebugException {
+		return value;
+	}
+
+	@Override
+	public String getName() throws DebugException {
+		return name;
+	}
+
+	@Override
+	public String getReferenceTypeName() throws DebugException {
+		return null;
+	}
+
+	@Override
+	public boolean hasValueChanged() throws DebugException {
+		return false;
+	}
+
+	@Override
+	public String getModelIdentifier() {
+		return null;
+	}
+
+	@Override
+	public IDebugTarget getDebugTarget() {
+		return null;
+	}
+
+	@Override
+	public ILaunch getLaunch() {
+		return null;
+	}
+
+	@Override
+	public <T> T getAdapter(Class<T> adapter) {
+		return null;
+	}
+
+	@Override
+	public void setValue(String expression) throws DebugException {
+
+	}
+
+	@Override
+	public void setValue(IValue value) throws DebugException {
+		this.value = value;
+	}
+
+	@Override
+	public boolean supportsValueModification() {
+		return false;
+	}
+
+	@Override
+	public boolean verifyValue(String expression) throws DebugException {
+		return false;
+	}
+
+	@Override
+	public boolean verifyValue(IValue value) throws DebugException {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	@Override
+	public boolean isPublic() throws DebugException {
+		return false;
+	}
+
+	@Override
+	public boolean isPrivate() throws DebugException {
+		return false;
+	}
+
+	@Override
+	public boolean isProtected() throws DebugException {
+		return false;
+	}
+
+	@Override
+	public boolean isPackagePrivate() throws DebugException {
+		return false;
+	}
+
+	@Override
+	public boolean isFinal() throws DebugException {
+		return false;
+	}
+
+	@Override
+	public boolean isStatic() throws DebugException {
+		return false;
+	}
+
+	@Override
+	public boolean isSynthetic() throws DebugException {
+		return false;
+	}
+
+	@Override
+	public String getSignature() throws DebugException {
+		return null;
+	}
+
+	@Override
+	public String getGenericSignature() throws DebugException {
+		return null;
+	}
+
+	@Override
+	public IJavaType getJavaType() throws DebugException {
+		return null;
+	}
+
+	@Override
+	public boolean isLocal() throws DebugException {
+		return false;
+	}
+
+}
diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ui/presentation/TestIWatchExpression.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ui/presentation/TestIWatchExpression.java
new file mode 100644
index 0000000..7c9415a
--- /dev/null
+++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ui/presentation/TestIWatchExpression.java
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * Copyright (c) 2022 Zsombor Gegesy 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:
+ *     Zsombor Gegesy - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.jdt.debug.tests.ui.presentation;
+
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.model.IDebugElement;
+import org.eclipse.debug.core.model.IDebugTarget;
+import org.eclipse.debug.core.model.IValue;
+import org.eclipse.debug.core.model.IWatchExpression;
+
+public class TestIWatchExpression implements IWatchExpression {
+	private IValue value;
+	private String expressionText;
+
+	public TestIWatchExpression(String expressionText, IValue value) {
+		this.expressionText = expressionText;
+		this.value = value;
+	}
+
+	@Override
+	public boolean hasErrors() {
+		return false;
+	}
+
+	@Override
+	public String[] getErrorMessages() {
+		return null;
+	}
+
+	@Override
+	public String getExpressionText() {
+		return expressionText;
+	}
+
+	@Override
+	public IValue getValue() {
+		return value;
+	}
+
+	@Override
+	public IDebugTarget getDebugTarget() {
+		return null;
+	}
+
+	@Override
+	public void dispose() {
+
+	}
+
+	@Override
+	public String getModelIdentifier() {
+		return null;
+	}
+
+	@Override
+	public ILaunch getLaunch() {
+		return null;
+	}
+
+	@Override
+	public <T> T getAdapter(Class<T> adapter) {
+		return null;
+	}
+
+	@Override
+	public void evaluate() {
+
+	}
+
+	@Override
+	public void setExpressionContext(IDebugElement context) {
+
+	}
+
+	@Override
+	public void setExpressionText(String expressionText) {
+
+	}
+
+	@Override
+	public boolean isPending() {
+		return false;
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return true;
+	}
+
+	@Override
+	public void setEnabled(boolean enabled) {
+	}
+
+}
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIModelPresentation.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIModelPresentation.java
index 7bed684..18503d4 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIModelPresentation.java
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIModelPresentation.java
@@ -2119,36 +2119,55 @@
 	 */
 	@Override
 	public Color getForeground(Object element) {
-		if (element instanceof IJavaVariable) {
+		if (element instanceof IJavaObject) {
 			try {
-				var variable = ((IJavaVariable) element).getValue();
-				if (variable instanceof IJavaObject) {
-					var label = ((IJavaObject) variable).getLabel();
-					if (label != null) {
-						return PlatformUI.getWorkbench().getThemeManager().getCurrentTheme().getColorRegistry().get(IJDIPreferencesConstants.PREF_LABELED_OBJECT_COLOR);
-					}
+				var label = ((IJavaObject) element).getLabel();
+				if (label != null) {
+					return getColorFromRegistry(IJDIPreferencesConstants.PREF_LABELED_OBJECT_COLOR);
 				}
+				return null;
 			} catch (DebugException e) {
 			}
 		}
+		if (element instanceof IJavaVariable) {
+			try {
+				return getForeground(((IJavaVariable) element).getValue());
+			} catch (DebugException e) {
+			}
+		}
+		if (element instanceof IWatchExpression) {
+			var watchValue = ((IWatchExpression) element).getValue();
+			return getForeground(watchValue);
+		}
+		if (element instanceof JavaInspectExpression) {
+			var value = ((JavaInspectExpression) element).getValue();
+			return getForeground(value);
+		}
 		if (element instanceof JavaContendedMonitor && ((JavaContendedMonitor) element).getMonitor().isInDeadlock()) {
-			return PlatformUI.getWorkbench().getThemeManager().getCurrentTheme().getColorRegistry().get(IJDIPreferencesConstants.PREF_THREAD_MONITOR_IN_DEADLOCK_COLOR);
+			return getColorFromRegistry(IJDIPreferencesConstants.PREF_THREAD_MONITOR_IN_DEADLOCK_COLOR);
 		}
 		if (element instanceof JavaOwnedMonitor && ((JavaOwnedMonitor)element).getMonitor().isInDeadlock()) {
-			return PlatformUI.getWorkbench().getThemeManager().getCurrentTheme().getColorRegistry().get(IJDIPreferencesConstants.PREF_THREAD_MONITOR_IN_DEADLOCK_COLOR);
+			return getColorFromRegistry(IJDIPreferencesConstants.PREF_THREAD_MONITOR_IN_DEADLOCK_COLOR);
 		}
 		if (element instanceof JavaWaitingThread && ((JavaWaitingThread)element).getThread().isInDeadlock()) {
-			return PlatformUI.getWorkbench().getThemeManager().getCurrentTheme().getColorRegistry().get(IJDIPreferencesConstants.PREF_THREAD_MONITOR_IN_DEADLOCK_COLOR);
+			return getColorFromRegistry(IJDIPreferencesConstants.PREF_THREAD_MONITOR_IN_DEADLOCK_COLOR);
 		}
 		if (element instanceof JavaOwningThread && ((JavaOwningThread)element).getThread().isInDeadlock()) {
-			return PlatformUI.getWorkbench().getThemeManager().getCurrentTheme().getColorRegistry().get(IJDIPreferencesConstants.PREF_THREAD_MONITOR_IN_DEADLOCK_COLOR);
+			return getColorFromRegistry(IJDIPreferencesConstants.PREF_THREAD_MONITOR_IN_DEADLOCK_COLOR);
 		}
 		if (element instanceof IJavaThread && ThreadMonitorManager.getDefault().isInDeadlock((IJavaThread)element)) {
-			return PlatformUI.getWorkbench().getThemeManager().getCurrentTheme().getColorRegistry().get(IJDIPreferencesConstants.PREF_THREAD_MONITOR_IN_DEADLOCK_COLOR);
+			return getColorFromRegistry(IJDIPreferencesConstants.PREF_THREAD_MONITOR_IN_DEADLOCK_COLOR);
 		}
 		return null;
 	}
 
+	/**
+	 * Visible for testing.
+	 */
+	protected Color getColorFromRegistry(String symbolicName) {
+		return PlatformUI.getWorkbench().getThemeManager().getCurrentTheme().getColorRegistry().get(symbolicName);
+	}
+
 	/* (non-Javadoc)
 	 * @see org.eclipse.jface.viewers.IColorProvider#getBackground(java.lang.Object)
 	 */