Added TreeModelViewerFilter to make filtering more efficient.
diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/FilterTests.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/FilterTests.java
index f7401f9..46eb8e7 100644
--- a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/FilterTests.java
+++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/FilterTests.java
@@ -21,7 +21,10 @@
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer;
import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewerFilter;
+import org.eclipse.jface.viewers.ITreeSelection;
import org.eclipse.jface.viewers.TreePath;
+import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.swt.layout.FillLayout;
@@ -84,6 +87,11 @@
}
}
+ protected IInternalTreeModelViewer getInternalViewer() {
+ return (IInternalTreeModelViewer)fViewer;
+ }
+
+
class TestViewerFilter extends ViewerFilter {
Pattern fPattern;
@@ -101,21 +109,70 @@
return true;
}
}
+
+ class TestTMVFilter extends TreeModelViewerFilter {
+ Pattern fPattern;
+ Object fParentElement;
+ TestTMVFilter(String pattern, Object parentElement) {
+ fPattern = Pattern.compile(pattern);
+ fParentElement = parentElement;
+ }
+
+ public boolean isApplicable(ITreeModelViewer viewer, Object parentElement) {
+ if (fParentElement != null) {
+ return fParentElement.equals(parentElement);
+ }
+
+ return true;
+ }
+
+ public boolean select(Viewer viewer, Object parentElement, Object element) {
+ if (element instanceof TestElement) {
+ TestElement te = (TestElement)element;
+ return !fPattern.matcher(te.getLabel()).find();
+ }
+
+ return true;
+ }
+ }
public void testSimpleSingleLevel() throws InterruptedException {
- // Create the model with test data
TestModel model = TestModel.simpleSingleLevel();
+ doTestSimpleLevel(model, new ViewerFilter[] { new TestViewerFilter("2") });
+ }
+
+ public void testSimpleSingleLevelWithTMVFilter() throws InterruptedException {
+ TestModel model = TestModel.simpleSingleLevel();
+ doTestSimpleLevel(model, new ViewerFilter[] { new TestTMVFilter("2", model.getRootElement()) });
+ }
+
+ public void testSimpleSingleLevelWithMixedFilters() throws InterruptedException {
+ TestModel model = TestModel.simpleSingleLevel();
+ doTestSimpleLevel(model, new ViewerFilter[] { new TestTMVFilter("2", model.getRootElement()), new TestViewerFilter("1") });
+ }
+ public void testSimpleMultiLevel() throws InterruptedException {
+ TestModel model = TestModel.simpleMultiLevel();
+ doTestSimpleLevel(model, new ViewerFilter[] { new TestViewerFilter(".1"), new TestViewerFilter(".2") });
+ }
+
+ public void testSimpleMultiLevelWithTMVFilter() throws InterruptedException {
+ TestModel model = TestModel.simpleMultiLevel();
+ doTestSimpleLevel(model, new ViewerFilter[] { new TestTMVFilter(".1", null), new TestTMVFilter(".2", null) });
+ }
+
+ public void testSimpleMultiLevelWithMixedFilters() throws InterruptedException {
+ TestModel model = TestModel.simpleMultiLevel();
+ doTestSimpleLevel(model, new ViewerFilter[] { new TestViewerFilter(".1"), new TestTMVFilter(".2", null) });
+ }
+
+ private void doTestSimpleLevel(TestModel model, ViewerFilter[] filters) throws InterruptedException {
+
// Make sure that all elements are expanded
fViewer.setAutoExpandLevel(-1);
- // Create filter for element 2
- ViewerFilter[] filters = new ViewerFilter[] { new TestViewerFilter("2") };
fViewer.setFilters(filters);
- // Create the agent which forces the tree to populate
- //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
-
// Create the listener which determines when the view is finished updating.
// fListener.reset(TreePath.EMPTY, model.getRootElement(), filters, -1, false, false);
fListener.reset(TreePath.EMPTY, model.getRootElement(), filters, -1, true, true);
@@ -128,32 +185,21 @@
model.validateData(fViewer, TreePath.EMPTY, false, filters);
}
-
- public void testSimpleMultiLevel() throws InterruptedException {
- //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
-
- TestModel model = TestModel.simpleMultiLevel();
- fViewer.setAutoExpandLevel(-1);
-
- // Create filter for elements ending with numbers 0-4.
- ViewerFilter[] filters = new ViewerFilter[] { new TestViewerFilter(".2") };
- fViewer.setFilters(filters);
-
- //fListener.reset(TreePath.EMPTY, model.getRootElement(), filters, -1, false, false);
- fListener.reset(TreePath.EMPTY, model.getRootElement(), filters, -1, true, true);
- fViewer.setInput(model.getRootElement());
- while (!fListener.isFinished()) if (!fDisplay.readAndDispatch ()) Thread.sleep(0);
-
- model.validateData(fViewer, TreePath.EMPTY, false, filters);
+
+ public void testLargeSingleLevel() throws InterruptedException {
+ doTestLargeSingleLevel(new ViewerFilter[] { new TestViewerFilter("2") });
}
- public void testLargeSingleLevel() throws InterruptedException {
+ public void testLargeSingleLevelWithTMVFilter() throws InterruptedException {
+ doTestLargeSingleLevel(new ViewerFilter[] { new TestTMVFilter("2", null) });
+ }
+
+ private void doTestLargeSingleLevel(ViewerFilter[] filters) throws InterruptedException {
TestModel model = new TestModel();
model.setRoot( new TestElement(model, "root", new TestElement[0] ) );
- model.setElementChildren(TreePath.EMPTY, TestModel.makeSingleLevelModelElements(model, 300, "model."));
+ model.setElementChildren(TreePath.EMPTY, TestModel.makeSingleLevelModelElements(model, 3000, "model."));
- // Create filter for element 2
- ViewerFilter[] filters = new ViewerFilter[] { new TestViewerFilter("2") };
+ // Set filters
fViewer.setFilters(filters);
fListener.setFailOnRedundantUpdates(false);
@@ -165,18 +211,31 @@
while (!fListener.isFinished(ALL_UPDATES_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0);
}
+
/**
* Replace an element that is not visible but filtered out. With an element that is NOT filtered out.
* Fire REPLACE delta.
*/
public void testReplacedUnrealizedFilteredElement() throws InterruptedException {
+ doTestReplacedUnrealizedFilteredElement(new ViewerFilter[] { new TestViewerFilter("2") });
+ }
+
+
+ /**
+ * Replace an element that is not visible but filtered out. With an element that is NOT filtered out.
+ * Fire REPLACE delta.
+ */
+ public void testReplacedUnrealizedFilteredElementWithTMVFilter() throws InterruptedException {
+ doTestReplacedUnrealizedFilteredElement(new ViewerFilter[] { new TestTMVFilter("2", null) });
+ }
+
+ private void doTestReplacedUnrealizedFilteredElement(ViewerFilter[] filters) throws InterruptedException {
+
// Populate a view with a large model (only first 100 elements will be visible in virtual viewer).
TestModel model = new TestModel();
model.setRoot( new TestElement(model, "root", new TestElement[0] ) );
model.setElementChildren(TreePath.EMPTY, TestModel.makeSingleLevelModelElements(model, 300, "model."));
- // Create filter for element 2
- ViewerFilter[] filters = new ViewerFilter[] { new TestViewerFilter("2") };
fViewer.setFilters(filters);
fListener.setFailOnRedundantUpdates(false);
@@ -197,7 +256,7 @@
// Reposition the viewer to make element 100 the top element, making the replaced element visible.
fListener.reset();
- ((IInternalTreeModelViewer) fViewer).reveal(TreePath.EMPTY, 100);
+ ((IInternalTreeModelViewer) fViewer).reveal(TreePath.EMPTY, 150);
while (!fListener.isFinished(ALL_UPDATES_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0);
// Verify that the replaced element is in viewer now (i.e. it's not filtered out.
@@ -205,19 +264,25 @@
Assert.assertTrue(replacedElementPaths.length != 0);
}
-
+
+ public void testRefreshUnrealizedFilteredElement() throws InterruptedException {
+ doTestRefreshUnrealizedFilteredElement(new ViewerFilter[] { new TestViewerFilter("2") });
+ }
+
+ public void testRefreshUnrealizedFilteredElementWithTMVFilter() throws InterruptedException {
+ doTestRefreshUnrealizedFilteredElement(new ViewerFilter[] { new TestTMVFilter("2", null) });
+ }
+
/**
* Replace an element that is not visible but filtered out. With an element that is NOT filtered out.
* Fire CONTENT delta on parent.
*/
- public void testRefreshUnrealizedFilteredElement() throws InterruptedException {
+ private void doTestRefreshUnrealizedFilteredElement(ViewerFilter[] filters) throws InterruptedException {
// Populate a view with a large model (only first 100 elements will be visible in virtual viewer).
TestModel model = new TestModel();
model.setRoot( new TestElement(model, "root", new TestElement[0] ) );
model.setElementChildren(TreePath.EMPTY, TestModel.makeSingleLevelModelElements(model, 300, "model."));
- // Create filter for element 2
- ViewerFilter[] filters = new ViewerFilter[] { new TestViewerFilter("2") };
fViewer.setFilters(filters);
fListener.setFailOnRedundantUpdates(false);
@@ -238,11 +303,130 @@
// Reposition the viewer to make element 100 the top element, making the replaced element visible.
fListener.reset();
- ((IInternalTreeModelViewer) fViewer).reveal(TreePath.EMPTY, 100);
+ ((IInternalTreeModelViewer) fViewer).reveal(TreePath.EMPTY, 150);
while (!fListener.isFinished(ALL_UPDATES_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0);
// Verify that the replaced element is in viewer now (i.e. it's not filtered out.
TreePath[] replacedElementPaths = fViewer.getElementPaths(replacedElement);
Assert.assertTrue(replacedElementPaths.length != 0);
}
+
+ public void testRefreshToUnfilterElements() throws InterruptedException {
+ doTestRefreshToUnfilterElements(new ViewerFilter[] { new TestViewerFilter(".1"), new TestViewerFilter(".2") });
+ }
+
+ public void testRefreshToUnfilterElementsWithTMVFilter() throws InterruptedException {
+ doTestRefreshToUnfilterElements(new ViewerFilter[] { new TestTMVFilter(".1", null), new TestTMVFilter(".2", null) });
+ }
+
+ public void testRefreshToUnfilterElementsWithMixedFilters() throws InterruptedException {
+ doTestRefreshToUnfilterElements(new ViewerFilter[] { new TestViewerFilter(".1"), new TestTMVFilter(".2", null) });
+ }
+
+ /**
+ * Replace an element that is not visible but filtered out. With an element that is NOT filtered out.
+ * Fire CONTENT delta on parent.
+ */
+ private void doTestRefreshToUnfilterElements(ViewerFilter[] filters) throws InterruptedException {
+ // Populate a view with a large model (only first 100 elements will be visible in virtual viewer).
+ TestModel model = TestModel.simpleMultiLevel();
+
+ fViewer.setFilters(filters);
+
+ fListener.setFailOnRedundantUpdates(false);
+ fListener.reset();
+
+ // Make sure that all elements are expanded
+ fViewer.setAutoExpandLevel(-1);
+
+ // Populate the view (all elements containing a "2" will be filtered out.
+ fViewer.setInput(model.getRootElement());
+
+ while (!fListener.isFinished(ALL_UPDATES_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0);
+
+ // Turn off filters and refresh.
+ filters = new ViewerFilter[0];
+ fViewer.setFilters(filters);
+ fListener.reset();
+ model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT));
+ while (!fListener.isFinished(ALL_UPDATES_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0);
+
+ model.validateData(fViewer, TreePath.EMPTY, false, filters);
+ }
+
+ public void testPreserveExpandedOnMultLevelContent() throws InterruptedException {
+ //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
+ TestModel model = StateTests.alternatingSubsreesModel(6);
+
+ // NOTE: WE ARE NOT EXPANDING ANY CHILDREN
+
+ // Create the listener, only check the first level
+ fListener.reset(TreePath.EMPTY, model.getRootElement(), 1, true, false);
+
+ // Set the input into the view and update the view.
+ fViewer.setInput(model.getRootElement());
+ while (!fListener.isFinished()) if (!fDisplay.readAndDispatch ()) Thread.sleep(0);
+ model.validateData(fViewer, TreePath.EMPTY, true);
+
+ StateTests.expandAlternateElements(fListener, model, true);
+
+ // Set a selection in view
+ // Set a selection in view
+ TreeSelection originalSelection = new TreeSelection(
+ new TreePath[] { model.findElement("5"), model.findElement("5.1"), model.findElement("6") });
+ fViewer.setSelection(originalSelection);
+ Assert.assertTrue( StateTests.areTreeSelectionsEqual(originalSelection, (ITreeSelection)fViewer.getSelection()) );
+
+ // Set a filter to remove element "1"
+ ViewerFilter[] filters = new ViewerFilter[] { new TestViewerFilter("^1$") };
+ fViewer.setFilters(filters);
+
+ // Note: Re-expanding nodes causes redundant updates.
+ fListener.reset(false, false);
+ fListener.addUpdates(getInternalViewer(), TreePath.EMPTY, model.getRootElement(), filters, -1, ALL_UPDATES_COMPLETE);
+
+ // Post the refresh delta
+ model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT));
+ while (!fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE))
+ if (!fDisplay.readAndDispatch ()) Thread.sleep(0);
+
+ // Validate data
+ model.validateData(fViewer, TreePath.EMPTY, true, filters);
+ Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("2")) == false);
+ Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("3")) == true);
+ Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("3.1")) == true);
+ Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("4")) == false);
+ Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("5")) == true);
+ Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("5.1")) == true);
+ Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("6")) == false);
+ Assert.assertTrue( StateTests.areTreeSelectionsEqual(originalSelection, (ITreeSelection)fViewer.getSelection()) );
+
+ // Note: in past it was observed sub-optimal coalescing in this test due
+ // to scattered update requests from viewer.
+ Assert.assertTrue( fListener.checkCoalesced(TreePath.EMPTY, 0, 6) );
+
+ // Clear the filter, to re-add the element
+ filters = new ViewerFilter[0];
+ fViewer.setFilters(filters);
+
+ // Refresh again to get the filtered element back
+ fListener.reset();
+ fListener.addUpdates(getInternalViewer(), TreePath.EMPTY, model.getRootElement(), filters, -1, ALL_UPDATES_COMPLETE);
+ model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT));
+ while (!fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE))
+ if (!fDisplay.readAndDispatch ()) Thread.sleep(0);
+
+ // Validate data
+ model.validateData(fViewer, TreePath.EMPTY, true, filters);
+ Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("2")) == false);
+ Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("3")) == true);
+ Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("3.1")) == true);
+ Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("4")) == false);
+ Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("5")) == true);
+ Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("5.1")) == true);
+ Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("6")) == false);
+ Assert.assertTrue( StateTests.areTreeSelectionsEqual(originalSelection, (ITreeSelection)fViewer.getSelection()) );
+
+ }
+
}
diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerFilterTests.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerFilterTests.java
index 13ffecd..853d2e7 100644
--- a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerFilterTests.java
+++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerFilterTests.java
@@ -27,6 +27,6 @@
}
protected IInternalTreeModelViewer createViewer(Display display, Shell shell) {
- return new TreeModelViewer(fShell, SWT.VIRTUAL, new PresentationContext("TestViewer"));
+ return new TreeModelViewer(fShell, SWT.VIRTUAL | SWT.MULTI, new PresentationContext("TestViewer"));
}
}
diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/PerformanceTests.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/PerformanceTests.java
index 90d9126..2a5b067 100644
--- a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/PerformanceTests.java
+++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/PerformanceTests.java
@@ -20,6 +20,8 @@
import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeSelection;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
@@ -260,5 +262,61 @@
}
}
+
+ public void testRefreshListFiltered() throws InterruptedException {
+ TestModel model = new TestModel();
+ model.setRoot( new TestElement(model, "root", new TestElement[0] ) );
+ int numElements = (int)Math.pow(2, getTestModelDepth());
+ model.setElementChildren(TreePath.EMPTY, TestModel.makeSingleLevelModelElements(model, 1000, "model."));
+
+ fViewer.setAutoExpandLevel(-1);
+
+ // Create the listener
+ fListener.reset(TreePath.EMPTY, model.getRootElement(), -1, true, false);
+
+ fViewer.addFilter(new ViewerFilter() {
+ public boolean select(Viewer viewer, Object parentElement, Object element) {
+ if (element instanceof TestElement) {
+ String id = ((TestElement)element).getID();
+ if (id.startsWith("model.")) {
+ id = id.substring("model.".length());
+ }
+ if (id.length() >= 2 && (id.charAt(1) == '1' || id.charAt(1) == '3' || id.charAt(1) == '5' || id.charAt(1) == '7' || id.charAt(1) == '9')) {
+ return false;
+ }
+ }
+ return true;
+ }
+ });
+
+ // Set the input into the view and update the view.
+ fViewer.setInput(model.getRootElement());
+ while (!fListener.isFinished()) if (!fDisplay.readAndDispatch ()) Thread.sleep(0);
+ model.validateData(fViewer, TreePath.EMPTY);
+
+ Performance perf = Performance.getDefault();
+ PerformanceMeter meter = perf.createPerformanceMeter(perf.getDefaultScenarioId(this));
+ try {
+ for (int i = 0; i < 100; i++) {
+ // Update the model
+ model.setAllAppendix(" - pass " + i);
+
+ TestElement element = model.getRootElement();
+ fListener.reset(TreePath.EMPTY, element, -1, false, false);
+
+ meter.start();
+ model.postDelta(new ModelDelta(element, IModelDelta.CONTENT));
+ while (!fListener.isFinished(ALL_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE))
+ if (!fDisplay.readAndDispatch ()) Thread.sleep(0);
+ meter.stop();
+ System.gc();
+ }
+
+ meter.commit();
+ perf.assertPerformance(meter);
+ } finally {
+ meter.dispose();
+ }
+ }
}
diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/StateTests.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/StateTests.java
index 77d0a12..3ad16f9 100644
--- a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/StateTests.java
+++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/StateTests.java
@@ -189,7 +189,7 @@
* (size).1
* (size).1.1
*/
- private TestModel alternatingSubsreesModel(int size) {
+ static TestModel alternatingSubsreesModel(int size) {
TestModel model = new TestModel();
TestElement[] elements = new TestElement[size];
@@ -207,7 +207,7 @@
return model;
}
- private boolean areTreeSelectionsEqual(ITreeSelection sel1, ITreeSelection sel2) {
+ static boolean areTreeSelectionsEqual(ITreeSelection sel1, ITreeSelection sel2) {
Set sel1Set = new HashSet();
sel1Set.addAll( Arrays.asList(sel1.getPaths()) );
@@ -217,9 +217,9 @@
return sel1Set.equals(sel2Set);
}
- private void expandAlternateElements(TestModel model, boolean waitForAllUpdates) throws InterruptedException {
- fListener.reset();
- fListener.setFailOnRedundantUpdates(false);
+ static void expandAlternateElements(TestModelUpdatesListener listener, TestModel model, boolean waitForAllUpdates) throws InterruptedException {
+ listener.reset();
+ listener.setFailOnRedundantUpdates(false);
TestElement rootElement = model.getRootElement();
TestElement[] children = rootElement.getChildren();
@@ -237,7 +237,7 @@
int index = i;
while (element.getChildren().length != 0) {
TreePath elementPath = model.findElement(element.getLabel());
- fListener.addUpdates(
+ listener.addUpdates(
elementPath, element, 1,
CHILD_COUNT_UPDATES | (waitForAllUpdates ? CHILDREN_UPDATES : 0) );
delta = delta.addNode(element, index, IModelDelta.EXPAND, element.getChildren().length);
@@ -247,8 +247,8 @@
}
model.postDelta(rootDelta);
- while (!fListener.isFinished(CONTENT_SEQUENCE_COMPLETE | MODEL_CHANGED_COMPLETE))
- if (!fDisplay.readAndDispatch ()) Thread.sleep(0);
+ while (!listener.isFinished(CONTENT_SEQUENCE_COMPLETE | MODEL_CHANGED_COMPLETE))
+ if (!Display.getDefault().readAndDispatch ()) Thread.sleep(0);
}
public void testPreserveExpandedOnRemove() throws InterruptedException {
@@ -265,7 +265,7 @@
while (!fListener.isFinished()) if (!fDisplay.readAndDispatch ()) Thread.sleep(0);
model.validateData(fViewer, TreePath.EMPTY, true);
- expandAlternateElements(model, true);
+ expandAlternateElements(fListener, model, true);
// Set a selection in view
TreeSelection originalSelection = new TreeSelection(model.findElement("5.1"));
@@ -306,7 +306,7 @@
while (!fListener.isFinished()) if (!fDisplay.readAndDispatch ()) Thread.sleep(0);
model.validateData(fViewer, TreePath.EMPTY, true);
- expandAlternateElements(model, true);
+ expandAlternateElements(fListener, model, true);
// Set a selection in view
TreeSelection originalSelection = new TreeSelection(model.findElement("5.1"));
@@ -352,7 +352,7 @@
while (!fListener.isFinished()) if (!fDisplay.readAndDispatch ()) Thread.sleep(0);
model.validateData(fViewer, TreePath.EMPTY, true);
- expandAlternateElements(model, true);
+ expandAlternateElements(fListener, model, true);
// Set a selection in view
// Set a selection in view
@@ -460,7 +460,7 @@
while (!fListener.isFinished()) if (!fDisplay.readAndDispatch ()) Thread.sleep(0);
model.validateData(fViewer, TreePath.EMPTY, true);
- expandAlternateElements(model, true);
+ expandAlternateElements(fListener, model, true);
// Set a selection in view
// TreeSelection originalSelection = new TreeSelection(
@@ -530,7 +530,7 @@
while (!fListener.isFinished(CONTENT_SEQUENCE_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0);
// model.validateData(fViewer, TreePath.EMPTY, true);
- expandAlternateElements(model, false);
+ expandAlternateElements(fListener, model, false);
// Set a selection in view
TreeSelection originalSelection = new TreeSelection(model.findElement("5.1.1"));
@@ -739,7 +739,7 @@
model.validateData(fViewer, TreePath.EMPTY, true);
// Expand some, but not all elements
- expandAlternateElements(model, true);
+ expandAlternateElements(fListener, model, true);
// Set a selection in view
fViewer.setSelection(new TreeSelection(new TreePath[] { model.findElement("5.1"), model.findElement("5.1.1"), model.findElement("6.1.1") } ));
@@ -838,7 +838,7 @@
model.validateData(fViewer, TreePath.EMPTY, true);
// Expand some, but not all elements
- expandAlternateElements(model, true);
+ expandAlternateElements(fListener, model, true);
// Set a selection in view
fViewer.setSelection(new TreeSelection(new TreePath[] { model.findElement("5.1"), model.findElement("5.1.1"), model.findElement("6.1.1") } ));
@@ -880,7 +880,7 @@
model.validateData(fViewer, TreePath.EMPTY, true);
// Expand some, but not all elements
- expandAlternateElements(model, true);
+ expandAlternateElements(fListener, model, true);
// Set a selection in view
fViewer.setSelection(new TreeSelection(new TreePath[] { model.findElement("5.1"), model.findElement("5.1.1"), model.findElement("6.1.1") } ));
@@ -924,7 +924,7 @@
fViewer.setInput(model.getRootElement());
while (!fListener.isFinished(CONTENT_SEQUENCE_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0);
- expandAlternateElements(model, false);
+ expandAlternateElements(fListener, model, false);
// Set a selection in view
TreeSelection originalSelection = new TreeSelection(model.findElement("5.1.1"));
@@ -981,7 +981,7 @@
fViewer.setInput(model.getRootElement());
while (!fListener.isFinished(CONTENT_SEQUENCE_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0);
- expandAlternateElements(model, false);
+ expandAlternateElements(fListener, model, false);
// Set a selection in view
TreeSelection originalSelection = new TreeSelection(model.findElement("5.1.1"));
diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/TestModelUpdatesListener.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/TestModelUpdatesListener.java
index 8b540b1..40dde84 100644
--- a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/TestModelUpdatesListener.java
+++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/TestModelUpdatesListener.java
@@ -431,9 +431,9 @@
}
public boolean isFinished(int flags) {
- if (isTimedOut()) {
- throw new RuntimeException("Timed Out: " + toString(flags));
- }
+// if (isTimedOut()) {
+// throw new RuntimeException("Timed Out: " + toString(flags));
+// }
if (fFailExpectation != null) {
throw fFailExpectation;
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 82ba80b..a3cba9b 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
@@ -25,6 +25,7 @@
import org.eclipe.debug.tests.viewer.model.PresentationContextTests;
import org.eclipe.debug.tests.viewer.model.VirtualViewerContentTests;
import org.eclipe.debug.tests.viewer.model.VirtualViewerDeltaTests;
+import org.eclipe.debug.tests.viewer.model.VirtualViewerFilterTests;
import org.eclipe.debug.tests.viewer.model.VirtualViewerLazyModeTests;
import org.eclipe.debug.tests.viewer.model.VirtualViewerSelectionTests;
import org.eclipe.debug.tests.viewer.model.VirtualViewerStateTests;
@@ -62,6 +63,7 @@
addTest(new TestSuite(VirtualViewerSelectionTests.class));
addTest(new TestSuite(VirtualViewerStateTests.class));
addTest(new TestSuite(VirtualViewerUpdateTests.class));
+ addTest(new TestSuite(VirtualViewerFilterTests.class));
// Viewer neutral tests
addTest(new TestSuite(FilterTransformTests.class));
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/LocalSuite.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/LocalSuite.java
index 55acd57..6098eb8 100644
--- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/LocalSuite.java
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/LocalSuite.java
@@ -17,6 +17,7 @@
import org.eclipe.debug.tests.viewer.model.JFaceViewerCheckTests;
import org.eclipe.debug.tests.viewer.model.JFaceViewerContentTests;
import org.eclipe.debug.tests.viewer.model.JFaceViewerDeltaTests;
+import org.eclipe.debug.tests.viewer.model.JFaceViewerFilterTests;
import org.eclipe.debug.tests.viewer.model.JFaceViewerLazyTests;
import org.eclipe.debug.tests.viewer.model.JFaceViewerSelectionTests;
import org.eclipe.debug.tests.viewer.model.JFaceViewerStateTests;
@@ -53,6 +54,7 @@
addTest(new TestSuite(JFaceViewerUpdateTests.class));
addTest(new TestSuite(JFaceViewerLazyTests.class));
addTest(new TestSuite(JFaceViewerTopIndexTests.class));
+ addTest(new TestSuite(JFaceViewerFilterTests.class));
addTest(new TestSuite(ColumnPresentationTests.class));
}
}
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ChildrenCountUpdate.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ChildrenCountUpdate.java
index 1c2a0e6..5616975 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ChildrenCountUpdate.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ChildrenCountUpdate.java
@@ -40,7 +40,13 @@
* children update is performed on child elements to filter them as part of the
* child count calculation.
*/
- private boolean fShouldFilter;
+ private boolean fShouldFilter = false;
+
+ /**
+ * Children indexes which are currently filtered. When updating child count, also need
+ * to verify that currently filtered children are still filtered.
+ */
+ private int[] fFilteredChildren = null;
/**
* Children update used to filter children.
@@ -57,7 +63,8 @@
*/
public ChildrenCountUpdate(TreeModelContentProvider provider, Object viewerInput, TreePath elementPath, Object element, IElementContentProvider elementContentProvider) {
super(provider, viewerInput, elementPath, element, elementContentProvider, provider.getPresentationContext());
- fShouldFilter = provider.getViewer().getFilters().length != 0;
+ fShouldFilter = provider.areTreeModelViewerFiltersApplicable(element);
+ fFilteredChildren = provider.getFilteredChildren(elementPath);
}
public synchronized void cancel() {
@@ -66,13 +73,24 @@
}
super.cancel();
}
-
+
protected synchronized void scheduleViewerUpdate() {
- if (fShouldFilter) {
- // If filtering is enabled perform child update on all children in order to update
- // viewer filters.
+ // If filtering is enabled perform child update on all children in order to update
+ // viewer filters.
+ if (fShouldFilter || fFilteredChildren != null) {
if (fChildrenUpdate == null) {
- fChildrenUpdate = new ChildrenUpdate(getContentProvider(), getViewerInput(), getElementPath(), getElement(), 0, getCount(), getElementContentProvider()) {
+ int startIdx;
+ int count;
+ if (fShouldFilter) {
+ startIdx = 0;
+ count = getCount();
+ } else {
+ startIdx = fFilteredChildren[0];
+ int endIdx = fFilteredChildren[fFilteredChildren.length - 1];
+ count = endIdx - startIdx + 1;
+ }
+
+ fChildrenUpdate = new ChildrenUpdate(getContentProvider(), getViewerInput(), getElementPath(), getElement(), startIdx, count, getElementContentProvider()) {
protected void performUpdate() {
performUpdate(true);
ChildrenCountUpdate.super.scheduleViewerUpdate();
@@ -116,7 +134,13 @@
if (TreeModelContentProvider.DEBUG_CONTENT_PROVIDER && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) {
System.out.println("setChildCount(" + getElement() + ", modelCount: " + fCount + " viewCount: " + viewCount + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
}
- getContentProvider().getViewer().setChildCount(elementPath, viewCount);
+ // Special case for element 0 in a set of filtered elements:
+ // Child 0 is automatically updated by the tree at the same time that the child count is requested. Therefore,
+ // If this child count update filtered out this element, it needs to be updated again.
+ if (fShouldFilter && getContentProvider().isFiltered(elementPath, 0)) {
+ getContentProvider().updateElement(elementPath, 0);
+ }
+ getContentProvider().getViewer().setChildCount(elementPath, viewCount);
getContentProvider().getStateTracker().restorePendingStateOnUpdate(getElementPath(), -1, true, true, false);
}
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelContentProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelContentProvider.java
index 090cf96..d929349 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelContentProvider.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelContentProvider.java
@@ -15,6 +15,7 @@
import org.eclipse.debug.internal.ui.viewers.model.provisional.IStateUpdateListener;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdateListener;
import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewerFilter;
import org.eclipse.jface.viewers.IContentProvider;
import org.eclipse.jface.viewers.ILazyTreePathContentProvider;
import org.eclipse.jface.viewers.TreePath;
@@ -86,6 +87,20 @@
public int modelToViewIndex(TreePath parentPath, int index);
/**
+ * Returns whether the children of given element should be filtered.
+ * <p>This method is used to determine whether any of the registered filters
+ * that extend {@link TreeModelViewerFilter} are applicable to the given
+ * element. If so, then children of given element should be filtered
+ * prior to populating them in the viewer.
+ *
+ * @param parentElement
+ * the parent element
+ * @return whether there are any {@link TreeModelViewerFilter} filters
+ * applicable to given parent
+ */
+ public boolean areTreeModelViewerFiltersApplicable(Object parentElement);
+
+ /**
* Returns whether the given element is filtered.
*
* @param parentElementOrTreePath
@@ -160,6 +175,13 @@
*/
public void removeModelChangedListener(IModelChangedListener listener);
+ /**
+ * Causes the content provider to save the expansion and selection state
+ * of given element. The state is then restored as the tree is lazily
+ * re-populated.
+ * @param path Path of the element to save.
+ */
+ public void preserveState(TreePath path);
/**
* Registers the specified listener for state update notifications.
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/InternalTreeModelViewer.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/InternalTreeModelViewer.java
index d3cea20..cd0a693 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/InternalTreeModelViewer.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/InternalTreeModelViewer.java
@@ -625,6 +625,27 @@
refreshColumns();
}
}
+
+ protected void internalRefresh(Object element, boolean updateLabels) {
+ ITreeModelContentProvider contentProvider = (ITreeModelContentProvider)getContentProvider();
+
+ if (element == null) {
+ internalRefresh(getControl(), getRoot(), true, updateLabels);
+ contentProvider.preserveState(TreePath.EMPTY);
+ } else {
+ Widget[] items = findItems(element);
+ if (items.length != 0) {
+ for (int i = 0; i < items.length; i++) {
+ if (items[i] instanceof TreeItem) {
+ contentProvider.preserveState(getTreePathFromItem((TreeItem)items[i]));
+ } else {
+ contentProvider.preserveState(TreePath.EMPTY);
+ }
+ }
+ }
+ }
+ super.internalRefresh(element, updateLabels);
+ }
/**
* Refreshes the columns in the view, based on the viewer input.
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/InternalVirtualTreeModelViewer.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/InternalVirtualTreeModelViewer.java
index d3da1cf..046f3dd 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/InternalVirtualTreeModelViewer.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/InternalVirtualTreeModelViewer.java
@@ -493,6 +493,8 @@
}
private void refresh(VirtualItem item) {
+ getContentProvider().preserveState(getTreePathFromItem(item));
+
if (!item.needsDataUpdate()) {
if (item.getParent() != null) {
item.setNeedsLabelUpdate();
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/SubTreeModelViewer.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/SubTreeModelViewer.java
index 3ef3dd7..b045aba 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/SubTreeModelViewer.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/SubTreeModelViewer.java
@@ -426,6 +426,10 @@
fBaseProvider.addModelChangedListener(listener);
}
+ public void preserveState(TreePath subPath) {
+ fBaseProvider.preserveState(createFullPath(subPath));
+ }
+
public void addStateUpdateListener(IStateUpdateListener listener) {
fBaseProvider.addStateUpdateListener(listener);
}
@@ -462,6 +466,10 @@
fBaseProvider.setModelDeltaMask(mask);
}
+ public boolean areTreeModelViewerFiltersApplicable(Object parentElement) {
+ return fBaseProvider.areTreeModelViewerFiltersApplicable(parentElement);
+ }
+
public boolean shouldFilter(Object parentElementOrTreePath, Object element) {
if (parentElementOrTreePath instanceof TreePath) {
TreePath path = (TreePath)parentElementOrTreePath;
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/TreeModelContentProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/TreeModelContentProvider.java
index f38fd78..18b37ec 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/TreeModelContentProvider.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/TreeModelContentProvider.java
@@ -46,6 +46,7 @@
import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdateListener;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewerFilter;
import org.eclipse.jface.viewers.IContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TreePath;
@@ -234,6 +235,10 @@
fStateTracker.addStateUpdateListener(listener);
}
+ public void preserveState(TreePath path) {
+ fStateTracker.appendToPendingStateDelta(path);
+ }
+
public void removeStateUpdateListener(IStateUpdateListener listener) {
fStateTracker.removeStateUpdateListener(listener);
}
@@ -509,10 +514,34 @@
return fTransform.modelToViewCount(parentPath, count);
}
+ public boolean areTreeModelViewerFiltersApplicable(Object parentElement) {
+ ViewerFilter[] filters = fViewer.getFilters();
+ if (filters.length > 0) {
+ for (int j = 0; j < filters.length; j++) {
+ if (filters[j] instanceof TreeModelViewerFilter &&
+ ((TreeModelViewerFilter)filters[j]).isApplicable(fViewer, parentElement))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
public boolean shouldFilter(Object parentElementOrTreePath, Object element) {
ViewerFilter[] filters = fViewer.getFilters();
if (filters.length > 0) {
for (int j = 0; j < filters.length; j++) {
+ if (filters[j] instanceof TreeModelViewerFilter) {
+ // Skip the filter if not applicable to parent element
+ Object parentElement = parentElementOrTreePath instanceof TreePath
+ ? ((TreePath)parentElementOrTreePath).getLastSegment() : parentElementOrTreePath;
+ if (parentElement == null) parentElement = fViewer.getInput();
+ if (!((TreeModelViewerFilter)filters[j]).isApplicable(fViewer, parentElement)) {
+ continue;
+ }
+ }
+
if (!(filters[j].select((Viewer) fViewer, parentElementOrTreePath, element))) {
return true;
}
@@ -1219,7 +1248,6 @@
}
TreePath treePath = getViewerTreePath(delta);
cancelSubtreeUpdates(treePath);
- fStateTracker.appendToPendingStateDelta(treePath);
getViewer().refresh(getElement(treePath));
}
@@ -1414,9 +1442,31 @@
index = fTransform.indexOfFilteredElement(parentPath, delta.getElement());
}
if (index >= 0) {
- clearFilteredChild(parentPath, index);
+ boolean filtered = isFiltered(parentPath, index);
+ boolean shouldFilter = shouldFilter(parentPath, delta.getReplacementElement());
+
+ // Update the filter transform
+ if (filtered) {
+ clearFilteredChild(parentPath, index);
+ }
+ if (shouldFilter) {
+ addFilteredIndex(parentPath, index, delta.getElement());
+ }
+
+ // Update the viewer
+ if (filtered) {
+ if (!shouldFilter) {
+ getViewer().insert(parentPath, delta.getReplacementElement(), modelToViewIndex(parentPath, index));
+ }
+ //else do nothing
+ } else {
+ if (shouldFilter) {
+ getViewer().remove(parentPath, modelToViewIndex(parentPath, index));
+ } else {
+ getViewer().replace(parentPath, delta.getIndex(), delta.getReplacementElement());
+ }
+ }
}
- getViewer().replace(parentPath, delta.getIndex(), delta.getElement());
}
protected void handleSelect(IModelDelta delta) {
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ViewerStateTracker.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ViewerStateTracker.java
index fa71299..a116ca7 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ViewerStateTracker.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ViewerStateTracker.java
@@ -367,6 +367,8 @@
* @param path Path to subtree to restore.
*/
void appendToPendingStateDelta(final TreePath path) {
+ if (fContentProvider.getViewer() == null) return; // Not initialized yet.
+
if (DEBUG_STATE_SAVE_RESTORE && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) {
System.out.println("STATE APPEND BEGIN: " + path.getLastSegment()); //$NON-NLS-1$
}
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/TreeModelViewerFilter.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/TreeModelViewerFilter.java
new file mode 100644
index 0000000..0779386
--- /dev/null
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/TreeModelViewerFilter.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.debug.internal.ui.viewers.model.provisional;
+
+import org.eclipse.jface.viewers.ViewerFilter;
+
+/**
+ * Viewer filter for the Tree Model Viewer which allows more efficient filtering
+ * in the lazy viewer.
+ * <p>
+ * The standard {@link ViewerFilter} class must be applied to all elements in the
+ * tree, thus forcing the lazy viewer to retrieve all children of all elements and
+ * defeating the lazy loading behavior. This class adds an {@link #isApplicable(ITreeModelViewer, Object)}
+ * method, which can be used by the filter to discern which parent elements the
+ * filter should apply to.
+ * </p>
+ *
+ * @since 3.8
+ */
+abstract public class TreeModelViewerFilter extends ViewerFilter {
+
+ /**
+ * Determines whether the filter applies to the given parent element.
+ * @return Returns true if the viewer should use the given filter on the
+ * given element.
+ */
+ abstract public boolean isApplicable(ITreeModelViewer viewer, Object parentElement);
+}