Bug 487400 - [patch] Enable filtering for classpath tab entries

Change-Id: Ie5fab6e51a8e5dc88c1fe07f5c4ed0edfd03632f
Signed-off-by:  Christian Buck <c.buck@gmx.de>
diff --git a/org.eclipse.jdt.debug.ui/.settings/.api_filters b/org.eclipse.jdt.debug.ui/.settings/.api_filters
index f23a01b..2a6baac 100644
--- a/org.eclipse.jdt.debug.ui/.settings/.api_filters
+++ b/org.eclipse.jdt.debug.ui/.settings/.api_filters
@@ -1,43 +1,35 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>

-<component id="org.eclipse.jdt.debug.ui" version="2">

-    <resource path="ui/org/eclipse/jdt/debug/ui/launchConfigurations/JavaClasspathTab.java" type="org.eclipse.jdt.debug.ui.launchConfigurations.JavaClasspathTab">

-        <filter comment="Known illegal extension" id="576720909">

-            <message_arguments>

-                <message_argument value="AbstractJavaClasspathTab"/>

-                <message_argument value="JavaClasspathTab"/>

-            </message_arguments>

-        </filter>

-    </resource>

-    <resource path="ui/org/eclipse/jdt/internal/debug/ui/classpath/ClasspathEntry.java" type="org.eclipse.jdt.internal.debug.ui.classpath.ClasspathEntry">

-        <filter comment="Known illegal implementation" id="574619656">

-            <message_arguments>

-                <message_argument value="IRuntimeClasspathEntry"/>

-                <message_argument value="ClasspathEntry"/>

-            </message_arguments>

-        </filter>

-    </resource>

-    <resource path="ui/org/eclipse/jdt/internal/debug/ui/classpath/RuntimeClasspathViewer.java" type="org.eclipse.jdt.internal.debug.ui.classpath.RuntimeClasspathViewer">

-        <filter comment="Known illegal extension" id="571473929">

-            <message_arguments>

-                <message_argument value="TreeViewer"/>

-                <message_argument value="RuntimeClasspathViewer"/>

-            </message_arguments>

-        </filter>

-    </resource>

-    <resource path="ui/org/eclipse/jdt/internal/debug/ui/display/DisplayViewerConfiguration.java" type="org.eclipse.jdt.internal.debug.ui.display.DisplayViewerConfiguration">

-        <filter id="571473929">

-            <message_arguments>

-                <message_argument value="JavaSourceViewerConfiguration"/>

-                <message_argument value="DisplayViewerConfiguration"/>

-            </message_arguments>

-        </filter>

-    </resource>

-    <resource path="ui/org/eclipse/jdt/internal/debug/ui/launcher/AppletSelectionDialog.java" type="org.eclipse.jdt.internal.debug.ui.launcher.AppletSelectionDialog$PackageRenderer">

-        <filter comment="known API usage" id="571473929">

-            <message_arguments>

-                <message_argument value="JavaElementLabelProvider"/>

-                <message_argument value="PackageRenderer"/>

-            </message_arguments>

-        </filter>

-    </resource>

-</component>

+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<component id="org.eclipse.jdt.debug.ui" version="2">
+    <resource path="ui/org/eclipse/jdt/debug/ui/launchConfigurations/JavaClasspathTab.java" type="org.eclipse.jdt.debug.ui.launchConfigurations.JavaClasspathTab">
+        <filter comment="Known illegal extension" id="576720909">
+            <message_arguments>
+                <message_argument value="AbstractJavaClasspathTab"/>
+                <message_argument value="JavaClasspathTab"/>
+            </message_arguments>
+        </filter>
+    </resource>
+    <resource path="ui/org/eclipse/jdt/internal/debug/ui/classpath/ClasspathEntry.java" type="org.eclipse.jdt.internal.debug.ui.classpath.ClasspathEntry">
+        <filter comment="Known illegal implementation" id="574619656">
+            <message_arguments>
+                <message_argument value="IRuntimeClasspathEntry"/>
+                <message_argument value="ClasspathEntry"/>
+            </message_arguments>
+        </filter>
+    </resource>
+    <resource path="ui/org/eclipse/jdt/internal/debug/ui/display/DisplayViewerConfiguration.java" type="org.eclipse.jdt.internal.debug.ui.display.DisplayViewerConfiguration">
+        <filter id="571473929">
+            <message_arguments>
+                <message_argument value="JavaSourceViewerConfiguration"/>
+                <message_argument value="DisplayViewerConfiguration"/>
+            </message_arguments>
+        </filter>
+    </resource>
+    <resource path="ui/org/eclipse/jdt/internal/debug/ui/launcher/AppletSelectionDialog.java" type="org.eclipse.jdt.internal.debug.ui.launcher.AppletSelectionDialog$PackageRenderer">
+        <filter comment="known API usage" id="571473929">
+            <message_arguments>
+                <message_argument value="JavaElementLabelProvider"/>
+                <message_argument value="PackageRenderer"/>
+            </message_arguments>
+        </filter>
+    </resource>
+</component>
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/debug/ui/launchConfigurations/JavaClasspathTab.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/debug/ui/launchConfigurations/JavaClasspathTab.java
index 13afaae..19087d3 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/debug/ui/launchConfigurations/JavaClasspathTab.java
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/debug/ui/launchConfigurations/JavaClasspathTab.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2015 IBM Corporation and others.
+ * Copyright (c) 2000, 2017 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -121,13 +121,13 @@
 		
 		fClasspathViewer = new RuntimeClasspathViewer(comp);
 		fClasspathViewer.addEntriesChangedListener(this);
-		fClasspathViewer.getControl().setFont(font);
-		fClasspathViewer.setLabelProvider(new ClasspathLabelProvider());
-		fClasspathViewer.setContentProvider(new ClasspathContentProvider(this));
+		fClasspathViewer.getTreeViewer().getControl().setFont(font);
+		fClasspathViewer.getTreeViewer().setLabelProvider(new ClasspathLabelProvider());
+		fClasspathViewer.getTreeViewer().setContentProvider(new ClasspathContentProvider(this));
 		if (!isShowBootpath()) {
-			fClasspathViewer.addFilter(new BootpathFilter());
+			fClasspathViewer.getTreeViewer().addFilter(new BootpathFilter());
 		}
-	
+
 		Composite pathButtonComp = new Composite(comp, SWT.NONE);
 		GridLayout pathButtonLayout = new GridLayout();
 		pathButtonLayout.marginHeight = 0;
@@ -208,7 +208,7 @@
 	@Override
 	public void initializeFrom(ILaunchConfiguration configuration) {
 		refresh(configuration);
-		fClasspathViewer.expandToLevel(2);
+		fClasspathViewer.getTreeViewer().expandToLevel(2);
 	}
 	
 	/* (non-Javadoc)
@@ -224,7 +224,7 @@
 					return;
 				}
 			}
-			fClasspathViewer.refresh();
+			fClasspathViewer.getTreeViewer().refresh();
 		} catch (CoreException e) {
 		}
 	}
@@ -235,23 +235,8 @@
 	 * @param configuration the configuration
 	 */
 	private void refresh(ILaunchConfiguration configuration) {
-		boolean useDefault = true;
 		setErrorMessage(null);
-		try {
-			useDefault = configuration.getAttribute(IJavaLaunchConfigurationConstants.ATTR_DEFAULT_CLASSPATH, true);
-		} catch (CoreException e) {
-			JDIDebugUIPlugin.log(e);
-		}
-		
-		if (configuration == getLaunchConfiguration()) {
-			// no need to update if an explicit path is being used and this setting
-			// has not changed (and viewing the same config as last time)
-			if (!useDefault) {
-				setDirty(false);
-				return;			
-			}
-		}
-		
+
 		setLaunchConfiguration(configuration);
 		try {
 			createClasspathModel(configuration);
@@ -260,7 +245,7 @@
 		}
 		
 		fClasspathViewer.setLaunchConfiguration(configuration);
-		fClasspathViewer.setInput(fModel);
+		fClasspathViewer.getTreeViewer().setInput(fModel);
 		setDirty(false);
 	}
 	
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/classpath/RuntimeClasspathViewer.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/classpath/RuntimeClasspathViewer.java
index 3299fb8..a50fa2d 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/classpath/RuntimeClasspathViewer.java
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/classpath/RuntimeClasspathViewer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2015 IBM Corporation and others.
+ * Copyright (c) 2000, 2017 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -15,6 +15,8 @@
 import java.util.Iterator;
 import java.util.List;
 
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.ListenerList;
 import org.eclipse.core.runtime.preferences.IEclipsePreferences;
 import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener;
@@ -27,8 +29,8 @@
 import org.eclipse.jdt.internal.debug.ui.launcher.IEntriesChangedListener;
 import org.eclipse.jdt.internal.launching.LaunchingPlugin;
 import org.eclipse.jdt.launching.IRuntimeClasspathEntry;
-import org.eclipse.jface.dialogs.IDialogConstants;
 import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
 import org.eclipse.jface.viewers.IStructuredSelection;
 import org.eclipse.jface.viewers.StructuredSelection;
 import org.eclipse.jface.viewers.TreeViewer;
@@ -37,14 +39,16 @@
 import org.eclipse.swt.events.DisposeListener;
 import org.eclipse.swt.events.KeyAdapter;
 import org.eclipse.swt.events.KeyEvent;
-import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.dialogs.FilteredTree;
+import org.eclipse.ui.dialogs.PatternFilter;
+import org.eclipse.ui.progress.WorkbenchJob;
 
 /**
  * A viewer that displays and manipulates runtime classpath entries.
  */
-public class RuntimeClasspathViewer extends TreeViewer implements IClasspathViewer {
+public class RuntimeClasspathViewer implements IClasspathViewer {
 		
 	/**
 	 * Entry changed listeners
@@ -70,31 +74,86 @@
 
 		}
 	};
+
+	private static class RuntimeClasspathFilteredTree extends FilteredTree {
+
+		private boolean isFiltering;
+
+		private RuntimeClasspathFilteredTree(Composite parent, PatternFilter filter) {
+			super(parent, 0, filter, true);
+		}
+
+		private boolean hasFilterTextEntered() {
+			return isFiltering;
+		}
+
+		/**
+		 * Called by modify listener -> implicit change listener.
+		 */
+		@Override
+		protected void textChanged() {
+
+			super.textChanged();
+
+			final String filterString = getFilterString();
+			if (null != filterString) {
+				// REVIEW: There are several different ways used to check for empty filter texts:
+				// comparing with "", comparing with IInternalDebugCoreConstants.EMPTY_STRING and checking size of trimmed value.
+				isFiltering = !filterString.trim().isEmpty();
+			} else {
+				isFiltering = false;
+			}
+		}
+
+		@Override
+		protected WorkbenchJob doCreateRefreshJob() {
+			final WorkbenchJob job = super.doCreateRefreshJob();
+
+			return new WorkbenchJob("Classpath filter refresh") { //$NON-NLS-1$
+
+				@Override
+				public IStatus runInUIThread(IProgressMonitor monitor) {
+					final IStatus status = job.runInUIThread(monitor);
+
+					if (!isFiltering) {
+						getViewer().expandToLevel(2);
+					}
+
+					return status;
+				}
+			};
+		}
+	}
+
+	private final RuntimeClasspathFilteredTree fTree;
 		
+	public TreeViewer getTreeViewer() {
+		return fTree.getViewer();
+	}
+
 	/**
 	 * Creates a runtime classpath viewer with the given parent.
 	 *
 	 * @param parent the parent control
 	 */
 	public RuntimeClasspathViewer(Composite parent) {
-		super(parent);
 		
-		GridData data = new GridData(GridData.FILL_BOTH);
-		data.widthHint = IDialogConstants.ENTRY_FIELD_WIDTH;
-		data.heightHint = getTree().getItemHeight();
-		getTree().setLayoutData(data);
+		final PatternFilter filter = new PatternFilter();
+		filter.setIncludeLeadingWildcard(true);
+		fTree = new RuntimeClasspathFilteredTree(parent, filter);
 		
-		getTree().addKeyListener(new KeyAdapter() {
+		getTreeViewer().getTree().addKeyListener(new KeyAdapter() {
 			@Override
 			public void keyPressed(KeyEvent event) {
-				if (updateSelection(RuntimeClasspathAction.REMOVE, (IStructuredSelection)getSelection()) && event.character == SWT.DEL && event.stateMask == 0) {
-					List<?> selection= getSelectionFromWidget();
-					getClasspathContentProvider().removeAll(selection);
+				if (updateSelection(RuntimeClasspathAction.REMOVE, (IStructuredSelection) getSelection()) && event.character == SWT.DEL
+						&& event.stateMask == 0) {
+					getClasspathContentProvider().removeAll(((IStructuredSelection) getSelectedEntries()).toList());
 					notifyChanged();
 				}
 			}
 		});
-		getTree().addDisposeListener(new DisposeListener() {
+
+		fTree.addDisposeListener(new DisposeListener() {
 			@Override
 			public void widgetDisposed(DisposeEvent e) {
 				IEclipsePreferences prefs = InstanceScope.INSTANCE.getNode(LaunchingPlugin.ID_PLUGIN);
@@ -190,8 +249,8 @@
 	 * @param configuration the backing {@link ILaunchConfiguration}
 	 */
 	public void setLaunchConfiguration(ILaunchConfiguration configuration) {
-		if (getLabelProvider() != null) {
-			((ClasspathLabelProvider)getLabelProvider()).setLaunchConfiguration(configuration);
+		if (getTreeViewer().getLabelProvider() != null) {
+			((ClasspathLabelProvider) getTreeViewer().getLabelProvider()).setLaunchConfiguration(configuration);
 		}
 	}
 	
@@ -242,11 +301,11 @@
 	 */
 	@Override
 	public Shell getShell() {
-		return getControl().getShell();
+		return getTreeViewer().getControl().getShell();
 	}
 	
 	private ClasspathContentProvider getClasspathContentProvider() {
-		return (ClasspathContentProvider)super.getContentProvider();
+		return (ClasspathContentProvider) getTreeViewer().getContentProvider();
 	}
 
 	/* (non-Javadoc)
@@ -268,8 +327,11 @@
 					}
 				}
 				return selection.size() > 0;
+			case RuntimeClasspathAction.MOVE:
+				if (fTree.hasFilterTextEntered()) {
+					return false;
+				}
 			case RuntimeClasspathAction.REMOVE :
-			case RuntimeClasspathAction.MOVE :
 				selected= selection.iterator();
 				while (selected.hasNext()) {
 					IClasspathEntry entry = selected.next();
@@ -303,4 +365,30 @@
 		
 		return new StructuredSelection(entries);
 	}
+
+	@Override
+	public void addSelectionChangedListener(ISelectionChangedListener listener) {
+		getTreeViewer().addSelectionChangedListener(listener);
+	}
+
+	@Override
+	public ISelection getSelection() {
+		return getTreeViewer().getSelection();
+	}
+
+	@Override
+	public void removeSelectionChangedListener(ISelectionChangedListener listener) {
+		getTreeViewer().removeSelectionChangedListener(listener);
+	}
+
+	@Override
+	public void setSelection(ISelection selection) {
+		getTreeViewer().setSelection(selection);
+	}
+
+	@Override
+	public void refresh(Object entry) {
+		getTreeViewer().refresh();
+	}
+
 }