Bug 334991 - Variables view: Add history with recent expressions to the
details pane
Change-Id: I7105b8125a10f0ccdfc39c3ba43961874369b797
diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ui/DetailPaneManagerTests.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ui/DetailPaneManagerTests.java
index 3e4855e..51ded11 100644
--- a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ui/DetailPaneManagerTests.java
+++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ui/DetailPaneManagerTests.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2006, 2012 IBM Corporation and others.
+ * Copyright (c) 2006, 2019 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -16,24 +16,21 @@
import java.util.HashSet;
import java.util.Set;
-import junit.framework.Test;
-
+import org.eclipse.debug.internal.ui.views.variables.details.DefaultDetailPane;
+import org.eclipse.debug.internal.ui.views.variables.details.DetailPaneManager;
+import org.eclipse.debug.ui.IDetailPane;
import org.eclipse.jdt.debug.core.IJavaVariable;
import org.eclipse.jdt.debug.testplugin.detailpane.SimpleDetailPane;
import org.eclipse.jdt.debug.testplugin.detailpane.TableDetailPane;
import org.eclipse.jdt.debug.tests.AbstractDebugTest;
-import org.eclipse.test.OrderedTestSuite;
-
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.jface.viewers.StructuredSelection;
-
-import org.eclipse.debug.internal.ui.views.variables.details.DefaultDetailPane;
-import org.eclipse.debug.internal.ui.views.variables.details.DetailPaneManager;
-
-import org.eclipse.debug.ui.IDetailPane;
-
import org.eclipse.jdt.internal.debug.core.logicalstructures.JDIPlaceholderVariable;
import org.eclipse.jdt.internal.debug.core.model.JDIDebugElement;
+import org.eclipse.jdt.internal.debug.ui.variables.JavaVariablesDetailPane;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.test.OrderedTestSuite;
+
+import junit.framework.Test;
/**
* Tests the detail pane functionality by testing <code>DetailPaneManager</code>.
@@ -142,7 +139,7 @@
selection = new StructuredSelection(new IJavaVariable[]{new JDIPlaceholderVariable("test var",null)});
id = fManager.getPreferredPaneFromSelection(selection);
- assertEquals("Incorrect pane ID", DefaultDetailPane.ID, id);
+ assertEquals("Incorrect pane ID", JavaVariablesDetailPane.JAVA_VARIABLE_DETAIL_PANE_VARIABLES, id);
// The factory sets the Table detail pane as the default if the first string is "test pane is default".
selection = new StructuredSelection(new String[]{"test pane is default","example selection"});
@@ -182,7 +179,8 @@
selection = new StructuredSelection(new Object[]{new JDIPlaceholderVariable("test var",null)});
result = fManager.getAvailablePaneIDs(selection);
- assertTrue("Set was incorrect",result.size()==2 && result.contains(DefaultDetailPane.ID) && result.contains(SimpleDetailPane.ID));
+ assertTrue("Set was incorrect", result.size() == 3 && result.contains(DefaultDetailPane.ID) && result.contains(SimpleDetailPane.ID)
+ && result.contains(JavaVariablesDetailPane.JAVA_VARIABLE_DETAIL_PANE_VARIABLES));
// Simple detail pane only available if selection has length of 1, containing a java variable
selection = new StructuredSelection(new Object[]{"String1",new JDIPlaceholderVariable("test var",null),"String3"});
diff --git a/org.eclipse.jdt.debug.ui/META-INF/MANIFEST.MF b/org.eclipse.jdt.debug.ui/META-INF/MANIFEST.MF
index d57c604..b8a192b 100644
--- a/org.eclipse.jdt.debug.ui/META-INF/MANIFEST.MF
+++ b/org.eclipse.jdt.debug.ui/META-INF/MANIFEST.MF
@@ -38,7 +38,7 @@
org.eclipse.core.variables;bundle-version="[3.2.0,4.0.0)",
org.eclipse.core.expressions;bundle-version="[3.4.0,4.0.0)",
org.eclipse.jdt.core;bundle-version="[3.15.0,4.0.0)",
- org.eclipse.debug.ui;bundle-version="[3.13.100,4.0.0)",
+ org.eclipse.debug.ui;bundle-version="[3.13.400,4.0.0)",
org.eclipse.jdt.debug;bundle-version="[3.11.0,4.0.0)",
org.eclipse.jdt.launching;bundle-version="[3.11.0,4.0.0)",
org.eclipse.jdt.ui;bundle-version="[3.15.100,4.0.0)",
diff --git a/org.eclipse.jdt.debug.ui/plugin.properties b/org.eclipse.jdt.debug.ui/plugin.properties
index 5e77de6..3000482 100644
--- a/org.eclipse.jdt.debug.ui/plugin.properties
+++ b/org.eclipse.jdt.debug.ui/plugin.properties
@@ -1,5 +1,5 @@
###############################################################################
-# Copyright (c) 2000, 2018 IBM Corporation and others.
+# Copyright (c) 2000, 2019 IBM Corporation and others.
#
# This program and the accompanying materials
# are made available under the terms of the Eclipse Public License 2.0
@@ -319,3 +319,5 @@
OpenFromClipboardAction.tooltip = Opens a Java element or Java Stack Trace from Clipboard
OpenFromClipboardAction.description = Opens a Java element or a Java stack trace from clipboard
OpenFromClipboardAction.name = Open from Clipboard
+
+VariablesView.name = Variables
\ No newline at end of file
diff --git a/org.eclipse.jdt.debug.ui/plugin.xml b/org.eclipse.jdt.debug.ui/plugin.xml
index 9f68823..2af9c44 100644
--- a/org.eclipse.jdt.debug.ui/plugin.xml
+++ b/org.eclipse.jdt.debug.ui/plugin.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.0"?>
<!--
- Copyright (c) 2005, 2017 IBM Corporation and others.
+ Copyright (c) 2005, 2019 IBM Corporation and others.
This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
@@ -3669,6 +3669,25 @@
</with>
</enablement>
</detailFactories>
+ <detailFactories
+ class="org.eclipse.jdt.internal.debug.ui.variables.JavaVariablesDetailPaneFactory"
+ id="org.eclipse.jdt.debug.ui.variables.detailFactories">
+ <enablement>
+ <with
+ variable="selection">
+ <count
+ value="1">
+ </count>
+ <iterate>
+ <or>
+ <instanceof
+ value="org.eclipse.jdt.debug.core.IJavaVariable">
+ </instanceof>
+ </or>
+ </iterate>
+ </with>
+ </enablement>
+ </detailFactories>
</extension>
<extension
point="org.eclipse.debug.ui.stringVariablePresentations">
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/propertypages/PropertyPageMessages.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/propertypages/PropertyPageMessages.java
index 1667ebc..2c8c3b1 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/propertypages/PropertyPageMessages.java
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/propertypages/PropertyPageMessages.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2003, 2016 IBM Corporation and others.
+ * Copyright (c) 2003, 2019 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -77,6 +77,13 @@
public static String ThreadFilterEditor_2;
public static String InstanceFilterEditor_0;
+ public static String JavaVariableDetailsPane_choosePreviousExpression;
+ public static String JavaVariableDetailsPane_description;
+ public static String JavaVariableDetailsPane_historySeparator;
+ public static String JavaVariableDetailsPane_name;
+ public static String JavaVariableDetailsPane_settings;
+
+
static {
// load message values from bundle file
NLS.initializeMessages(BUNDLE_NAME, PropertyPageMessages.class);
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/propertypages/PropertyPageMessages.properties b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/propertypages/PropertyPageMessages.properties
index be7b996..c982cb4 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/propertypages/PropertyPageMessages.properties
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/propertypages/PropertyPageMessages.properties
@@ -1,5 +1,5 @@
###############################################################################
-# Copyright (c) 2003, 2017 IBM Corporation and others.
+# Copyright (c) 2003, 2019 IBM Corporation and others.
#
# This program and the accompanying materials
# are made available under the terms of the Eclipse Public License 2.0
@@ -47,6 +47,7 @@
JavaBreakpointPage_7=&Suspend thread
JavaBreakpointPage_8=Suspend &VM
JavaBreakpointPage_9=Unable to cancel breakpoint creation
+JavaExceptionBreakpointPage_5=Java Exception Breakpoint
JavaLineBreakpointPage_2=&Line Number:
JavaLineBreakpointPage_3=&Member:
JavaLineBreakpointPage_4=&Method:
@@ -58,10 +59,14 @@
JavaLineBreakpointPage_18=Line Breakpoint
JavaLineBreakpointPage_19=Watchpoint
JavaLineBreakpointPage_20=Method Breakpoint
+JavaVariableDetailsPane_choosePreviousExpression=<Choose a previously entered expression for debugging>
+JavaVariableDetailsPane_description=Java Variable Detail Pane Viewer
+JavaVariableDetailsPane_historySeparator=Global history
+JavaVariableDetailsPane_name=Java Variable Detail Pane Viewer
+JavaVariableDetailsPane_settings=Java Variable Detail Pane Settings
ThreadFilterEditor_1=&Restrict to Selected Thread(s):
ThreadFilterEditor_2=Must select a thread in selected targets
InstanceFilterEditor_0=Restricted to Selected Ob&ject(s)
-JavaExceptionBreakpointPage_5=Java Exception Breakpoint
VMCapabilitiesPropertyPage_0=The selected debug element does not allow access to the underlying virtual machine.
VMCapabilitiesPropertyPage_1=&Virtual Machine:
VMCapabilitiesPropertyPage_3=Supports synthetic method step filter
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/variables/JavaVariablesDetailPane.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/variables/JavaVariablesDetailPane.java
new file mode 100644
index 0000000..ec0a1ea
--- /dev/null
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/variables/JavaVariablesDetailPane.java
@@ -0,0 +1,382 @@
+/*******************************************************************************
+ * Copyright (c) 2019 IBM Corporation 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.debug.ui.variables;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Stack;
+import java.util.regex.Pattern;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.debug.internal.ui.SWTFactory;
+import org.eclipse.debug.internal.ui.views.variables.details.DefaultDetailPane;
+import org.eclipse.debug.ui.IDetailPane3;
+import org.eclipse.jdt.debug.core.IJavaVariable;
+import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin;
+import org.eclipse.jdt.internal.debug.ui.propertypages.PropertyPageMessages;
+import org.eclipse.jface.dialogs.DialogSettings;
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocumentListener;
+import org.eclipse.jface.text.source.SourceViewer;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.ui.IPropertyListener;
+
+/**
+ * Java Variable detail pane.
+ *
+ * @since 3.10
+ */
+public class JavaVariablesDetailPane extends DefaultDetailPane implements IDetailPane3 {
+
+ /**
+ * Identifier for this Java Variable detail pane editor
+ */
+ public static final String JAVA_VARIABLE_DETAIL_PANE_VARIABLES = JDIDebugUIPlugin.getUniqueIdentifier() + ".JAVA_VARIABLE_DETAIL_PANE_VARIABLES"; //$NON-NLS-1$
+ public static final String NAME = PropertyPageMessages.JavaVariableDetailsPane_name;
+ public static final String DESCRIPTION = PropertyPageMessages.JavaVariableDetailsPane_description;
+
+ private boolean fDirty = false;
+ // property listeners
+ private ListenerList<IPropertyListener> fListeners = new ListenerList<>();
+ private IDocumentListener fDocumentListener;
+ private Combo fExpressionHistory;
+ private IDialogSettings fExpressionHistoryDialogSettings;
+ private Map<IJavaVariable, Stack<String>> fLocalExpressionHistory;
+ private int fSeparatorIndex;
+ private static final int MAX_HISTORY_SIZE = 10;
+ private static final String DS_SECTION_EXPRESSION_HISTORY = "expressionHistory"; //$NON-NLS-1$
+ private static final String DS_KEY_HISTORY_ENTRY_COUNT = "expressionHistoryEntryCount"; //$NON-NLS-1$
+ private static final String DS_KEY_HISTORY_ENTRY_PREFIX = "expressionHistoryEntry_"; //$NON-NLS-1$
+ private static final Pattern NEWLINE_PATTERN = Pattern.compile("\r\n|\r|\n"); //$NON-NLS-1$ ;
+
+ private IJavaVariable fVariable;
+ public JavaVariablesDetailPane() {
+ fExpressionHistoryDialogSettings = DialogSettings.getOrCreateSection(JDIDebugUIPlugin.getDefault().getDialogSettings(), DS_SECTION_EXPRESSION_HISTORY);
+ }
+ @Override
+ public void doSave(IProgressMonitor monitor) {
+ if (fVariable != null && isDirty()) {
+ setDirty(false);
+ if (hasExpressionHistory()) {
+ updateExpressionHistories();
+ }
+ }
+ }
+
+ @Override
+ public void doSaveAs() {
+ doSave(null);
+ }
+
+ @Override
+ public Control createControl(Composite parent) {
+ if (fExpressionHistoryDialogSettings != null) {
+ fLocalExpressionHistory = new HashMap<>();
+ fExpressionHistory = SWTFactory.createCombo(parent, SWT.DROP_DOWN | SWT.READ_ONLY, 1, null);
+ fExpressionHistory.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ int historyIndex = fExpressionHistory.getSelectionIndex() - 1;
+ if (historyIndex >= 0 && historyIndex != fSeparatorIndex && getSourceViewer() != null) {
+ getSourceViewer().getDocument().set(getExpressionHistory()[historyIndex]);
+ }
+ }
+ });
+ GridData data = new GridData(GridData.FILL_HORIZONTAL);
+ data.widthHint = 10;
+ fExpressionHistory.setLayoutData(data);
+ fExpressionHistory.setEnabled(false);
+ }
+ Control newControl = super.createControl(parent);
+ SourceViewer viewer = getSourceViewer();
+ fDocumentListener = new IDocumentListener() {
+ @Override
+ public void documentAboutToBeChanged(DocumentEvent event) {
+ }
+
+ @Override
+ public void documentChanged(DocumentEvent event) {
+ setDirty(true);
+ }
+ };
+ viewer.getDocument().addDocumentListener(fDocumentListener);
+ return newControl;
+ }
+
+ /**
+ * Initializes the Expression history drop-down with values.
+ */
+ private void initializeExpressionHistoryDropDown() {
+ fExpressionHistory.setItems(getExpressionHistoryLabels());
+ String userHint = PropertyPageMessages.JavaVariableDetailsPane_choosePreviousExpression;
+ fExpressionHistory.add(userHint, 0);
+ fExpressionHistory.setText(userHint);
+ }
+
+ /**
+ * Returns the Expression history labels for the current variale.
+ *
+ * @return an array of strings containing the Expression history labels
+ */
+ private String[] getExpressionHistoryLabels() {
+ String[] expressions = getExpressionHistory();
+ String[] labels = new String[expressions.length];
+ for (int i = 0; i < expressions.length; i++) {
+ labels[i] = NEWLINE_PATTERN.matcher(expressions[i]).replaceAll(" "); //$NON-NLS-1$
+ }
+ return labels;
+ }
+
+ /**
+ * Returns the Expression history entries for the current variable.
+ *
+ * @return an array of strings containing the history of Expressions
+ */
+ private String[] getExpressionHistory() {
+ fSeparatorIndex = -1;
+
+ // Get global history
+ String[] globalItems = readExpressionHistory(fExpressionHistoryDialogSettings);
+
+ // Get local history
+ Stack<String> localHistory = fLocalExpressionHistory.get(fVariable);
+ if (localHistory == null) {
+ return globalItems;
+ }
+
+ // Create combined history
+ int localHistorySize = Math.min(localHistory.size(), MAX_HISTORY_SIZE);
+ String[] historyItems = new String[localHistorySize + globalItems.length + 1];
+ for (int i = 0; i < localHistorySize; i++) {
+ historyItems[i] = localHistory.get(localHistory.size() - i - 1);
+ }
+ fSeparatorIndex = localHistorySize;
+ historyItems[localHistorySize] = getSeparatorLabel();
+ System.arraycopy(globalItems, 0, historyItems, localHistorySize + 1, globalItems.length);
+ return historyItems;
+ }
+
+ /**
+ * Updates the local and global Expression histories.
+ */
+ private void updateExpressionHistories() {
+ String newItem = getSourceViewer().getDocument().get();
+ if (newItem.length() == 0) {
+ return;
+ }
+
+ // Update local history
+ Stack<String> localHistory = fLocalExpressionHistory.get(fVariable);
+ if (localHistory == null) {
+ localHistory = new Stack<>();
+ fLocalExpressionHistory.put(fVariable, localHistory);
+ }
+
+ localHistory.remove(newItem);
+ localHistory.push(newItem);
+
+ // Update global history
+ String[] globalItems = readExpressionHistory(fExpressionHistoryDialogSettings);
+ if (globalItems.length > 0 && newItem.equals(globalItems[0])) {
+ return;
+ }
+
+ if (globalItems.length == 0) {
+ globalItems = new String[1];
+ } else {
+ String[] tempItems = new String[globalItems.length + 1];
+ System.arraycopy(globalItems, 0, tempItems, 1, globalItems.length);
+ globalItems = tempItems;
+ }
+ globalItems[0] = newItem;
+ storeExpressionHistory(globalItems, fExpressionHistoryDialogSettings);
+ }
+
+ /**
+ * Reads the Expression history from the given dialog settings.
+ *
+ * @param dialogSettings
+ * the dialog settings
+ * @return the Expression history
+ */
+ private static String[] readExpressionHistory(IDialogSettings dialogSettings) {
+ int count = 0;
+ try {
+ count = dialogSettings.getInt(DS_KEY_HISTORY_ENTRY_COUNT);
+ } catch (NumberFormatException ex) {
+ // No history yet
+ }
+ count = Math.min(count, MAX_HISTORY_SIZE);
+ String[] expressions = new String[count];
+ for (int i = 0; i < count; i++) {
+ expressions[i] = dialogSettings.get(DS_KEY_HISTORY_ENTRY_PREFIX + i);
+ }
+ return expressions;
+ }
+
+ /**
+ * Writes the given Expressions into the given dialog settings.
+ *
+ * @param expressions
+ * an array of strings containing the Expressions
+ * @param dialogSettings
+ * the dialog settings
+ */
+ private static void storeExpressionHistory(String[] expressions, IDialogSettings dialogSettings) {
+ int length = Math.min(expressions.length, MAX_HISTORY_SIZE);
+ int count = 0;
+ outer: for (int i = 0; i < length; i++) {
+ for (int j = 0; j < i; j++) {
+ if (expressions[i].equals(expressions[j])) {
+ break outer;
+ }
+ }
+ dialogSettings.put(DS_KEY_HISTORY_ENTRY_PREFIX + count, expressions[i]);
+ count = count + 1;
+ }
+ dialogSettings.put(DS_KEY_HISTORY_ENTRY_COUNT, count);
+ }
+
+ /**
+ * Returns the label for the history separator.
+ *
+ * @return the label for the history separator
+ */
+ private String getSeparatorLabel() {
+ int borderWidth = fExpressionHistory.computeTrim(0, 0, 0, 0).width;
+ Rectangle rect = fExpressionHistory.getBounds();
+ int width = rect.width - borderWidth;
+
+ GC gc = new GC(fExpressionHistory);
+ gc.setFont(fExpressionHistory.getFont());
+
+ int fSeparatorWidth = gc.getAdvanceWidth('-');
+ String separatorLabel = PropertyPageMessages.JavaVariableDetailsPane_historySeparator;
+ int fMessageLength = gc.textExtent(separatorLabel).x;
+
+ gc.dispose();
+
+ StringBuilder dashes = new StringBuilder();
+ int chars = (((width - fMessageLength) / fSeparatorWidth) / 2) - 2;
+ for (int i = 0; i < chars; i++) {
+ dashes.append('-');
+ }
+
+ StringBuilder result = new StringBuilder();
+ result.append(dashes);
+ result.append(" " + separatorLabel + " "); //$NON-NLS-1$//$NON-NLS-2$
+ result.append(dashes);
+ return result.toString().trim();
+ }
+
+ /**
+ * Tells whether this editor shows a Expression history drop-down list.
+ *
+ * @return <code>true</code> if this editor shows a Expression history drop-down list, <code>false</code> otherwise
+ */
+ private boolean hasExpressionHistory() {
+ return fExpressionHistory != null;
+ }
+
+ @Override
+ public boolean isDirty() {
+ return fDirty;
+ }
+
+ private void setDirty(boolean dirty) {
+ fDirty = dirty;
+ }
+ @Override
+ public boolean isSaveAsAllowed() {
+ return false;
+ }
+
+ @Override
+ public boolean isSaveOnCloseNeeded() {
+ return true;
+ }
+
+ @Override
+ public void addPropertyListener(IPropertyListener listener) {
+ fListeners.add(listener);
+
+ }
+
+ @Override
+ public void removePropertyListener(IPropertyListener listener) {
+ fListeners.remove(listener);
+
+ }
+
+ @Override
+ public String getDescription() {
+ return DESCRIPTION;
+ }
+
+ @Override
+ public String getID() {
+ return JAVA_VARIABLE_DETAIL_PANE_VARIABLES;
+ }
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public void display(IStructuredSelection selection) {
+ if (selection != null && selection.getFirstElement() instanceof IJavaVariable) {
+ IJavaVariable variable = (IJavaVariable) (selection.getFirstElement());
+ if (fVariable == null || !fVariable.equals(variable)) {
+ fVariable = variable;
+ fExpressionHistory.setEnabled(true);
+ initializeExpressionHistoryDropDown();
+ }
+ }
+ super.display(selection);
+ }
+
+ /**
+ * Clears the Java variable detail viewer, removes all text.
+ */
+ @Override
+ protected void clearSourceViewer(){
+ fVariable = null;
+ fExpressionHistory.setEnabled(false);
+ super.clearSourceViewer();
+ }
+
+ @Override
+ public void dispose() {
+ fExpressionHistory.dispose();
+ fLocalExpressionHistory.clear();
+ if (fDocumentListener != null && getSourceViewer() != null && getSourceViewer().getDocument() != null) {
+ getSourceViewer().getDocument().removeDocumentListener(fDocumentListener);
+ }
+ fListeners.clear();
+ super.dispose();
+ }
+}
+
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/variables/JavaVariablesDetailPaneFactory.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/variables/JavaVariablesDetailPaneFactory.java
new file mode 100644
index 0000000..c057a6d
--- /dev/null
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/variables/JavaVariablesDetailPaneFactory.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright (c) 2019 IBM Corporation 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.debug.ui.variables;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.debug.ui.IDetailPane;
+import org.eclipse.debug.ui.IDetailPaneFactory;
+import org.eclipse.jdt.debug.core.IJavaVariable;
+import org.eclipse.jdt.internal.debug.ui.propertypages.PropertyPageMessages;
+import org.eclipse.jface.viewers.IStructuredSelection;
+
+/**
+ * Detail pane factory for Java variable.
+ *
+ * @since 3.10
+ */
+public class JavaVariablesDetailPaneFactory implements IDetailPaneFactory {
+
+ /**
+ * Maps pane IDs to names
+ */
+ private Map<String, String> fNameMap;
+
+ @Override
+ public Set<String> getDetailPaneTypes(IStructuredSelection selection) {
+ HashSet<String> set = new HashSet<>();
+ if (selection.size() == 1) {
+ IJavaVariable b = (IJavaVariable) selection.getFirstElement();
+ if (b != null) {
+ set.add(JavaVariablesDetailPane.JAVA_VARIABLE_DETAIL_PANE_VARIABLES);
+ }
+ }
+ return set;
+ }
+
+ @Override
+ public String getDefaultDetailPane(IStructuredSelection selection) {
+ if (selection.size() == 1) {
+ IJavaVariable b = (IJavaVariable) selection.getFirstElement();
+ if (b != null) {
+ return JavaVariablesDetailPane.JAVA_VARIABLE_DETAIL_PANE_VARIABLES;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public IDetailPane createDetailPane(String paneID) {
+ if (JavaVariablesDetailPane.JAVA_VARIABLE_DETAIL_PANE_VARIABLES.equals(paneID)) {
+ return new JavaVariablesDetailPane();
+ }
+ return null;
+ }
+
+ @Override
+ public String getDetailPaneName(String paneID) {
+ return getNameMap().get(paneID);
+ }
+
+ @Override
+ public String getDetailPaneDescription(String paneID) {
+ return getNameMap().get(paneID);
+ }
+
+ private Map<String, String> getNameMap() {
+ if (fNameMap == null) {
+ fNameMap = new HashMap<>();
+ fNameMap.put(JavaVariablesDetailPane.JAVA_VARIABLE_DETAIL_PANE_VARIABLES, PropertyPageMessages.JavaVariableDetailsPane_settings);
+ }
+ return fNameMap;
+ }
+
+}