diff options
author | Pawel Piech | 2009-11-09 23:12:41 +0000 |
---|---|---|
committer | Pawel Piech | 2009-11-09 23:12:41 +0000 |
commit | 565144f327f94dea5cbbaf0bdea0cddc79280463 (patch) | |
tree | 5b1aef471f85a19121f453d8533ad1b78839d702 | |
parent | fd836696c57b45691efd5a3dff341cc962b81c6e (diff) | |
download | eclipse.platform.debug-565144f327f94dea5cbbaf0bdea0cddc79280463.tar.gz eclipse.platform.debug-565144f327f94dea5cbbaf0bdea0cddc79280463.tar.xz eclipse.platform.debug-565144f327f94dea5cbbaf0bdea0cddc79280463.zip |
Bug 291267 - [flex-hierarchy] Viewer does not preserve expansion state correctly when elements are inserted/removed due to IModelDelta.CONTENT
29 files changed, 1308 insertions, 248 deletions
diff --git a/org.eclipse.debug.tests/META-INF/MANIFEST.MF b/org.eclipse.debug.tests/META-INF/MANIFEST.MF index 8a243b9d5..052406d6e 100644 --- a/org.eclipse.debug.tests/META-INF/MANIFEST.MF +++ b/org.eclipse.debug.tests/META-INF/MANIFEST.MF @@ -8,7 +8,8 @@ Require-Bundle: org.eclipse.ui;bundle-version="[3.6.0,4.0.0)", org.eclipse.core.runtime;bundle-version="[3.5.0,4.0.0)", org.eclipse.debug.ui;bundle-version="[3.6.0,4.0.0)", org.junit;bundle-version="3.8.2", - org.eclipse.core.filesystem;bundle-version="[1.3.0,2.0.0)" + org.eclipse.core.filesystem;bundle-version="[1.3.0,2.0.0)", + org.eclipse.test.performance;bundle-version="3.6.0" Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: J2SE-1.4 Bundle-Vendor: %providerName diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/ChildrenUpdateTests.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/ChildrenUpdateTests.java index c0244be14..561104977 100644 --- a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/ChildrenUpdateTests.java +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/ChildrenUpdateTests.java @@ -65,7 +65,7 @@ public class ChildrenUpdateTests extends TestCase { public void setAutoExpandLevel(int level) { } - public void saveElementState(TreePath path, ModelDelta delta) { + public void saveElementState(TreePath path, ModelDelta delta, int flags) { } public void removeViewerUpdateListener(IViewerUpdateListener listener) { @@ -163,6 +163,10 @@ public class ChildrenUpdateTests extends TestCase { return null; } + public boolean getHasChildren(Object elementOrTreePath) { + return false; + } + public int getChildCount(TreePath path) { return 0; } @@ -176,6 +180,10 @@ public class ChildrenUpdateTests extends TestCase { public void autoExpand(TreePath elementPath) { } + + public boolean getElementChildrenRealized(TreePath parentPath) { + return false; + } }; } } diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/DeltaTests.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/DeltaTests.java index ec382c721..5f6886270 100644 --- a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/DeltaTests.java +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/DeltaTests.java @@ -121,9 +121,9 @@ abstract public class DeltaTests extends TestCase { TestElement element = model.getRootElement().getChildren()[0]; TreePath elementPath = new TreePath(new Object[] { element }); TestElement[] newChildren = new TestElement[] { - new TestElement(model, "1.1", new TestElement[0]), - new TestElement(model, "1.2", new TestElement[0]), - new TestElement(model, "1.3", new TestElement[0]), + new TestElement(model, "1.1 - new", new TestElement[0]), + new TestElement(model, "1.2 - new", new TestElement[0]), + new TestElement(model, "1.3 - new", new TestElement[0]), }; ModelDelta delta = model.setElementChildren(elementPath, newChildren); @@ -133,6 +133,56 @@ abstract public class DeltaTests extends TestCase { model.validateData(fViewer, TreePath.EMPTY); } + public void testRefreshStruct2() { + //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer); + + TestModel model = TestModel.simpleMultiLevel(); + fViewer.setAutoExpandLevel(-1); + + // Create the listener + 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 ()) fDisplay.sleep (); + model.validateData(fViewer, TreePath.EMPTY); + + String prefix = "new - "; + model.setElementChildren(TreePath.EMPTY, new TestElement[] { + new TestElement(model, prefix + "1", new TestElement[0]), + new TestElement(model, prefix + "2", true, false, new TestElement[] { + new TestElement(model, prefix + "2.1", true, true, new TestElement[0]), + new TestElement(model, prefix + "2.2", false, true, new TestElement[0]), + new TestElement(model, prefix + "2.3", true, false, new TestElement[0]), + }), + new TestElement(model, prefix + "3", new TestElement[] { + new TestElement(model, prefix + "3.1", new TestElement[] { + new TestElement(model, prefix + "3.1.1", new TestElement[0]), + new TestElement(model, prefix + "3.1.2", new TestElement[0]), + new TestElement(model, prefix + "3.1.3", new TestElement[0]), + }), + new TestElement(model, prefix + "3.2", new TestElement[] { + new TestElement(model, prefix + "3.2.1", new TestElement[0]), + new TestElement(model, prefix + "3.2.2", new TestElement[0]), + new TestElement(model, prefix + "3.2.3", new TestElement[0]), + }), + new TestElement(model, prefix + "3.3", new TestElement[] { + new TestElement(model, prefix + "3.3.1", new TestElement[0]), + new TestElement(model, prefix + "3.3.2", new TestElement[0]), + new TestElement(model, prefix + "3.3.3", new TestElement[0]), + }), + }) + }); + + TestElement element = model.getRootElement(); + fListener.reset(TreePath.EMPTY, element, -1, false, false); + + model.postDelta(new ModelDelta(element, IModelDelta.CONTENT)); + while (!fListener.isFinished(TestModelUpdatesListener.ALL_UPDATES_COMPLETE | TestModelUpdatesListener.MODEL_CHANGED_COMPLETE)) + if (!fDisplay.readAndDispatch ()) fDisplay.sleep (); + model.validateData(fViewer, TreePath.EMPTY); + } + public void testInsert() { //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer); @@ -160,7 +210,7 @@ abstract public class DeltaTests extends TestCase { fListener.addChildreUpdate(TreePath.EMPTY, 6); fListener.addHasChildrenUpdate(elementPath); fListener.addLabelUpdate(elementPath); - // TODO: redundant updates on insert! + // TODO: redundant label updates on insert! fListener.setFailOnRedundantUpdates(false); model.postDelta(delta); while (!fListener.isFinished(TestModelUpdatesListener.ALL_UPDATES_COMPLETE | TestModelUpdatesListener.MODEL_CHANGED_COMPLETE)) @@ -296,7 +346,7 @@ abstract public class DeltaTests extends TestCase { } - public void testRemoveElement() { + public void testRemove() { //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer); TestModel model = TestModel.simpleSingleLevel(); @@ -465,4 +515,58 @@ abstract public class DeltaTests extends TestCase { model.validateData(fViewer, TreePath.EMPTY); } + + public void testBug292322() { + //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer); + TestModel model = TestModel.simpleMultiLevel(); + 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 ()) fDisplay.sleep (); + model.validateData(fViewer, TreePath.EMPTY, true); + + // Update the model: remove one child of an un-expanded element, then + // make sure that the number of children is correct. + TreePath parentPath = model.findElement("2"); + TestElement parentElement = model.getElement(parentPath); + ModelDelta delta = model.removeElementChild(parentPath, 0); + + // Update the viewer + fListener.reset(parentPath, parentElement, 0, false, false); + //fListener.addChildreCountUpdate(parentPath); + model.postDelta(delta); + while (!fListener.isFinished(TestModelUpdatesListener.MODEL_CHANGED_COMPLETE | TestModelUpdatesListener.CONTENT_COMPLETE)) + if (!fDisplay.readAndDispatch ()) fDisplay.sleep (); + + // Validate the viewer data. + model.validateData(fViewer, TreePath.EMPTY, true); + + // Update the model: remove the remaining children and make sure that + // the element children are updated to false. + model.removeElementChild(parentPath, 0); + + // Update the viewer + fListener.reset(parentPath, parentElement, 0, false, false); + model.postDelta(delta); + while (!fListener.isFinished(TestModelUpdatesListener.MODEL_CHANGED_COMPLETE | TestModelUpdatesListener.CONTENT_COMPLETE)) + if (!fDisplay.readAndDispatch ()) fDisplay.sleep (); + + // Validate the viewer data. + model.validateData(fViewer, TreePath.EMPTY, true); + + // Update the model: remove the remaining children and make sure that + // the element children are updated to false. + model.removeElementChild(parentPath, 0); + + // Update the viewer + fListener.reset(parentPath, parentElement, 0, false, false); + model.postDelta(delta); + while (!fListener.isFinished(TestModelUpdatesListener.MODEL_CHANGED_COMPLETE | TestModelUpdatesListener.CONTENT_COMPLETE)) + if (!fDisplay.readAndDispatch ()) fDisplay.sleep (); + + // Validate the viewer data. + model.validateData(fViewer, TreePath.EMPTY, true); + } + } diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/ITestModelUpdatesListenerConstants.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/ITestModelUpdatesListenerConstants.java new file mode 100644 index 000000000..26570fc5f --- /dev/null +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/ITestModelUpdatesListenerConstants.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2009 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.eclipe.debug.tests.viewer.model; + +/** + * Convenience interface with constants used by the test model update listener. + * + * @since 3.6 + */ +public interface ITestModelUpdatesListenerConstants { + + public static final int LABEL_UPDATES_COMPLETE = 0X0001; + public static final int CONTENT_UPDATES_COMPLETE = 0X0002; + public static final int LABEL_UPDATES = 0X0004; + public static final int HAS_CHILDREN_UPDATES = 0X0008; + public static final int CHILDREN_COUNT_UPDATES = 0X0010; + public static final int CHILDREN_UPDATES = 0X0020; + public static final int MODEL_CHANGED_COMPLETE = 0X0040; + public static final int MODEL_PROXIES_INSTALLED = 0X0080; + + public static final int VIEWER_UPDATES_RUNNING = 0X0100; + public static final int LABEL_UPDATES_RUNNING = 0X0200; + + public static final int LABEL_COMPLETE = LABEL_UPDATES_COMPLETE | LABEL_UPDATES; + public static final int CONTENT_COMPLETE = + CONTENT_UPDATES_COMPLETE | HAS_CHILDREN_UPDATES | CHILDREN_COUNT_UPDATES | CHILDREN_UPDATES; + + public static final int ALL_UPDATES_COMPLETE = LABEL_COMPLETE | CONTENT_COMPLETE | MODEL_PROXIES_INSTALLED | LABEL_UPDATES_RUNNING | VIEWER_UPDATES_RUNNING; +} diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerDeltaTests.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerDeltaTests.java index f50a0587f..ed613bd6f 100644 --- a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerDeltaTests.java +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerDeltaTests.java @@ -29,4 +29,10 @@ public class JFaceViewerDeltaTests extends DeltaTests { protected ITreeModelContentProviderTarget createViewer(Display display, Shell shell) { return new TreeModelViewer(fShell, SWT.VIRTUAL, new PresentationContext("TestViewer")); } + + /** + * TODO: remove this method when bug 292322 gets fixed in TreeViewer + */ + public void testBug292322() { + } } diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerPerformanceTests.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerPerformanceTests.java new file mode 100644 index 000000000..865db9508 --- /dev/null +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerPerformanceTests.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2009 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.eclipe.debug.tests.viewer.model; + +import org.eclipse.debug.internal.ui.viewers.model.ITreeModelContentProviderTarget; +import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +/** + * @since 3.6 + */ +public class JFaceViewerPerformanceTests extends PerformanceTests { + + public JFaceViewerPerformanceTests(String name) { + super(name); + } + + protected ITreeModelContentProviderTarget createViewer(Display display, Shell shell) { + return new TreeModelViewer(fShell, SWT.VIRTUAL, new PresentationContext("TestViewer")); + } + + protected int getTestModelDepth() { + return 5; + } +} 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 new file mode 100644 index 000000000..f4c0786c6 --- /dev/null +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/PerformanceTests.java @@ -0,0 +1,173 @@ +/******************************************************************************* + * Copyright (c) 2009 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.eclipe.debug.tests.viewer.model; + +import junit.framework.TestCase; + +import org.eclipe.debug.tests.viewer.model.TestModel.TestElement; +import org.eclipse.debug.internal.ui.viewers.model.ITreeModelContentProviderTarget; +import org.eclipse.debug.internal.ui.viewers.model.ITreeModelViewer; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; +import org.eclipse.jface.viewers.TreePath; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.test.performance.Performance; +import org.eclipse.test.performance.PerformanceMeter; +import org.eclipse.ui.PlatformUI; + +/** + * Tests to measure the performance of the viewer updates. + */ +abstract public class PerformanceTests extends TestCase implements ITestModelUpdatesListenerConstants { + Display fDisplay; + Shell fShell; + ITreeModelViewer fViewer; + TestModelUpdatesListener fListener; + + public PerformanceTests(String name) { + super(name); + } + + /** + * @throws java.lang.Exception + */ + protected void setUp() throws Exception { + fDisplay = PlatformUI.getWorkbench().getDisplay(); + fShell = new Shell(fDisplay/*, SWT.ON_TOP | SWT.SHELL_TRIM*/); + fShell.setMaximized(true); + fShell.setLayout(new FillLayout()); + + fViewer = createViewer(fDisplay, fShell); + + fListener = new TestModelUpdatesListener(false, false); + fViewer.addViewerUpdateListener(fListener); + fViewer.addLabelUpdateListener(fListener); + fViewer.addModelChangedListener(fListener); + + fShell.open (); + } + + abstract protected ITreeModelContentProviderTarget createViewer(Display display, Shell shell); + + /** + * @throws java.lang.Exception + */ + protected void tearDown() throws Exception { + fViewer.removeLabelUpdateListener(fListener); + fViewer.removeViewerUpdateListener(fListener); + fViewer.removeModelChangedListener(fListener); + + // Close the shell and exit. + fShell.close(); + while (!fShell.isDisposed()) if (!fDisplay.readAndDispatch ()) fDisplay.sleep (); + } + + /** + * Depth (size) of the test model to be used in the tests. This number allows + * the jface based tests to use a small enough model to fit on the screen, and + * for the virtual viewer to exercise the content provider to a greater extent. + */ + abstract protected int getTestModelDepth(); + + public void testRefreshStruct() { + TestModel model = new TestModel(); + model.setRoot( new TestElement(model, "root", new TestElement[0] ) ); + model.setElementChildren(TreePath.EMPTY, makeModelElements(model, getTestModelDepth(), "model")); + + fViewer.setAutoExpandLevel(-1); + + // Create the listener + 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 ()) fDisplay.sleep (); + 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); + //model.setElementChildren(TreePath.EMPTY, makeModelElements(model, getTestModelDepth(), "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 ()) fDisplay.sleep (); + model.validateData(fViewer, TreePath.EMPTY); + meter.stop(); + System.gc(); + } + + meter.commit(); + perf.assertPerformance(meter); + } finally { + meter.dispose(); + } + } + + public void testRefreshStructReplaceElements() { + TestModel model = new TestModel(); + model.setRoot( new TestElement(model, "root", new TestElement[0] ) ); + model.setElementChildren(TreePath.EMPTY, makeModelElements(model, getTestModelDepth(), "model")); + + fViewer.setAutoExpandLevel(-1); + + // Create the listener + 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 ()) fDisplay.sleep (); + model.validateData(fViewer, TreePath.EMPTY); + + Performance perf = Performance.getDefault(); + PerformanceMeter meter = perf.createPerformanceMeter(perf.getDefaultScenarioId(this)); + try { + for (int i = 0; i < 2000; i++) { + // Update the model + model.setElementChildren(TreePath.EMPTY, makeModelElements(model, getTestModelDepth(), "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 ()) fDisplay.sleep (); + model.validateData(fViewer, TreePath.EMPTY); + meter.stop(); + System.gc(); + } + + meter.commit(); + perf.assertPerformance(meter); + } finally { + meter.dispose(); + } + } + + private TestElement[] makeModelElements(TestModel model, int depth, String prefix) { + TestElement[] elements = new TestElement[depth]; + for (int i = 0; i < depth; i++) { + String name = prefix + "." + i; + elements[i] = new TestElement(model, name, makeModelElements(model, i, name)); + } + return elements; + } +} 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 f98c77a40..f19df2734 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 @@ -14,6 +14,7 @@ import junit.framework.Assert; import junit.framework.TestCase; import org.eclipe.debug.tests.viewer.model.TestModel.TestElement; +import org.eclipse.debug.internal.ui.viewers.model.ITreeModelContentProviderTarget; import org.eclipse.debug.internal.ui.viewers.model.ITreeModelViewer; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; @@ -29,7 +30,7 @@ import org.eclipse.ui.PlatformUI; * * @since 3.6 */ -abstract public class StateTests extends TestCase { +abstract public class StateTests extends TestCase implements ITestModelUpdatesListenerConstants { Display fDisplay; Shell fShell; ITreeModelViewer fViewer; @@ -123,12 +124,12 @@ abstract public class StateTests extends TestCase { fListener.addHasChildrenUpdate(path3); fListener.addLabelUpdate(path3); - while (!fListener.isFinished(TestModelUpdatesListener.CONTENT_UPDATES_COMPLETE | TestModelUpdatesListener.LABEL_UPDATES)) + while (!fListener.isFinished(CONTENT_UPDATES_COMPLETE | LABEL_UPDATES)) if (!fDisplay.readAndDispatch ()) fDisplay.sleep (); // Extract the new state from viewer ModelDelta savedDelta = new ModelDelta(model.getRootElement(), IModelDelta.NO_CHANGE); - fViewer.saveElementState(path0, savedDelta); + fViewer.saveElementState(path0, savedDelta, IModelDelta.EXPAND | IModelDelta.SELECT); Assert.assertTrue( deltaMatches(updateDelta, savedDelta) ); } @@ -154,4 +155,183 @@ abstract public class StateTests extends TestCase { return false; } + private TestModel alternatingSubsreesModel() { + TestModel model = new TestModel(); + model.setRoot( new TestElement(model, "root", new TestElement[] { + new TestElement(model, "1", new TestElement[] { + new TestElement(model, "1.1", new TestElement[] { + new TestElement(model, "1.1.1", new TestElement[0]), + }), + }), + new TestElement(model, "2", new TestElement[] { + new TestElement(model, "2.1", new TestElement[] { + new TestElement(model, "2.1.1", new TestElement[0]), + }), + }), + new TestElement(model, "3", new TestElement[] { + new TestElement(model, "3.1", new TestElement[] { + new TestElement(model, "3.1.1", new TestElement[0]), + }), + }), + new TestElement(model, "4", new TestElement[] { + new TestElement(model, "4.1", new TestElement[] { + new TestElement(model, "4.1.1", new TestElement[0]), + }), + }), + new TestElement(model, "5", new TestElement[] { + new TestElement(model, "5.1", new TestElement[] { + new TestElement(model, "5.1.1", new TestElement[0]), + }), + }), + new TestElement(model, "6", new TestElement[] { + new TestElement(model, "6.1", new TestElement[] { + new TestElement(model, "6.1.1", new TestElement[0]), + }), + }) + }) ); + return model; + } + + private void expandAlternateElements(TestModel model) { + // Expand every other child + fListener.reset(); + fListener.setFailOnRedundantUpdates(false); + ITreeModelContentProviderTarget viewer = (ITreeModelContentProviderTarget)fViewer; + TreePath path; + fListener.addUpdates(path = model.findElement("1"), (TestElement)path.getLastSegment(), 1, CHILDREN_COUNT_UPDATES | CHILDREN_UPDATES); + viewer.setExpandedState(path, true); + fListener.addUpdates(path = model.findElement("3"), (TestElement)path.getLastSegment(), 1, CHILDREN_COUNT_UPDATES | CHILDREN_UPDATES); + viewer.setExpandedState(path, true); + fListener.addUpdates(path = model.findElement("5"), (TestElement)path.getLastSegment(), 1, CHILDREN_COUNT_UPDATES | CHILDREN_UPDATES); + viewer.setExpandedState(path, true); + + while (!fListener.isFinished()) if (!fDisplay.readAndDispatch ()) fDisplay.sleep (); + model.validateData(fViewer, TreePath.EMPTY, true); + + // Expand the sub-children as well (so that the expanded nodes go 2 levels down. + fListener.reset(); + fListener.addUpdates(path = model.findElement("1.1"), (TestElement)path.getLastSegment(), 1, CHILDREN_COUNT_UPDATES | CHILDREN_UPDATES); + viewer.setExpandedState(path, true); + fListener.addUpdates(path = model.findElement("3.1"), (TestElement)path.getLastSegment(), 1, CHILDREN_COUNT_UPDATES | CHILDREN_UPDATES); + viewer.setExpandedState(path, true); + fListener.addUpdates(path = model.findElement("5.1"), (TestElement)path.getLastSegment(), 1, CHILDREN_COUNT_UPDATES | CHILDREN_UPDATES); + viewer.setExpandedState(path, true); + while (!fListener.isFinished()) if (!fDisplay.readAndDispatch ()) fDisplay.sleep (); + model.validateData(fViewer, TreePath.EMPTY, true); + + } + + public void testPreserveExpandedOnRemove() { + //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer); + TestModel model = alternatingSubsreesModel(); + + // 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 ()) fDisplay.sleep (); + model.validateData(fViewer, TreePath.EMPTY, true); + + expandAlternateElements(model); + + // Update the model + ModelDelta delta = model.removeElementChild(TreePath.EMPTY, 0); + + // Remove delta should not generate any new updates + fListener.reset(); + model.postDelta(delta); + while (!fListener.isFinished(MODEL_CHANGED_COMPLETE)) + if (!fDisplay.readAndDispatch ()) fDisplay.sleep (); + model.validateData(fViewer, TreePath.EMPTY, true); + ITreeModelContentProviderTarget viewer = (ITreeModelContentProviderTarget)fViewer; + Assert.assertTrue(viewer.getExpandedState(model.findElement("2")) == false); + Assert.assertTrue(viewer.getExpandedState(model.findElement("3")) == true); + Assert.assertTrue(viewer.getExpandedState(model.findElement("3.1")) == true); + Assert.assertTrue(viewer.getExpandedState(model.findElement("4")) == false); + Assert.assertTrue(viewer.getExpandedState(model.findElement("5")) == true); + Assert.assertTrue(viewer.getExpandedState(model.findElement("5.1")) == true); + Assert.assertTrue(viewer.getExpandedState(model.findElement("6")) == false); + } + + public void testPreserveExpandedOnInsert() { + //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer); + TestModel model = alternatingSubsreesModel(); + + // 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 ()) fDisplay.sleep (); + model.validateData(fViewer, TreePath.EMPTY, true); + + expandAlternateElements(model); + + // Update the model + ModelDelta delta = model.insertElementChild(TreePath.EMPTY, 0, new TestElement(model, "0 - new", new TestElement[0])); + + // Insert delta should generate updates only for the new element + TreePath path = model.findElement("0 - new"); + // Note: redundant label updates on insert. + fListener.reset(path, (TestElement)path.getLastSegment(), 0, false, false); + fListener.addChildreUpdate(TreePath.EMPTY, 0); + model.postDelta(delta); + while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | ALL_UPDATES_COMPLETE)) + if (!fDisplay.readAndDispatch ()) fDisplay.sleep (); + model.validateData(fViewer, TreePath.EMPTY, true); + ITreeModelContentProviderTarget viewer = (ITreeModelContentProviderTarget)fViewer; + Assert.assertTrue(viewer.getExpandedState(model.findElement("1")) == true); + Assert.assertTrue(viewer.getExpandedState(model.findElement("1.1")) == true); + Assert.assertTrue(viewer.getExpandedState(model.findElement("2")) == false); + Assert.assertTrue(viewer.getExpandedState(model.findElement("3")) == true); + Assert.assertTrue(viewer.getExpandedState(model.findElement("3.1")) == true); + Assert.assertTrue(viewer.getExpandedState(model.findElement("4")) == false); + Assert.assertTrue(viewer.getExpandedState(model.findElement("5")) == true); + Assert.assertTrue(viewer.getExpandedState(model.findElement("5.1")) == true); + Assert.assertTrue(viewer.getExpandedState(model.findElement("6")) == false); + } + + + public void testPreserveExpandedOnContent() { + //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer); + TestModel model = alternatingSubsreesModel(); + + // 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 ()) fDisplay.sleep (); + model.validateData(fViewer, TreePath.EMPTY, true); + + expandAlternateElements(model); + + // Update the model + model.removeElementChild(TreePath.EMPTY, 0); + + // Content delta should generate updates for the elements being re-expanded + ITreeModelContentProviderTarget viewer = (ITreeModelContentProviderTarget)fViewer; + // Note: Re-expanding nodes causes redundant updates. + fListener.reset(false, false); + fListener.addUpdates(viewer, TreePath.EMPTY, model.getRootElement(), -1, ALL_UPDATES_COMPLETE); + model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT)); + while (!fListener.isFinished(ALL_UPDATES_COMPLETE)) + if (!fDisplay.readAndDispatch ()) fDisplay.sleep (); + model.validateData(fViewer, TreePath.EMPTY, true); + Assert.assertTrue(viewer.getExpandedState(model.findElement("2")) == false); + Assert.assertTrue(viewer.getExpandedState(model.findElement("3")) == true); + Assert.assertTrue(viewer.getExpandedState(model.findElement("3.1")) == true); + Assert.assertTrue(viewer.getExpandedState(model.findElement("4")) == false); + Assert.assertTrue(viewer.getExpandedState(model.findElement("5")) == true); + Assert.assertTrue(viewer.getExpandedState(model.findElement("5.1")) == true); + Assert.assertTrue(viewer.getExpandedState(model.findElement("6")) == false); + } + } diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/TestModel.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/TestModel.java index 11f7f983a..c4f5f348d 100644 --- a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/TestModel.java +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/TestModel.java @@ -152,7 +152,7 @@ public class TestModel implements IElementContentProvider, IElementLabelProvider /** * Constructor private. Use static factory methods instead. */ - private TestModel() {} + public TestModel() {} public TestElement getRootElement() { return fRoot; @@ -264,7 +264,18 @@ public class TestModel implements IElementContentProvider, IElementLabelProvider doSetExpanded(element.fChildren[i]); } } + + public void setAllAppendix(String appendix) { + doSetAllAppendix(fRoot, appendix); + } + private void doSetAllAppendix(TestElement element, String appendix) { + element.setLabelAppendix(appendix); + for (int i = 0; i < element.fChildren.length; i++) { + doSetAllAppendix(element.fChildren[i], appendix); + } + } + public void validateData(ITreeModelViewer viewer, TreePath path) { validateData(viewer, path, false); @@ -287,10 +298,13 @@ public class TestModel implements IElementContentProvider, IElementLabelProvider Assert.assertEquals(children[i], viewer.getChildElement(path, i)); validateData(viewer, path.createChildPath(children[i]), expandedElementsOnly); } + } else if (!viewer.getExpandedState(path)) { + // If element not expanded, verify the plus sign. + Assert.assertEquals(viewer.getHasChildren(path), element.getChildren().length > 0); } } - private void setRoot(TestElement root) { + public void setRoot(TestElement root) { fRoot = root; } @@ -557,7 +571,7 @@ public class TestModel implements IElementContentProvider, IElementLabelProvider }) ); return model; } - + public static TestModel compositeMultiLevel() { TestModel m2 = new TestModel(); m2.setRoot( new TestElement(m2, "m2.root", new TestElement[] { 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 928c7fa32..c694e1d1d 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 @@ -20,6 +20,7 @@ import junit.framework.Assert; import org.eclipe.debug.tests.viewer.model.TestModel.TestElement; import org.eclipse.debug.internal.ui.viewers.model.ILabelUpdateListener; +import org.eclipse.debug.internal.ui.viewers.model.ITreeModelContentProviderTarget; import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; @@ -31,27 +32,9 @@ import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdateListener; import org.eclipse.jface.viewers.TreePath; -public class TestModelUpdatesListener implements IViewerUpdateListener, ILabelUpdateListener, IModelChangedListener { - - public static final int LABEL_UPDATES_COMPLETE = 0X0001; - public static final int CONTENT_UPDATES_COMPLETE = 0X0002; - public static final int LABEL_UPDATES = 0X0004; - public static final int HAS_CHILDREN_UPDATES = 0X0008; - public static final int CHILDREN_COUNT_UPDATES = 0X0010; - public static final int CHILDREN_UPDATES = 0X0020; - public static final int MODEL_CHANGED_COMPLETE = 0X0040; - public static final int MODEL_PROXIES_INSTALLED = 0X0080; - - public static final int VIEWER_UPDATES_RUNNING = 0X0100; - public static final int LABEL_UPDATES_RUNNING = 0X0200; - - public static final int LABEL_COMPLETE = LABEL_UPDATES_COMPLETE | LABEL_UPDATES; - public static final int CONTENT_COMPLETE = - CONTENT_UPDATES_COMPLETE | HAS_CHILDREN_UPDATES | CHILDREN_COUNT_UPDATES | CHILDREN_UPDATES; - - - public static final int ALL_UPDATES_COMPLETE = LABEL_COMPLETE | CONTENT_COMPLETE | MODEL_PROXIES_INSTALLED | LABEL_UPDATES_RUNNING | VIEWER_UPDATES_RUNNING; - +public class TestModelUpdatesListener + implements IViewerUpdateListener, ILabelUpdateListener, IModelChangedListener, ITestModelUpdatesListenerConstants +{ private boolean fFailOnRedundantUpdates; private boolean fFailOnMultipleUpdateSequences; @@ -87,7 +70,13 @@ public class TestModelUpdatesListener implements IViewerUpdateListener, ILabelUp setFailOnRedundantUpdates(failOnRedundantUpdates); setFailOnMultipleUpdateSequences(failOnMultipleUpdateSequences); } - + + public void reset(boolean failOnRedundantUpdates, boolean failOnMultipleUpdateSequences) { + reset(); + setFailOnRedundantUpdates(failOnRedundantUpdates); + setFailOnMultipleUpdateSequences(failOnMultipleUpdateSequences); + } + public void reset() { fHasChildrenUpdates.clear(); fChildrenUpdates.clear(); @@ -124,7 +113,7 @@ public class TestModelUpdatesListener implements IViewerUpdateListener, ILabelUp childrenIndexes.add(new Integer(index)); } - public void recurseremoveChildreUpdate(TreePath path, int index) { + public void removeChildrenUpdate(TreePath path, int index) { Set childrenIndexes = (Set)fChildrenUpdates.get(path); if (childrenIndexes != null) { childrenIndexes.remove(new Integer(index)); @@ -143,27 +132,42 @@ public class TestModelUpdatesListener implements IViewerUpdateListener, ILabelUp } public void addUpdates(TreePath path, TestElement element, int levels) { - TestElement[] children = element.getChildren(); - + addUpdates(path, element, levels, ALL_UPDATES_COMPLETE); + } + + public void addUpdates(TreePath path, TestElement element, int levels, int flags) { + addUpdates(null, path, element, levels, flags); + } + + public void addUpdates(ITreeModelContentProviderTarget viewer, TreePath path, TestElement element, int levels, int flags) { if (!path.equals(TreePath.EMPTY)) { - fLabelUpdates.add(path); - fHasChildrenUpdates.add(path); + if ((flags & LABEL_UPDATES) != 0) { + fLabelUpdates.add(path); + } + if ((flags & HAS_CHILDREN_UPDATES) != 0) { + fHasChildrenUpdates.add(path); + } } - if (levels != 0) { - levels--; - if (children.length > 0) { - fChildCountUpdates.add(path); - Set childrenIndexes = new HashSet(); + if (levels-- != 0) { + TestElement[] children = element.getChildren(); + if (children.length > 0 && (viewer == null || path.getSegmentCount() == 0 || viewer.getExpandedState(path))) { + if ((flags & CHILDREN_COUNT_UPDATES) != 0) { + fChildCountUpdates.add(path); + } + if ((flags & CHILDREN_UPDATES) != 0) { + Set childrenIndexes = new HashSet(); + for (int i = 0; i < children.length; i++) { + childrenIndexes.add(new Integer(i)); + } + fChildrenUpdates.put(path, childrenIndexes); + } + for (int i = 0; i < children.length; i++) { - childrenIndexes.add(new Integer(i)); + addUpdates(viewer, path.createChildPath(children[i]), children[i], levels, flags); } - fChildrenUpdates.put(path, childrenIndexes); } - for (int i = 0; i < children.length; i++) { - addUpdates(path.createChildPath(children[i]), children[i], levels); - } } } @@ -224,11 +228,9 @@ public class TestModelUpdatesListener implements IViewerUpdateListener, ILabelUp synchronized (this) { fViewerUpdatesRunning++; } - System.out.println("started: " + update); } public void updateComplete(IViewerUpdate update) { - System.out.println("completed: " + update); synchronized (this) { fViewerUpdatesRunning--; } @@ -285,7 +287,6 @@ public class TestModelUpdatesListener implements IViewerUpdateListener, ILabelUp synchronized (this) { fLabelUpdatesRunning++; } - System.out.println("started: " + update); } public void labelUpdatesBegin() { diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VirtualViewerLazyModeTests.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VirtualViewerLazyModeTests.java index 7977d286b..6afa8f6de 100644 --- a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VirtualViewerLazyModeTests.java +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VirtualViewerLazyModeTests.java @@ -10,16 +10,11 @@ *******************************************************************************/ package org.eclipe.debug.tests.viewer.model; -import junit.framework.Assert; import junit.framework.TestCase; -import org.eclipe.debug.tests.viewer.model.TestModel.TestElement; import org.eclipse.debug.internal.ui.viewers.model.ITreeModelViewer; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; -import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext; import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualTreeModelViewer; -import org.eclipse.jface.viewers.TreePath; import org.eclipse.swt.SWT; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Display; @@ -28,6 +23,8 @@ import org.eclipse.ui.PlatformUI; /** * Tests which verify the operation of the virtual viewer in the lazy mode. + * Note: the virtual viewer doesn't support lazy mode yet, so this class + * is really just a big place holder. * * @since 3.6 */ @@ -77,91 +74,7 @@ public class VirtualViewerLazyModeTests extends TestCase { while (!fShell.isDisposed()) if (!fDisplay.readAndDispatch ()) fDisplay.sleep (); } - public void testUpdateViewer() { - //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer); - - TestModel model = TestModel.simpleMultiLevel(); - - // Initialize the listener. In lazy mode, until a selection - // is made, only the root element count should be updated. - fListener.reset(); - fListener.addChildreCountUpdate(TreePath.EMPTY); - - // Set the input into the view and update the view. - fViewer.setInput(model.getRootElement()); - while (!fListener.isFinished(TestModelUpdatesListener.CONTENT_UPDATES_COMPLETE)) - if (!fDisplay.readAndDispatch ()) fDisplay.sleep (); - - // Do not try to validate data, because the virtual viewer contains only - // a subset of model. - - // Create the update delta - TestElement element = model.getRootElement(); - TreePath path0 = TreePath.EMPTY; - ModelDelta delta = new ModelDelta(model.getRootElement(), -1, IModelDelta.EXPAND, element.getChildren().length); - ModelDelta updateDelta = delta; - element = element.getChildren()[2]; - TreePath path1 = path0.createChildPath(element); - delta = delta.addNode(element, 2, IModelDelta.EXPAND, element.fChildren.length); - element = element.getChildren()[1]; - TreePath path2 = path1.createChildPath(element); - delta = delta.addNode(element, 1, IModelDelta.EXPAND, element.fChildren.length); - element = element.getChildren()[1]; - TreePath path3 = path2.createChildPath(element); - delta = delta.addNode(element, 1, IModelDelta.SELECT); - - fViewer.updateViewer(updateDelta); - - // Viewer should update data and labels of items in selection - fListener.reset(); - fListener.addChildreUpdate(path0, 2); - fListener.addHasChildrenUpdate(path1); - fListener.addChildreCountUpdate(path1); - fListener.addLabelUpdate(path1); - fListener.addChildreUpdate(path1, 1); - fListener.addHasChildrenUpdate(path2); - fListener.addChildreCountUpdate(path2); - fListener.addLabelUpdate(path2); - fListener.addHasChildrenUpdate(path2); - fListener.addChildreCountUpdate(path2); - fListener.addChildreUpdate(path2, 1); - fListener.addHasChildrenUpdate(path3); - fListener.addLabelUpdate(path3); - - // When processing expand and select deltas, the label may be requested - // multiple times for the same element. So we have to set the fail for - // redundant updates to false. - fListener.setFailOnRedundantUpdates(false); - - while (!fListener.isFinished(TestModelUpdatesListener.CONTENT_UPDATES_COMPLETE | TestModelUpdatesListener.LABEL_UPDATES)) - if (!fDisplay.readAndDispatch ()) fDisplay.sleep (); - - // Extract the new state from viewer - ModelDelta savedDelta = new ModelDelta(model.getRootElement(), IModelDelta.NO_CHANGE); - fViewer.saveElementState(path0, savedDelta); - - Assert.assertTrue( deltaMatches(updateDelta, savedDelta) ); + public void test() { + // TODO } - - boolean deltaMatches(ModelDelta requested, ModelDelta received) { - if ( requested.getElement().equals(received.getElement()) && - requested.getFlags() == received.getFlags() && - ( requested.getChildCount() == -1 || requested.getChildCount() == received.getChildCount() )&& - ( requested.getIndex() == -1 || requested.getIndex() == received.getIndex()) && - ((requested.getReplacementElement() != null && requested.getReplacementElement().equals(received.getReplacementElement())) || - (requested.getReplacementElement() == null && received.getReplacementElement() == null)) && - requested.getChildDeltas().length == received.getChildDeltas().length) - { - for (int i = 0; i < requested.getChildDeltas().length; i++) { - ModelDelta requestedChildDelta = (ModelDelta)requested.getChildDeltas()[i]; - ModelDelta receivedChildDelta = received.getChildDelta(requestedChildDelta.getElement()); - if ( receivedChildDelta == null || !deltaMatches(requestedChildDelta, receivedChildDelta) ) { - return false; - } - } - return true; - } - return false; - } - } diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VirtualViewerPerformanceTests.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VirtualViewerPerformanceTests.java new file mode 100644 index 000000000..2c4e6cd6b --- /dev/null +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VirtualViewerPerformanceTests.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2009 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.eclipe.debug.tests.viewer.model; + +import org.eclipse.debug.internal.ui.viewers.model.ITreeModelContentProviderTarget; +import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualTreeModelViewer; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +/** + * @since 3.6 + */ +public class VirtualViewerPerformanceTests extends PerformanceTests { + + public VirtualViewerPerformanceTests(String name) { + super(name); + } + + protected ITreeModelContentProviderTarget createViewer(Display display, Shell shell) { + return new VirtualTreeModelViewer(fDisplay, 0, new PresentationContext("TestViewer")); + } + + protected int getTestModelDepth() { + return 7; + } +} 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 34b728d04..8d60693fb 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 @@ -36,7 +36,7 @@ import org.eclipe.debug.tests.viewer.model.VirtualViewerUpdateTests; import org.eclipse.debug.tests.statushandlers.StatusHandlerTests; /** - * Tests for integration and nightly builds. + * Tests for integration and nightly builds. * * @since 3.6 */ diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/PerformanceSuite.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/PerformanceSuite.java new file mode 100644 index 000000000..98fcfaca9 --- /dev/null +++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/PerformanceSuite.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2009 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.tests; + +import junit.framework.Test; +import junit.framework.TestSuite; + +import org.eclipe.debug.tests.viewer.model.JFaceViewerPerformanceTests; +import org.eclipe.debug.tests.viewer.model.VirtualViewerPerformanceTests; + +/** + * Tests for release builds. + * + * @since 3.6 + */ +public class PerformanceSuite extends TestSuite { + + /** + * Returns the suite. This is required to use the JUnit Launcher. + * + * @return the test suite + */ + public static Test suite() { + return new PerformanceSuite(); + } + + /** + * Constructs the automated test suite. Adds all tests. + */ + public PerformanceSuite() { + // JFace viewer tests + addTest(new TestSuite(JFaceViewerPerformanceTests.class)); + + // Virtual viewer tests + addTest(new TestSuite(VirtualViewerPerformanceTests.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 eb4da116d..19b0be07a 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 @@ -52,7 +52,7 @@ class ChildrenCountUpdate extends ViewerUpdateMonitor implements IChildrenCountU 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); - getContentProvider().doRestore(getElementPath(), -1, true, true); + getContentProvider().doRestore(getElementPath(), -1, true, true, false); } public void setChildCount(int numChildren) { diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ChildrenUpdate.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ChildrenUpdate.java index bc17d93a1..21db89ddd 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ChildrenUpdate.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ChildrenUpdate.java @@ -78,10 +78,12 @@ public class ChildrenUpdate extends ViewerUpdateMonitor implements IChildrenUpda } TreePath childPath = elementPath.createChildPath(element); provider.updateHasChildren(childPath); - provider.doRestore(childPath, modelIndex, false, false); + provider.doRestore(childPath, modelIndex, false, false, false); } } } + + provider.doRestore(elementPath, -1, true, true, true); } else { provider.updateHasChildren(elementPath); } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ElementCompareRequest.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ElementCompareRequest.java index 66b44cc5f..2b3422ed2 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ElementCompareRequest.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ElementCompareRequest.java @@ -30,6 +30,7 @@ class ElementCompareRequest extends MementoUpdate implements IElementCompareRequ private ModelDelta fDelta; private boolean fKnowsHasChildren; private boolean fKnowsChildCount; + private boolean fCheckChildrenRealized; /** @@ -37,13 +38,17 @@ class ElementCompareRequest extends MementoUpdate implements IElementCompareRequ * @param element * @param memento */ - public ElementCompareRequest(ModelContentProvider provider, Object viewerInput, Object element, TreePath elementPath, IMemento memento, ModelDelta delta, int modelIndex, boolean hasChildren, boolean knowsChildCount) { + public ElementCompareRequest(ModelContentProvider provider, Object viewerInput, Object element, + TreePath elementPath, IMemento memento, ModelDelta delta, int modelIndex, + boolean hasChildren, boolean knowsChildCount, boolean checkChildrenRealized) + { super(provider, viewerInput, provider.getPresentationContext(), element, elementPath, memento); fProvider = provider; fDelta = delta; fModelIndex = modelIndex; fKnowsHasChildren = hasChildren; fKnowsChildCount = knowsChildCount; + fCheckChildrenRealized = checkChildrenRealized; } /* (non-Javadoc) @@ -96,4 +101,12 @@ class ElementCompareRequest extends MementoUpdate implements IElementCompareRequ boolean knowChildCount() { return fKnowsChildCount; } + + void setCheckChildrenRealized(boolean checkChildrenRealized) { + fCheckChildrenRealized = checkChildrenRealized; + } + + boolean checkChildrenRealized() { + return fCheckChildrenRealized; + } } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/HasChildrenUpdate.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/HasChildrenUpdate.java index be08635b3..61bb05dc6 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/HasChildrenUpdate.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/HasChildrenUpdate.java @@ -53,7 +53,7 @@ class HasChildrenUpdate extends ViewerUpdateMonitor implements IHasChildrenUpdat contentProvider.getViewer().autoExpand(elementPath); } if (elementPath.getSegmentCount() > 0) { - getContentProvider().doRestore(getElementPath(), -1, true, false); + getContentProvider().doRestore(getElementPath(), -1, true, false, false); } } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelContentProviderTarget.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelContentProviderTarget.java index 701ebca0d..dee5a1ec8 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelContentProviderTarget.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelContentProviderTarget.java @@ -206,7 +206,16 @@ public interface ITreeModelContentProviderTarget extends ITreeModelViewer { public boolean getExpandedState(Object elementOrTreePath); /** - * Returns the child count of the element at the given path. + * Returns whether the given element has children. + * + * @since 3.6 + */ + public boolean getHasChildren(Object elementOrTreePath); + + /** + * Returns the child count of the element at the given path. <br> + * Note: The child count may be incorrect if the element is not + * expanded in the tree. */ public int getChildCount(TreePath path); @@ -229,4 +238,14 @@ public interface ITreeModelContentProviderTarget extends ITreeModelViewer { */ public int findElementIndex(TreePath parentPath, Object element); + /** + * Returns a boolean indicating whether all the child elements of the + * given parent have been realized already. + * + * @param parentPath + * @return + * + * @since 3.6 + */ + public boolean getElementChildrenRealized(TreePath parentPath); } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelViewer.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelViewer.java index f834b75d6..9e6198327 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelViewer.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelViewer.java @@ -165,8 +165,13 @@ public interface ITreeModelViewer extends ISelectionProvider { * as it parses the sub-tree. * @param path Path where to start saving the state. * @param delta The delta where the state is to be saved. + * @param flagsToSave The flags to preserve during the state save. The + * supported flags are <code>IModelDelta.SELECT</code>, + * <code>IModelDelta.EXPAND</code>, <code>IModelDelta.COLLAPSE</code>. + * + * @since 3.6 */ - public void saveElementState(TreePath path, ModelDelta delta); + public void saveElementState(TreePath path, ModelDelta delta, int flagsToSave); /** * Causes the viewer to process the given delta as if it came from a 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 dd1e8e894..1a8bddf79 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 @@ -1948,6 +1948,54 @@ public class InternalTreeModelViewer extends TreeViewer .updateElement(treePath, index); } +//************************************************************************** +// Another couple of methods copied from TreeViewer to workaround the UI bug 266189. +// + protected void createChildren(Widget widget) { + Object element = widget.getData(); + if (element == null && widget instanceof TreeItem) { + // parent has not been materialized + virtualMaterializeItem((TreeItem) widget); + // try getting the element now that updateElement was called + element = widget.getData(); + } + if (element == null) { + // give up because the parent is still not materialized + return; + } + Item[] children = getChildren(widget); + if (children.length == 1 && children[0].getData() == null) { + // found a dummy node + virtualLazyUpdateChildCount(widget, children.length); + children = getChildren(widget); + } + // DO NOT touch all children + return; + } + + private void virtualMaterializeItem(TreeItem treeItem) { + if (treeItem.getData() != null) { + // already materialized + return; + } + + int index; + Widget parent = treeItem.getParentItem(); + if (parent == null) { + parent = treeItem.getParent(); + } + Object parentElement = parent.getData(); + if (parentElement != null) { + if (parent instanceof Tree) { + index = ((Tree) parent).indexOf(treeItem); + } else { + index = ((TreeItem) parent).indexOf(treeItem); + } + virtualLazyUpdateWidget(parent, index); + } + } + + /** * Performs auto expand on an element at the specified path if the auto expand * level dictates the element should be expanded. @@ -1978,6 +2026,19 @@ public class InternalTreeModelViewer extends TreeViewer return -1; } + public boolean getElementChildrenRealized(TreePath parentPath) { + Widget parentItem = findItem(parentPath); + if (parentItem != null) { + Item[] children = getChildren(parentItem); + for (int i = 0; i < children.length; i++) { + if (children[i].getData() == null) { + return false; + } + } + } + return true; + } + public Display getDisplay() { Control control = getControl(); if (control != null) { @@ -2193,7 +2254,7 @@ public class InternalTreeModelViewer extends TreeViewer return null; } - public void saveElementState(TreePath path, ModelDelta delta) { + public void saveElementState(TreePath path, ModelDelta delta, int flagsToSave) { Tree tree = (Tree) getControl(); TreeItem[] selection = tree.getSelection(); Set set = new HashSet(); @@ -2206,49 +2267,59 @@ public class InternalTreeModelViewer extends TreeViewer if (w instanceof Tree) { delta.setChildCount( ((ITreeModelContentProvider)getContentProvider()).viewToModelCount(path, tree.getItemCount())); - delta.setFlags(delta.getFlags() | IModelDelta.EXPAND); + if ((flagsToSave & IModelDelta.EXPAND) != 0) { + delta.setFlags(delta.getFlags() | IModelDelta.EXPAND); + } items = tree.getItems(); } else if (w instanceof TreeItem) { TreeItem item = (TreeItem)w; - delta.setChildCount( - ((ITreeModelContentProvider)getContentProvider()).viewToModelCount(path, item.getItemCount())); + int itemCount = item.getItemCount(); + delta.setChildCount(((ITreeModelContentProvider)getContentProvider()).viewToModelCount(path, itemCount)); if (item.getExpanded()) { - delta.setFlags(delta.getFlags() | IModelDelta.EXPAND); + if ((flagsToSave & IModelDelta.EXPAND) != 0) { + delta.setFlags(delta.getFlags() | IModelDelta.EXPAND); + } + } else if ((flagsToSave & IModelDelta.COLLAPSE) != 0 && itemCount > 0){ + delta.setFlags(delta.getFlags() | IModelDelta.COLLAPSE); } - if (set.contains(item)) { + + if (set.contains(item) && (flagsToSave & IModelDelta.SELECT) != 0) { delta.setFlags(delta.getFlags() | IModelDelta.SELECT); } items = ((TreeItem)w).getItems(); } if (items != null) { for (int i = 0; i < items.length; i++) { - doSaveElementState(path, delta, items[i], set, i); + doSaveElementState(path, delta, items[i], set, i, flagsToSave); } } } - private void doSaveElementState(TreePath parentPath, ModelDelta delta, TreeItem item, Collection set, int index) { + private void doSaveElementState(TreePath parentPath, ModelDelta delta, TreeItem item, Collection set, int index, int flagsToSave) { Object element = item.getData(); if (element != null) { boolean expanded = item.getExpanded(); boolean selected = set.contains(item); - if (expanded || selected) { - int flags = IModelDelta.NO_CHANGE; - if (expanded) { - flags = flags | IModelDelta.EXPAND; - } - if (selected) { - flags = flags | IModelDelta.SELECT; - } + int itemCount = item.getItemCount(); + int flags = IModelDelta.NO_CHANGE; + if (expanded && (flagsToSave & IModelDelta.EXPAND) != 0) { + flags = flags | IModelDelta.EXPAND; + } + if (!expanded && (flagsToSave & IModelDelta.COLLAPSE) != 0 && itemCount > 0) { + flags = flags | IModelDelta.COLLAPSE; + } + if (selected && (flagsToSave & IModelDelta.SELECT) != 0) { + flags = flags | IModelDelta.SELECT; + } + if (expanded || flags != IModelDelta.NO_CHANGE) { int modelIndex = ((ITreeModelContentProvider)getContentProvider()).viewToModelIndex(parentPath, index); TreePath elementPath = parentPath.createChildPath(element); - int numChildren = ((ITreeModelContentProvider)getContentProvider()). - viewToModelCount(elementPath, item.getItemCount()); + int numChildren = ((ITreeModelContentProvider)getContentProvider()).viewToModelCount(elementPath, itemCount); ModelDelta childDelta = delta.addNode(element, modelIndex, flags, numChildren); if (expanded) { TreeItem[] items = item.getItems(); for (int i = 0; i < items.length; i++) { - doSaveElementState(elementPath, childDelta, items[i], set, i); + doSaveElementState(elementPath, childDelta, items[i], set, i, flagsToSave); } } } @@ -2302,6 +2373,25 @@ public class InternalTreeModelViewer extends TreeViewer return false; } + public boolean getHasChildren(Object elementOrTreePath) { + if (elementOrTreePath instanceof TreePath && + ((TreePath)elementOrTreePath).getSegmentCount() == 0) + { + return getTree().getItemCount() > 0; + } + + Widget[] items = internalFindItems(elementOrTreePath); + if (items != null && items.length > 0) { + if (items[0] instanceof TreeItem) { + return ((TreeItem)items[0]).getItemCount() > 0; + } else { + return ((Tree)items[0]).getItemCount() > 0; + } + } + + return false; + } + /* * (non-Javadoc) * @see org.eclipse.jface.viewers.StructuredViewer#handleSelect(org.eclipse.swt.events.SelectionEvent) 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 2b42fb480..427d49b59 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 @@ -22,6 +22,9 @@ import java.util.Map; import java.util.Set; import java.util.Map.Entry; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; import org.eclipse.debug.internal.ui.viewers.model.VirtualItem.Index; import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation; import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentationFactory; @@ -51,6 +54,7 @@ import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IMemento; +import org.eclipse.ui.progress.UIJob; /** * A tree model viewer without a UI component. @@ -170,6 +174,8 @@ public class InternalVirtualTreeModelViewer extends Viewer */ private Map fShowColumns = new HashMap(); + private UIJob fValidateJob; + public InternalVirtualTreeModelViewer(Display display, int style, IPresentationContext context) { fDisplay = display; fContext = context; @@ -237,6 +243,10 @@ public class InternalVirtualTreeModelViewer extends Viewer //Object oldData = item.getData(); associate(element, item); doUpdate(item); + VirtualItem[] children = item.getItems(); + for (int j = 0; j < children.length; j++) { + children[j].setNeedsDataUpdate(); + } } } // Restore the selection if we are not already in a nested @@ -249,7 +259,7 @@ public class InternalVirtualTreeModelViewer extends Viewer handleInvalidSelection(selection, newSelection); } } - fTree.validate(); + validate(); } VirtualTree getTree() { @@ -268,7 +278,7 @@ public class InternalVirtualTreeModelViewer extends Viewer } else { // TODO: Implement insert() for element } - fTree.validate(); + validate(); } public void remove(final Object parentOrTreePath, final int index) { @@ -282,9 +292,18 @@ public class InternalVirtualTreeModelViewer extends Viewer VirtualItem parentItem = parentItems[i]; if (parentItem.isDisposed()) continue; + + // Parent item is not expanded so just update its contents so that + // the plus sign gets refreshed. + if (!parentItem.getExpanded()) { + parentItem.setNeedsCountUpdate(); + parentItem.setItemCount(-1); + virtualLazyUpdateHasChildren(parentItem); + } + if (index < parentItem.getItemCount()) { - VirtualItem item =parentItem.getItem(new VirtualItem.Index(index)); + if (item.getData() != null) { removedPath = getTreePathFromItem(item); disassociate(item); @@ -369,6 +388,15 @@ public class InternalVirtualTreeModelViewer extends Viewer } return -1; } + + public boolean getElementChildrenRealized(TreePath parentPath) { + VirtualItem parentItem = findItem(parentPath); + if (parentItem != null) { + return !parentItem.childrenNeedDataUpdate(); + } + return true; + } + private ITreeModelLabelProvider getLabelProvider() { return fLabelProvider; @@ -382,36 +410,72 @@ public class InternalVirtualTreeModelViewer extends Viewer public void refresh() { refresh(fTree); - getTree().validate(); + validate(); } public void refresh(Object element) { VirtualItem[] items = findItems(element); for (int i = 0; i < items.length; i++) { refresh(items[i]); - getTree().validate(items[i]); + validate(); } } private void refresh(VirtualItem item) { - item.setNeedsCountUpdate(); - item.setNeedsLabelUpdate(); + if (!item.needsDataUpdate()) { + if (item.getParent() != null) { + item.setNeedsLabelUpdate(); + virtualLazyUpdateHasChildren(item); + } + + VirtualItem[] items = item.getItems(); + for (int i = 0; i < items.length; i++) { + items[i].setNeedsDataUpdate(); + } + } + refreshStruct(item); + } + + private void refreshStruct(VirtualItem item) { + boolean expanded = false; if (item.getParent() == null) { + // root item virtualLazyUpdateChildCount(item); - } else if (item.getExpanded()) { - virtualLazyUpdateChildCount(item); - } else if (item.getData() != null) { - virtualLazyUpdateHasChildren(item); + expanded = true; + } else { + if (item.getExpanded()) { + virtualLazyUpdateData(item); + expanded = true; + } } - + VirtualItem[] items = item.getItems(); for (int i = 0; i < items.length; i++) { - items[i].setNeedsDataUpdate(); - refresh(items[i]); + if (expanded) { + refreshStruct(items[i]); + } else { + item.clear(new VirtualItem.Index(i)); + } } } - - + + private void validate() { + if (fValidateJob == null) { + fValidateJob = new UIJob(getDisplay(), "Virtual viewer validate job") { + { + setSystem(true); + } + + public IStatus runInUIThread(IProgressMonitor monitor) { + fValidateJob = null; + fTree.validate(); + return Status.OK_STATUS; + } + }; + fValidateJob.schedule(); + } + } + protected void inputChanged(Object input, Object oldInput) { resetColumns(input); } @@ -483,7 +547,7 @@ public class InternalVirtualTreeModelViewer extends Viewer } } }); - fTree.validate(); + validate(); } public void setHasChildren(final Object elementOrTreePath, final boolean hasChildren) { @@ -506,7 +570,7 @@ public class InternalVirtualTreeModelViewer extends Viewer if (hasChildren) { if (!item.getExpanded()) { item.setItemCount(-1); - } else if (item.needsCountUpdate()) { + } else { virtualLazyUpdateChildCount(item); } } @@ -514,10 +578,19 @@ public class InternalVirtualTreeModelViewer extends Viewer } }); } + + public boolean getHasChildren(Object elementOrTreePath) { + VirtualItem[] items = findItems(elementOrTreePath); + if (items.length > 0) { + return items[0].hasItems(); + } + return false; + } private void virtualLazyUpdateHasChildren(VirtualItem item) { TreePath treePath; treePath = getTreePathFromItem(item); + item.clearNeedsCountUpdate(); getContentProvider().updateHasChildren(treePath); } @@ -595,7 +668,7 @@ public class InternalVirtualTreeModelViewer extends Viewer if (item.needsLabelUpdate()) { virtualLazyUpdateLabel(item); } - if (item.getExpanded() && item.hasItems() && item.needsCountUpdate()) { + if (item.needsCountUpdate() && item.getExpanded()) { virtualLazyUpdateChildCount(item); } } @@ -723,7 +796,7 @@ public class InternalVirtualTreeModelViewer extends Viewer } // Make sure that the new selection is properly revealed. - fTree.validate(); + validate(); } public void update(Object element) { @@ -735,7 +808,7 @@ public class InternalVirtualTreeModelViewer extends Viewer public void doUpdate(VirtualItem item) { item.setNeedsLabelUpdate(); - fTree.validate(item); + validate(); } public ISelection getSelection() { @@ -784,14 +857,15 @@ public class InternalVirtualTreeModelViewer extends Viewer if (items.length > 0) { expandToLevel(items[0], level); } - fTree.validate(); + validate(); } public void setExpandedState(Object elementOrTreePath, boolean expanded) { VirtualItem[] items = findItems(elementOrTreePath); - if (items.length > 0) { - items[0].setExpanded(expanded); + for (int i = 0; i < items.length; i++) { + items[i].setExpanded(expanded); } + validate(); } public boolean getExpandedState(Object elementOrTreePath) { @@ -808,10 +882,6 @@ public class InternalVirtualTreeModelViewer extends Viewer return; } - if (item.getItemCount() < 0) { - virtualLazyUpdateChildCount(item); - item.clearNeedsCountUpdate(); - } item.setExpanded(true); if (item.getData() == null) { @@ -1220,11 +1290,17 @@ public class InternalVirtualTreeModelViewer extends Viewer } public int getChildCount(TreePath path) { + int childCount = -1; VirtualItem[] items = findItems(path); if (items.length > 0) { - return items[0].getItemCount(); + childCount = items[0].getItemCount(); + // Mimic the jface viewer behavior which returns 1 for child count + // for an item that has children but is not yet expanded. + if (childCount == -1 && items[0].hasItems()) { + childCount = 1; + } } - return -1; + return childCount; } public Object getChildElement(TreePath path, int index) { @@ -1241,7 +1317,7 @@ public class InternalVirtualTreeModelViewer extends Viewer return null; } - public void saveElementState(TreePath path, ModelDelta delta) { + public void saveElementState(TreePath path, ModelDelta delta, int flagsToSave) { VirtualTree tree = getTree(); VirtualItem[] selection = tree.getSelection(); Set set = new HashSet(); @@ -1255,32 +1331,40 @@ public class InternalVirtualTreeModelViewer extends Viewer if (parent != null) { delta.setChildCount(((ModelContentProvider)getContentProvider()).viewToModelCount(path, parent.getItemCount())); if (parent.getExpanded()) { - delta.setFlags(delta.getFlags() | IModelDelta.EXPAND); + if ((flagsToSave & IModelDelta.EXPAND) != 0) { + delta.setFlags(delta.getFlags() | IModelDelta.EXPAND); + } + } else if ((flagsToSave & IModelDelta.COLLAPSE) != 0 && parent.hasItems()){ + delta.setFlags(delta.getFlags() | IModelDelta.COLLAPSE); } - if (set.contains(parent)) { + + if (set.contains(parent) && (flagsToSave & IModelDelta.SELECT) != 0) { delta.setFlags(delta.getFlags() | IModelDelta.SELECT); } items = parent.getItems(); for (int i = 0; i < items.length; i++) { - doSaveElementState(path, delta, items[i], set); + doSaveElementState(path, delta, items[i], set, flagsToSave); } } } - private void doSaveElementState(TreePath parentPath, ModelDelta delta, VirtualItem item, Collection set) { + private void doSaveElementState(TreePath parentPath, ModelDelta delta, VirtualItem item, Collection set, int flagsToSave) { Object element = item.getData(); if (element != null) { boolean expanded = item.getExpanded(); boolean selected = set.contains(item); - if (expanded || selected) { - int flags = IModelDelta.NO_CHANGE; - if (expanded) { - flags = flags | IModelDelta.EXPAND; - } - if (selected) { - flags = flags | IModelDelta.SELECT; - } + int flags = IModelDelta.NO_CHANGE; + if (expanded && (flagsToSave & IModelDelta.EXPAND) != 0) { + flags = flags | IModelDelta.EXPAND; + } + if (!expanded && (flagsToSave & IModelDelta.COLLAPSE) != 0 && item.hasItems()){ + flags = flags | IModelDelta.COLLAPSE; + } + if (selected && (flagsToSave & IModelDelta.SELECT) != 0) { + flags = flags | IModelDelta.SELECT; + } + if (expanded || flags != IModelDelta.NO_CHANGE) { int modelIndex = ((ModelContentProvider)getContentProvider()).viewToModelIndex(parentPath, item.getIndex().intValue()); TreePath elementPath = parentPath.createChildPath(element); int numChildren = ((ModelContentProvider)getContentProvider()).viewToModelCount(elementPath, item.getItemCount()); @@ -1288,7 +1372,7 @@ public class InternalVirtualTreeModelViewer extends Viewer if (expanded) { VirtualItem[] items = item.getItems(); for (int i = 0; i < items.length; i++) { - doSaveElementState(elementPath, childDelta, items[i], set); + doSaveElementState(elementPath, childDelta, items[i], set, flagsToSave); } } } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ModelContentProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ModelContentProvider.java index 647523d10..584d22585 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ModelContentProvider.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ModelContentProvider.java @@ -158,9 +158,17 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi private Object fQueuedRestore = null; /** + * Dummy marker element used in the state delta. The marker indicates + * that a given element in the pending state delta has been removed. + * It replaces the original element so that it may optionally be + * garbage collected. + */ + private final static String ELEMENT_REMOVED = "ELEMENT_REMOVED"; //$NON-NLS-1$ + + /** * Used to determine when restoration delta has been processed */ - class CheckState implements IModelDeltaVisitor { + protected class CheckState implements IModelDeltaVisitor { private boolean complete = true; private IModelDelta topDelta = null; /* (non-Javadoc) @@ -168,6 +176,17 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi */ public boolean visit(IModelDelta delta, int depth) { if (delta.getFlags() != IModelDelta.NO_CHANGE) { + IModelDelta parentDelta = delta.getParentDelta(); + if (parentDelta != null && parentDelta.getFlags() == IModelDelta.NO_CHANGE) { + TreePath deltaPath = getViewerTreePath(delta); + if ( ((delta.getElement() instanceof IMemento) && !areMementoUpdatesPending(deltaPath.getParentPath(), delta)) || + (!(delta.getElement() instanceof IMemento) && !areElementUpdatesPending(deltaPath)) ) + { + removeDelta(delta); + return false; + } + } + if (delta.getFlags() == IModelDelta.REVEAL && !(delta.getElement() instanceof IMemento)) { topDelta = delta; } else { @@ -185,6 +204,79 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi public IModelDelta getTopItemDelta() { return topDelta; } + + private boolean areElementUpdatesPending(TreePath path) { + synchronized (fRequestsInProgress) { + TreePath parentPath = path.getParentPath(); + List requests = (List)fWaitingRequests.get(path); + if (requests != null) { + for (int i = 0; i < requests.size(); i++) { + ViewerUpdateMonitor update = (ViewerUpdateMonitor)requests.get(i); + if (update instanceof ChildrenUpdate) { + return true; + } + } + } + requests = (List)fWaitingRequests.get(parentPath); + if (requests != null) { + for (int i = 0; i < requests.size(); i++) { + ViewerUpdateMonitor update = (ViewerUpdateMonitor)requests.get(i); + if (update.getElement().equals(path.getLastSegment())) { + return true; + } + } + } + requests = (List)fRequestsInProgress.get(path); + if (requests != null) { + for (int i = 0; i < requests.size(); i++) { + ViewerUpdateMonitor update = (ViewerUpdateMonitor)requests.get(i); + if (update instanceof ChildrenUpdate) { + return true; + } + } + } + requests = (List)fRequestsInProgress.get(parentPath); + if (requests != null) { + for (int i = 0; i < requests.size(); i++) { + ViewerUpdateMonitor update = (ViewerUpdateMonitor)requests.get(i); + if (update.getElement().equals(path.getLastSegment())) { + return true; + } + } + } + } + return false; + } + + private boolean areMementoUpdatesPending(TreePath path, IModelDelta delta) { + if (fCompareRequestsInProgress.size() > 10) { + return fCompareRequestsInProgress.containsKey(new CompareRequestKey(path, delta)); + } else { + for (Iterator itr = fCompareRequestsInProgress.keySet().iterator(); itr.hasNext(); ) { + CompareRequestKey key = (CompareRequestKey)itr.next(); + if (key.fPath.equals(path) && delta.getElement().equals(key.fDelta.getElement())) { + return true; + } + } + } + return false; + } + + private void removeDelta(IModelDelta delta) { + if (DEBUG_STATE_SAVE_RESTORE && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) { + System.out.println("STATE RESTORE REMOVED: " + delta.getElement()); //$NON-NLS-1$ + } + + delta.accept(new IModelDeltaVisitor() { + public boolean visit(IModelDelta _visitorDelta, int depth) { + ModelDelta visitorDelta = (ModelDelta)_visitorDelta; + visitorDelta.setElement(ELEMENT_REMOVED); + visitorDelta.setFlags(IModelDelta.NO_CHANGE); + return true; + } + }); + + } } /** @@ -340,7 +432,7 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi public IStatus runInUIThread(IProgressMonitor monitor) { if (!isDisposed() && input.equals(getViewer().getInput())) { if (DEBUG_STATE_SAVE_RESTORE && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) { - System.out.println("RESTORE: " + stateDelta.toString()); //$NON-NLS-1$ + System.out.println("STATE RESTORE: " + stateDelta.toString()); //$NON-NLS-1$ } fViewerStates.remove(keyMementoString); fPendingState = stateDelta; @@ -388,14 +480,106 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi /** * @param delta */ - abstract void doRestore(final ModelDelta delta, boolean knowsHasChildren, boolean knowsChildCount); + abstract void doRestore(final ModelDelta delta, boolean knowsHasChildren, boolean knowsChildCount, boolean checkChildrenRealized); + + protected void appendToPendingStateDelta(TreePath path) { + if (DEBUG_STATE_SAVE_RESTORE && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) { + System.out.println("STATE APPEND BEGIN: " + path.getLastSegment()); //$NON-NLS-1$ + } + // build a model delta representing expansion and selection state + final ModelDelta appendDeltaRoot = new ModelDelta(getViewer().getInput(), IModelDelta.NO_CHANGE); + ModelDelta delta = appendDeltaRoot; + for (int i = 0; i < path.getSegmentCount(); i++) { + delta = delta.addNode(path.getSegment(i), IModelDelta.NO_CHANGE); + } + + fViewer.saveElementState(path, delta, IModelDelta.COLLAPSE | IModelDelta.EXPAND | IModelDelta.SELECT); + + // Append a marker CONTENT flag to all the delta nodes that contain the EXPAND node. These + // markers are used by the restore logic to know when a delta node can be removed. + delta.accept(new IModelDeltaVisitor() { + public boolean visit(IModelDelta delta, int depth) { + if ((delta.getFlags() & IModelDelta.EXPAND) != 0) { + ((ModelDelta)delta).setFlags(delta.getFlags() | IModelDelta.CONTENT); + } + return true; + } + }); + + if (DEBUG_STATE_SAVE_RESTORE && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) { + System.out.println("STATE APPEND DELTA FROM VIEW: " + appendDeltaRoot); //$NON-NLS-1$ + } + + if (fPendingState != null) { + // If the restore for the current input was never completed, preserve + // that restore along with the restore that was completed. + if (DEBUG_STATE_SAVE_RESTORE && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) { + System.out.println("STATE APPEND OUTSTANDING RESTORE: " + fPendingState); //$NON-NLS-1$ + } + + IModelDeltaVisitor pendingStateVisitor = new IModelDeltaVisitor() { + public boolean visit(IModelDelta pendingDeltaNode, int depth) { + // Ignore the top element. + if (pendingDeltaNode.getParentDelta() == null) { + return true; + } + + // Find the node in the save delta which is the parent + // of to the current pending delta node. + // If the parent node cannot be found, it means that + // most likely the user collapsed the parent node before + // the children were ever expanded. + // If the pending state node already exists in the parent + // node, it is already processed and we can skip it. + // If the pending state node does not contain any flags, + // we can also skip it. + ModelDelta saveDeltaNode = findSaveDelta(appendDeltaRoot, pendingDeltaNode); + if (saveDeltaNode != null && + !isDeltaInParent(pendingDeltaNode, saveDeltaNode) && + pendingDeltaNode.getFlags() != IModelDelta.NO_CHANGE) + { + saveDeltaNode.setChildCount(pendingDeltaNode.getParentDelta().getChildCount()); + copyIntoDelta(pendingDeltaNode, saveDeltaNode); + } else { + if (DEBUG_STATE_SAVE_RESTORE && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) { + System.out.println("STATE Skipping: " + pendingDeltaNode.getElement()); //$NON-NLS-1$ + } + } + + // If the pending delta node has a memento element, its + // children should also be mementos therefore the copy + // delta operation should have added all the children + // of this pending delta node into the save delta. + if (pendingDeltaNode.getElement() instanceof IMemento) { + return false; + } else { + return pendingDeltaNode.getChildCount() > 0; + } + } + }; + fPendingState.accept(pendingStateVisitor); + } + + if (appendDeltaRoot.getChildDeltas().length > 0) { + // Set the new delta root as the pending state delta. + fPendingState = appendDeltaRoot; + if (DEBUG_STATE_SAVE_RESTORE && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) { + System.out.println("STATE APPEND NEW PENDING STATE DELTA " + fPendingState); //$NON-NLS-1$ + } + } else { + if (DEBUG_STATE_SAVE_RESTORE && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) { + System.out.println("STATE APPEND CANCELED, NO DATA"); //$NON-NLS-1$ + } + } + } + /** * Perform any restoration required for the given tree path. * * @param path */ - protected synchronized void doRestore(final TreePath path, final int modelIndex, final boolean knowsHasChildren, final boolean knowsChildCount) { + protected synchronized void doRestore(final TreePath path, final int modelIndex, final boolean knowsHasChildren, final boolean knowsChildCount, final boolean checkChildrenRealized) { if (fPendingState == null) { return; } @@ -420,10 +604,13 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi // If found, just update the flags. existingRequest.setKnowsHasChildren(knowsHasChildren); existingRequest.setKnowsChildCount(knowsChildCount); + existingRequest.setCheckChildrenRealized(checkChildrenRealized); } else { // Start a new compare request ElementCompareRequest compareRequest = new ElementCompareRequest( - ModelContentProvider.this, getViewer().getInput(), potentialMatch, path, (IMemento) element, (ModelDelta)delta, modelIndex, knowsHasChildren, knowsChildCount); + ModelContentProvider.this, getViewer().getInput(), potentialMatch, path, + (IMemento) element, (ModelDelta)delta, modelIndex, + knowsHasChildren, knowsChildCount, checkChildrenRealized); fCompareRequestsInProgress.put(key, compareRequest); provider.compareElements(new IElementCompareRequest[]{ compareRequest }); } @@ -431,7 +618,7 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi } else if (element.equals(potentialMatch)) { // Element comparison already succeeded, and it matches our element. // Call restore with delta to process the delta flags. - doRestore((ModelDelta)delta, knowsHasChildren, knowsChildCount); + doRestore((ModelDelta)delta, knowsHasChildren, knowsChildCount, checkChildrenRealized); } return false; } @@ -447,7 +634,7 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi if (!request.isCanceled()) { if (request.isEqual()) { delta.setElement(request.getElement()); - doRestore(delta, request.knowsHasChildren(), request.knowChildCount()); + doRestore(delta, request.knowsHasChildren(), request.knowChildCount(), request.checkChildrenRealized()); } else if (request.getModelIndex() != -1){ // Comparison failed. // Check if the delta has a reveal flag, and if its index matches the index @@ -469,21 +656,21 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi IElementMementoProvider stateProvider = ViewerAdapterService.getMementoProvider(input); if (stateProvider != null) { if (DEBUG_STATE_SAVE_RESTORE && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) { - System.out.println("SAVE BEGIN: " + input); //$NON-NLS-1$ + System.out.println("STATE SAVE BEGIN: " + input); //$NON-NLS-1$ } // build a model delta representing expansion and selection state final ModelDelta saveDeltaRoot = new ModelDelta(input, IModelDelta.NO_CHANGE); buildViewerState(saveDeltaRoot); if (DEBUG_STATE_SAVE_RESTORE && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) { - System.out.println("SAVE DELTA FROM VIEW: " + saveDeltaRoot); //$NON-NLS-1$ + System.out.println("STATE SAVE DELTA FROM VIEW: " + saveDeltaRoot); //$NON-NLS-1$ } if (fPendingState != null) { // If the restore for the current input was never completed, preserve // that restore along with the restore that was completed. if (DEBUG_STATE_SAVE_RESTORE && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) { - System.out.println("SAVE OUTSTANDING RESTORE: " + fPendingState); //$NON-NLS-1$ + System.out.println("STATE SAVE OUTSTANDING RESTORE: " + fPendingState); //$NON-NLS-1$ } IModelDeltaVisitor pendingStateVisitor = new IModelDeltaVisitor() { @@ -520,7 +707,7 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi copyIntoDelta(pendingDeltaNode, saveDeltaNode); } else { if (DEBUG_STATE_SAVE_RESTORE && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) { - System.out.println(" Skipping: " + pendingDeltaNode.getElement()); //$NON-NLS-1$ + System.out.println("STATE Skipping: " + pendingDeltaNode.getElement()); //$NON-NLS-1$ } } @@ -543,7 +730,7 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi encodeDelta(saveDeltaRoot, stateProvider); } else { if (DEBUG_STATE_SAVE_RESTORE && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) { - System.out.println("SAVE CANCELED, NO DATA"); //$NON-NLS-1$ + System.out.println("STATE SAVE CANCELED, NO DATA"); //$NON-NLS-1$ } } } @@ -570,7 +757,7 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi deltaPath.addFirst(delta); } - // For each element in the patch of the pendingStateDelta, find the corresponding + // For each element in the path of the pendingStateDelta, find the corresponding // element in the partially restored delta being saved. Iterator itr = deltaPath.iterator(); // Skip the root element @@ -649,7 +836,7 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi DebugUIPlugin.log(e); } if (DEBUG_STATE_SAVE_RESTORE && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) { - System.out.println("SAVE COMPLETED: " + rootDelta); //$NON-NLS-1$ + System.out.println("STATE SAVE COMPLETED: " + rootDelta); //$NON-NLS-1$ } stateSaveComplete(this); } @@ -662,7 +849,7 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi } requests.clear(); if (DEBUG_STATE_SAVE_RESTORE && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) { - System.out.println("SAVE ABORTED: " + rootDelta.getElement()); //$NON-NLS-1$ + System.out.println("STATE SAVE ABORTED: " + rootDelta.getElement()); //$NON-NLS-1$ } stateSaveComplete(this); } @@ -1237,7 +1424,7 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi fPendingState.accept(state); if (state.isComplete()) { if (DEBUG_STATE_SAVE_RESTORE && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) { - System.out.println("RESTORE COMPELTE: " + fPendingState); //$NON-NLS-1$ + System.out.println("STATE RESTORE COMPELTE: " + fPendingState); //$NON-NLS-1$ } fPendingState = null; } @@ -1375,6 +1562,8 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi } } } + + /** * Returns whether this given request should be run, or should wait for parent @@ -1419,6 +1608,34 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi } } + protected boolean getElementChildrenRealized(TreePath path) { + synchronized (fRequestsInProgress) { + List requests = (List)fWaitingRequests.get(path); + if (requests != null) { + for (int i = 0; i < requests.size(); i++) { + if (requests.get(i) instanceof ChildrenUpdate) { + return false; + } + } + } + requests = (List)fRequestsInProgress.get(path); + if (requests != null) { + int numChildrenUpdateRequests = 0; + for (int i = 0; i < requests.size(); i++) { + if (requests.get(i) instanceof ChildrenUpdate) { + if (++numChildrenUpdateRequests > 1) { + return false; + } + } + } + } + } + + return getViewer().getElementChildrenRealized(path); + } + + + /** * Triggers waiting requests based on the given request that just completed. * 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 7156008c4..bc3c04cc5 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 @@ -197,6 +197,18 @@ public class SubTreeModelViewer extends TreeModelViewer { return -1; } + public boolean getHasChildren(Object elementOrTreePath) { + if (elementOrTreePath instanceof TreePath) { + TreePath path = (TreePath)elementOrTreePath; + if (path.startsWith(fRootPath, null)) { + return SubTreeModelViewer.this.getHasChildren(createSubPath(path)); + } + } else { + return SubTreeModelViewer.this.getHasChildren(elementOrTreePath); + } + return false; + } + public Object getChildElement(TreePath path, int index) { if (path.startsWith(fRootPath, null)) { return SubTreeModelViewer.this.getChildElement(createSubPath(path), index); @@ -214,6 +226,13 @@ public class SubTreeModelViewer extends TreeModelViewer { } return -1; } + + public boolean getElementChildrenRealized(TreePath parentPath) { + if (parentPath.startsWith(fRootPath, null)) { + return SubTreeModelViewer.this.getElementChildrenRealized(createSubPath(parentPath)); + } + return true; + } public void setElementData(TreePath path, int numColumns, String[] labels, ImageDescriptor[] images, FontData[] fontDatas, RGB[] foregrounds, RGB[] backgrounds) { if (path.startsWith(fRootPath, null)) { @@ -289,8 +308,8 @@ public class SubTreeModelViewer extends TreeModelViewer { SubTreeModelViewer.this.removeViewerUpdateListener(listener); } - public void saveElementState(TreePath path, ModelDelta delta) { - SubTreeModelViewer.this.saveElementState(path, delta); + public void saveElementState(TreePath path, ModelDelta delta, int deltaFlags) { + SubTreeModelViewer.this.saveElementState(path, delta, deltaFlags); } public void setAutoExpandLevel(int level) { 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 07041e627..2149b3896 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 @@ -112,7 +112,7 @@ public class TreeModelContentProvider extends ModelContentProvider implements IT getViewer().replace(parentPath, viewIndex, element); TreePath childPath = parentPath.createChildPath(element); updateHasChildren(childPath); - doRestore(childPath, modelIndex, false, false); + doRestore(childPath, modelIndex, false, false, false); } } else { if (DEBUG_CONTENT_PROVIDER && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) { @@ -134,6 +134,7 @@ public class TreeModelContentProvider extends ModelContentProvider implements IT } TreePath treePath = getViewerTreePath(delta); cancelSubtreeUpdates(treePath); + appendToPendingStateDelta(treePath); getViewer().refresh(getElement(treePath)); } @@ -413,7 +414,7 @@ public class TreeModelContentProvider extends ModelContentProvider implements IT */ protected void buildViewerState(ModelDelta delta) { ITreeModelContentProviderTarget viewer = getViewer(); - viewer.saveElementState(EMPTY_TREE_PATH, delta); + viewer.saveElementState(EMPTY_TREE_PATH, delta, IModelDelta.SELECT | IModelDelta.EXPAND); // Add memento for top item if it is mapped to an element. The reveal memento // is in its own path to avoid requesting unnecessary data when restoring it. @@ -455,7 +456,7 @@ public class TreeModelContentProvider extends ModelContentProvider implements IT for (int i = 0; i < count; i++) { Object data = getViewer().getChildElement(TreePath.EMPTY, i); if (data != null) { - doRestore(new TreePath(new Object[]{data}), i, false, false); + doRestore(new TreePath(new Object[]{data}), i, false, false, false); } } @@ -538,13 +539,26 @@ public class TreeModelContentProvider extends ModelContentProvider implements IT /** * @param delta */ - void doRestore(ModelDelta delta, boolean knowsHasChildren, boolean knowsChildCount) { + void doRestore(ModelDelta delta, boolean knowsHasChildren, boolean knowsChildCount, boolean checkChildrenRealized) { TreePath treePath = getViewerTreePath(delta); ITreeModelContentProviderTarget viewer = getViewer(); - // Attempt to expand the node only if the children are known. - if (knowsHasChildren && (delta.getFlags() & IModelDelta.EXPAND) != 0) { - viewer.expandToLevel(treePath, 1); - delta.setFlags(delta.getFlags() & ~IModelDelta.EXPAND); + + // Attempt to expand the node only if the children are known. + if (knowsHasChildren) { + if ((delta.getFlags() & IModelDelta.EXPAND) != 0) { + if (DEBUG_STATE_SAVE_RESTORE && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) { + System.out.println("STATE RESTORE EXPAND: " + treePath.getLastSegment()); //$NON-NLS-1$ + } + viewer.expandToLevel(treePath, 1); + delta.setFlags(delta.getFlags() & ~IModelDelta.EXPAND); + } + if ((delta.getFlags() & IModelDelta.COLLAPSE) != 0) { + if (DEBUG_STATE_SAVE_RESTORE && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) { + System.out.println("STATE RESTORE COLLAPSE: " + treePath.getLastSegment()); //$NON-NLS-1$ + } + getViewer().setExpandedState(treePath, false); + delta.setFlags(delta.getFlags() & ~IModelDelta.COLLAPSE); + } } if ((delta.getFlags() & IModelDelta.SELECT) != 0) { viewer.setSelection(new TreeSelection(treePath), false, false); @@ -569,6 +583,9 @@ public class TreeModelContentProvider extends ModelContentProvider implements IT TreePath parentPath = treePath.getParentPath(); int index = viewer.findElementIndex(parentPath, treePath.getLastSegment()); if (index >= 0) { + if (DEBUG_STATE_SAVE_RESTORE && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) { + System.out.println("STATE RESTORE REVEAL: " + treePath.getLastSegment()); //$NON-NLS-1$ + } viewer.reveal(parentPath, index); } } @@ -597,6 +614,19 @@ public class TreeModelContentProvider extends ModelContentProvider implements IT } } + // Some children of this element were just updated. If all its + // children are now realized, clear out any elements that still + // have flags, because they represent elements that were removed. + if ((checkChildrenRealized && getElementChildrenRealized(treePath)) || + (knowsHasChildren && !viewer.getHasChildren(treePath)) ) + { + if (DEBUG_STATE_SAVE_RESTORE && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) { + System.out.println("STATE RESTORE CONTENT: " + treePath.getLastSegment()); //$NON-NLS-1$ + } + delta.setFlags(delta.getFlags() & ~IModelDelta.CONTENT); + } + checkIfRestoreComplete(); } + } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/VirtualItem.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/VirtualItem.java index 337ef7197..e5e022e2e 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/VirtualItem.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/VirtualItem.java @@ -15,6 +15,8 @@ import java.util.Iterator; import java.util.Map; import java.util.TreeMap; +import org.eclipse.core.runtime.Assert; + /** * Virtual item, which is analogous to the SWT's tree item. * @@ -63,7 +65,7 @@ class VirtualItem { } public int compareTo(Object obj) { - return obj instanceof Index ? ((Index)obj).fIndexValue.compareTo(fIndexValue) : 0; + return obj instanceof Index ? fIndexValue.compareTo(((Index)obj).fIndexValue) : 0; } public String toString() { @@ -226,9 +228,6 @@ class VirtualItem { void setData(Object data) { fData.put(ELEMENT_DATA_KEY, data); - if (data == null) { - fNeedsDataUpdate = true; - } } Object getData () { @@ -236,10 +235,18 @@ class VirtualItem { } void setExpanded(boolean expanded) { + if (fExpanded == expanded) { + return; + } fExpanded = expanded; + + if (fExpanded && getItemCount() == -1) { + setNeedsCountUpdate(); + } + - //Assert.assrt(!fExpanded || hasItems()); - + Assert.isTrue(!fExpanded || hasItems()); + // If collapsed, make sure that all the children are collapsed as well. if (!fExpanded) { for (Iterator itr = fItems.values().iterator(); itr.hasNext();) { @@ -307,6 +314,22 @@ class VirtualItem { return item; } + boolean childrenNeedDataUpdate() { + if (getItemCount() == 0) { + return false; + } + if (fItems == null || fItems.size() != fItemCount) { + return true; + } + for (Iterator itr = fItems.values().iterator(); itr.hasNext();) { + VirtualItem child = (VirtualItem)itr.next(); + if (child.needsDataUpdate()) { + return true; + } + } + return false; + } + VirtualItem[] getItems() { return (VirtualItem[]) fItems.values().toArray(new VirtualItem[fItems.size()]); } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IModelDelta.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IModelDelta.java index 9b54befb8..18fd862ed 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IModelDelta.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IModelDelta.java @@ -116,6 +116,7 @@ public interface IModelDelta { * SELECT and REVEAL flags. * * @see IModelSelectionPolicy += * @since 3.5 */ public static int FORCE = 1 << 26; diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/ModelDelta.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/ModelDelta.java index 868cb23ab..0f900c3d1 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/ModelDelta.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/ModelDelta.java @@ -258,6 +258,9 @@ public class ModelDelta implements IModelDelta { if ((flags & IModelDelta.CONTENT) > 0) { buf.append("CONTENT | "); //$NON-NLS-1$ } + if ((flags & IModelDelta.COLLAPSE) > 0) { + buf.append("COLLAPSE | "); //$NON-NLS-1$ + } if ((flags & IModelDelta.EXPAND) > 0) { buf.append("EXPAND | "); //$NON-NLS-1$ } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchViewBreadcrumb.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchViewBreadcrumb.java index 19d35d85d..6542fab63 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchViewBreadcrumb.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchViewBreadcrumb.java @@ -390,7 +390,7 @@ public class LaunchViewBreadcrumb extends AbstractBreadcrumb implements IDebugCo fDropDownViewer.setFilters(filters); ModelDelta stateDelta = new ModelDelta(launchViewInput, IModelDelta.NO_CHANGE); - fTreeViewer.saveElementState(TreePath.EMPTY, stateDelta); + fTreeViewer.saveElementState(TreePath.EMPTY, stateDelta, IModelDelta.EXPAND | IModelDelta.SELECT); // If we do not want to expand the elements in the drop-down. // Prune the delta to only select the element in the @@ -483,7 +483,7 @@ public class LaunchViewBreadcrumb extends AbstractBreadcrumb implements IDebugCo } // Create the delta and save the drop-down viewer's state to it. - fDropDownViewer.saveElementState(TreePath.EMPTY, delta); + fDropDownViewer.saveElementState(TreePath.EMPTY, delta, IModelDelta.EXPAND | IModelDelta.SELECT); // Add the IModelDelta.FORCE flag to override the current selection in view. rootDelta.accept(new IModelDeltaVisitor(){ |