diff options
author | Pawel Piech | 2011-12-01 20:11:58 +0000 |
---|---|---|
committer | Pawel Piech | 2011-12-01 20:11:58 +0000 |
commit | 442033718d71f1bf4a24506ba2f5a065ffd25221 (patch) | |
tree | 6d5007903ef9e9ad6cccc74b31608839b418446f | |
parent | 95d71ceea2950ced3f11676734c7c019051a2c77 (diff) | |
download | eclipse.platform.debug-442033718d71f1bf4a24506ba2f5a065ffd25221.tar.gz eclipse.platform.debug-442033718d71f1bf4a24506ba2f5a065ffd25221.tar.xz eclipse.platform.debug-442033718d71f1bf4a24506ba2f5a065ffd25221.zip |
Bug 161435 - [flex] Promote asynchronous viewer framework to API -v20111201-2011
(merge flex_viewer_refactor branch into master)
100 files changed, 6854 insertions, 5712 deletions
diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/CheckTests.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/CheckTests.java index 4532a81d8..435827411 100644 --- a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/CheckTests.java +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/CheckTests.java @@ -14,8 +14,8 @@ import junit.framework.TestCase; import org.eclipe.debug.tests.viewer.model.TestModel.TestElement; import org.eclipse.core.commands.ExecutionException; -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.IInternalTreeModelViewer; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer; import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; import org.eclipse.jface.viewers.TreePath; import org.eclipse.swt.layout.FillLayout; @@ -57,7 +57,7 @@ abstract public class CheckTests extends TestCase { fShell.open (); } - abstract protected ITreeModelContentProviderTarget createViewer(Display display, Shell shell); + abstract protected IInternalTreeModelViewer createViewer(Display display, Shell shell); /** * @throws java.lang.Exception 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 4095eeff2..290f21f07 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 @@ -15,7 +15,7 @@ import junit.framework.TestCase; import org.eclipse.debug.internal.ui.DebugUIPlugin; import org.eclipse.debug.internal.ui.viewers.model.ChildrenUpdate; 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.IInternalTreeModelViewer; import org.eclipse.debug.internal.ui.viewers.model.TreeModelContentProvider; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelChangedListener; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; @@ -23,11 +23,14 @@ import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationCont import org.eclipse.debug.internal.ui.viewers.model.provisional.IStateUpdateListener; import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdateListener; import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; +import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.TreePath; import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.jface.viewers.ViewerLabel; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.widgets.Display; /** @@ -42,8 +45,8 @@ public class ChildrenUpdateTests extends TestCase { /* (non-Javadoc) * @see org.eclipse.debug.internal.ui.viewers.model.ModelContentProvider#getViewer() */ - protected ITreeModelContentProviderTarget getViewer() { - return new ITreeModelContentProviderTarget(){ + protected IInternalTreeModelViewer getViewer() { + return new IInternalTreeModelViewer(){ public void setSelection(ISelection selection) {} public void removeSelectionChangedListener(ISelectionChangedListener listener) {} @@ -113,7 +116,10 @@ public class ChildrenUpdateTests extends TestCase { public ViewerFilter[] getFilters() { return null; } - + + public void addFilter(ViewerFilter filter) {} + public void setFilters(ViewerFilter[] filters) {} + public boolean getExpandedState(Object elementOrTreePath) { return false; } @@ -143,6 +149,27 @@ public class ChildrenUpdateTests extends TestCase { public boolean getElementChildrenRealized(TreePath parentPath) { return false; } + + public boolean getElementChecked(TreePath path) { + return false; + } + + public boolean getElementGrayed(TreePath path) { + return false; + } + + public void setElementChecked(TreePath path, boolean checked, boolean grayed) { + } + + public TreePath[] getElementPaths(Object element) { + return null; + } + public void setElementData(TreePath path, int numColumns, String[] labels, ImageDescriptor[] images, + FontData[] fontDatas, RGB[] foregrounds, RGB[] backgrounds) { + } + public String[] getVisibleColumns() { + return null; + } }; } } @@ -164,23 +191,23 @@ public class ChildrenUpdateTests extends TestCase { public void testCoalesce () { Object element = new Object(); TreeModelContentProvider cp = getContentProvider(); - ChildrenUpdate update1 = new ChildrenUpdate(cp, element, TreePath.EMPTY, element, 1, null, null); - ChildrenUpdate update2 = new ChildrenUpdate(cp, element, TreePath.EMPTY, element, 2, null, null); + ChildrenUpdate update1 = new ChildrenUpdate(cp, element, TreePath.EMPTY, element, 1, null); + ChildrenUpdate update2 = new ChildrenUpdate(cp, element, TreePath.EMPTY, element, 2, null); assertTrue("Should coalesce", update1.coalesce(update2)); assertEquals("Wrong offset", 1, update1.getOffset()); assertEquals("Wrong length", 2, update1.getLength()); - update2 = new ChildrenUpdate(cp, element, TreePath.EMPTY, element, 3, null, null); + update2 = new ChildrenUpdate(cp, element, TreePath.EMPTY, element, 3, null); assertTrue("Should coalesce", update1.coalesce(update2)); assertEquals("Wrong offset", 1, update1.getOffset()); assertEquals("Wrong length", 3, update1.getLength()); - update2 = new ChildrenUpdate(cp, element, TreePath.EMPTY, element, 2, null, null); + update2 = new ChildrenUpdate(cp, element, TreePath.EMPTY, element, 2, null); assertTrue("Should coalesce", update1.coalesce(update2)); assertEquals("Wrong offset", 1, update1.getOffset()); assertEquals("Wrong length", 3, update1.getLength()); - update2 = new ChildrenUpdate(cp, element, TreePath.EMPTY, element, 5, null, null); + update2 = new ChildrenUpdate(cp, element, TreePath.EMPTY, element, 5, null); assertFalse("Should not coalesce", update1.coalesce(update2)); assertEquals("Wrong offset", 1, update1.getOffset()); assertEquals("Wrong length", 3, update1.getLength()); diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/ContentTests.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/ContentTests.java index 0725b1fd3..4eb52e6f7 100644 --- a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/ContentTests.java +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/ContentTests.java @@ -19,12 +19,12 @@ import junit.framework.TestCase; import org.eclipe.debug.tests.viewer.model.TestModel.TestElement; import org.eclipse.core.commands.ExecutionException; -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.IInternalTreeModelViewer; import org.eclipse.debug.internal.ui.viewers.model.provisional.ICheckUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer; import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; import org.eclipse.jface.viewers.TreePath; import org.eclipse.swt.layout.FillLayout; @@ -65,7 +65,7 @@ abstract public class ContentTests extends TestCase implements ITestModelUpdates fShell.open (); } - abstract protected ITreeModelContentProviderTarget createViewer(Display display, Shell shell); + abstract protected IInternalTreeModelViewer createViewer(Display display, Shell shell); /** * @throws java.lang.Exception @@ -107,6 +107,8 @@ abstract public class ContentTests extends TestCase implements ITestModelUpdates while (!fListener.isFinished()) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); model.validateData(fViewer, TreePath.EMPTY); + + Assert.assertTrue( fListener.checkCoalesced(TreePath.EMPTY, 0, 6) ); } public void testSimpleMultiLevel() throws InterruptedException { @@ -122,6 +124,8 @@ abstract public class ContentTests extends TestCase implements ITestModelUpdates while (!fListener.isFinished()) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); model.validateData(fViewer, TreePath.EMPTY); + + Assert.assertTrue( fListener.checkCoalesced(TreePath.EMPTY, 0, 3) ); } /** @@ -342,5 +346,4 @@ abstract public class ContentTests extends TestCase implements ITestModelUpdates } return expectedChildren.isEmpty(); } - } 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 d78e9367d..e53f17bdc 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 @@ -18,9 +18,9 @@ import junit.framework.TestCase; import org.eclipe.debug.tests.viewer.model.TestModel.TestElement; import org.eclipse.core.commands.ExecutionException; -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.IInternalTreeModelViewer; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer; import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ITreeSelection; @@ -61,7 +61,7 @@ abstract public class DeltaTests extends TestCase implements ITestModelUpdatesLi fShell.open (); } - abstract protected ITreeModelContentProviderTarget createViewer(Display display, Shell shell); + abstract protected IInternalTreeModelViewer createViewer(Display display, Shell shell); /** * @throws java.lang.Exception @@ -189,6 +189,38 @@ abstract public class DeltaTests extends TestCase implements ITestModelUpdatesLi model.validateData(fViewer, TreePath.EMPTY); } + public void testRefreshCoalesceStruct() throws InterruptedException { + //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer); + + // Create a single level model and add a single child to each element. + TestModel model = TestModel.simpleSingleLevel(); + TestElement[] rootChildren = model.getRootElement().getChildren(); + for (int i = 0; i < rootChildren.length; i++) { + model.setElementChildren( + new TreePath(new Object[] { rootChildren[i]} ), + new TestElement[] { new TestElement(model, i + ".1", new TestElement[0]) }); + } + + 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 ()) Thread.sleep(0); + model.validateData(fViewer, TreePath.EMPTY); + + fListener.reset(TreePath.EMPTY, model.getRootElement(), -1, false, false); + model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT)); + while (!fListener.isFinished(ALL_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE)) + if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + + model.validateData(fViewer, TreePath.EMPTY); + Assert.assertTrue( fListener.checkCoalesced(TreePath.EMPTY, 0, 6) ); + } + + public void testInsert() throws InterruptedException { //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer); @@ -378,12 +410,12 @@ abstract public class DeltaTests extends TestCase implements ITestModelUpdatesLi fListener.reset(); fListener.setFailOnRedundantUpdates(false); model.postDelta(rootDelta); - while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | CONTENT_UPDATES_COMPLETE)) + while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | CONTENT_SEQUENCE_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); // Update the elements that were added. fListener.reset(); - fListener.addUpdates((ITreeModelContentProviderTarget)fViewer, TreePath.EMPTY, model.getRootElement(), -1, ALL_UPDATES_COMPLETE); + fListener.addUpdates((IInternalTreeModelViewer)fViewer, TreePath.EMPTY, model.getRootElement(), -1, ALL_UPDATES_COMPLETE); rootDelta = new ModelDelta(model.getRootElement(), IModelDelta.CONTENT); model.getElementDelta(rootDelta, model.findElement("1.1"), true).setFlags(IModelDelta.CONTENT); model.getElementDelta(rootDelta, model.findElement("1.2"), true).setFlags(IModelDelta.CONTENT); @@ -395,9 +427,9 @@ abstract public class DeltaTests extends TestCase implements ITestModelUpdatesLi if (!fDisplay.readAndDispatch ()) Thread.sleep(0); fListener.reset(parentPath, model.getElement(parentPath), 1, false, true); - ((ITreeModelContentProviderTarget)fViewer).expandToLevel(parentPath, 1); + ((IInternalTreeModelViewer)fViewer).expandToLevel(parentPath, 1); - while (fListener.isFinished(CONTENT_UPDATES_STARTED) && !fListener.isFinished(CONTENT_UPDATES_COMPLETE)) + while (fListener.isFinished(CONTENT_SEQUENCE_STARTED) && !fListener.isFinished(CONTENT_SEQUENCE_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); model.validateData(fViewer, parentPath); @@ -421,17 +453,17 @@ abstract public class DeltaTests extends TestCase implements ITestModelUpdatesLi // Expand elment "2" TreePath parentPath = model.findElement("2"); fListener.reset(parentPath, model.getElement(parentPath), 1, false, true); - ((ITreeModelContentProviderTarget)fViewer).expandToLevel(parentPath, 1); + ((IInternalTreeModelViewer)fViewer).expandToLevel(parentPath, 1); - while (fListener.isFinished(CONTENT_UPDATES_STARTED) && !fListener.isFinished(CONTENT_UPDATES_COMPLETE)) + while (fListener.isFinished(CONTENT_SEQUENCE_STARTED) && !fListener.isFinished(CONTENT_SEQUENCE_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); // Collapse back element "2" - ((ITreeModelContentProviderTarget)fViewer).setExpandedState(parentPath, false); + ((IInternalTreeModelViewer)fViewer).setExpandedState(parentPath, false); // Update the children of element "2". fListener.reset(); - fListener.addUpdates((ITreeModelContentProviderTarget)fViewer, TreePath.EMPTY, model.getRootElement(), -1, ALL_UPDATES_COMPLETE); + fListener.addUpdates((IInternalTreeModelViewer)fViewer, TreePath.EMPTY, model.getRootElement(), -1, ALL_UPDATES_COMPLETE); ModelDelta rootDelta = new ModelDelta(model.getRootElement(), IModelDelta.CONTENT); model.getElementDelta(rootDelta, model.findElement("2.1"), true).setFlags(IModelDelta.CONTENT); model.getElementDelta(rootDelta, model.findElement("2.2"), true).setFlags(IModelDelta.CONTENT); @@ -443,9 +475,9 @@ abstract public class DeltaTests extends TestCase implements ITestModelUpdatesLi // Expand back element "2" fListener.reset(parentPath, model.getElement(parentPath), 1, false, true); - ((ITreeModelContentProviderTarget)fViewer).expandToLevel(parentPath, 1); + ((IInternalTreeModelViewer)fViewer).expandToLevel(parentPath, 1); - while (fListener.isFinished(CONTENT_UPDATES_STARTED) && !fListener.isFinished(CONTENT_UPDATES_COMPLETE)) + while (fListener.isFinished(CONTENT_SEQUENCE_STARTED) && !fListener.isFinished(CONTENT_SEQUENCE_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); model.validateData(fViewer, parentPath, true); @@ -533,7 +565,7 @@ abstract public class DeltaTests extends TestCase implements ITestModelUpdatesLi // Validate the expansion state BEFORE posting the delta. - ITreeModelContentProviderTarget contentProviderViewer = (ITreeModelContentProviderTarget)fViewer; + IInternalTreeModelViewer contentProviderViewer = (IInternalTreeModelViewer)fViewer; Assert.assertFalse(contentProviderViewer.getExpandedState(path_root_3)); Assert.assertFalse(contentProviderViewer.getExpandedState(path_root_3_2)); Assert.assertFalse(contentProviderViewer.getExpandedState(path_root_3_2_2)); @@ -588,13 +620,13 @@ abstract public class DeltaTests extends TestCase implements ITestModelUpdatesLi // Validate the expansion state BEFORE posting the delta. - ITreeModelContentProviderTarget contentProviderViewer = (ITreeModelContentProviderTarget)fViewer; + IInternalTreeModelViewer contentProviderViewer = (IInternalTreeModelViewer)fViewer; Assert.assertFalse(contentProviderViewer.getExpandedState(path_root_3)); model.postDelta(deltaRoot); while (true) { if (fListener.isFinished(MODEL_CHANGED_COMPLETE)) { - if (fListener.isFinished(CONTENT_UPDATES_COMPLETE | LABEL_UPDATES_COMPLETE) ) { + if (fListener.isFinished(CONTENT_SEQUENCE_COMPLETE | LABEL_SEQUENCE_COMPLETE) ) { break; } } diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/FilterTests.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/FilterTests.java new file mode 100644 index 000000000..46eb8e7e8 --- /dev/null +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/FilterTests.java @@ -0,0 +1,432 @@ +/******************************************************************************* + * 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 + * + * Copyright (c) 2009, 2011 Wind River Systems and others. + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipe.debug.tests.viewer.model; + +import java.util.regex.Pattern; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import org.eclipe.debug.tests.viewer.model.TestModel.TestElement; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.debug.internal.ui.viewers.model.IInternalTreeModelViewer; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewerFilter; +import org.eclipse.jface.viewers.ITreeSelection; +import org.eclipse.jface.viewers.TreePath; +import org.eclipse.jface.viewers.TreeSelection; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerFilter; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.PlatformUI; + +/** + * Tests that verify that the viewer property retrieves all the content + * from the model. + * + * @since 3.8 + */ +abstract public class FilterTests extends TestCase implements ITestModelUpdatesListenerConstants { + + Display fDisplay; + Shell fShell; + ITreeModelViewer fViewer; + TestModelUpdatesListener fListener; + + public FilterTests(String name) { + super(name); + } + + /** + * @throws java.lang.Exception + */ + protected void setUp() throws Exception { + fDisplay = PlatformUI.getWorkbench().getDisplay(); + fShell = new Shell(fDisplay); + fShell.setMaximized(true); + fShell.setLayout(new FillLayout()); + + fViewer = createViewer(fDisplay, fShell); + + fListener = new TestModelUpdatesListener(fViewer, true, true); + + fShell.open (); + } + + abstract protected IInternalTreeModelViewer createViewer(Display display, Shell shell); + + /** + * @throws java.lang.Exception + */ + protected void tearDown() throws Exception { + fListener.dispose(); + fViewer.getPresentationContext().dispose(); + + // Close the shell and exit. + fShell.close(); + while (!fShell.isDisposed()) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + } + + protected void runTest() throws Throwable { + try { + super.runTest(); + } catch (Throwable t) { + throw new ExecutionException("Test failed: " + t.getMessage() + "\n fListener = " + fListener.toString(), t); + } + } + + protected IInternalTreeModelViewer getInternalViewer() { + return (IInternalTreeModelViewer)fViewer; + } + + + class TestViewerFilter extends ViewerFilter { + + Pattern fPattern; + TestViewerFilter(String pattern) { + fPattern = Pattern.compile(pattern); + } + + + public boolean select(Viewer viewer, Object parentElement, Object element) { + if (element instanceof TestElement) { + TestElement te = (TestElement)element; + return !fPattern.matcher(te.getLabel()).find(); + } + + return true; + } + } + + class TestTMVFilter extends TreeModelViewerFilter { + Pattern fPattern; + Object fParentElement; + TestTMVFilter(String pattern, Object parentElement) { + fPattern = Pattern.compile(pattern); + fParentElement = parentElement; + } + + public boolean isApplicable(ITreeModelViewer viewer, Object parentElement) { + if (fParentElement != null) { + return fParentElement.equals(parentElement); + } + + return true; + } + + public boolean select(Viewer viewer, Object parentElement, Object element) { + if (element instanceof TestElement) { + TestElement te = (TestElement)element; + return !fPattern.matcher(te.getLabel()).find(); + } + + return true; + } + } + + public void testSimpleSingleLevel() throws InterruptedException { + TestModel model = TestModel.simpleSingleLevel(); + doTestSimpleLevel(model, new ViewerFilter[] { new TestViewerFilter("2") }); + } + + public void testSimpleSingleLevelWithTMVFilter() throws InterruptedException { + TestModel model = TestModel.simpleSingleLevel(); + doTestSimpleLevel(model, new ViewerFilter[] { new TestTMVFilter("2", model.getRootElement()) }); + } + + public void testSimpleSingleLevelWithMixedFilters() throws InterruptedException { + TestModel model = TestModel.simpleSingleLevel(); + doTestSimpleLevel(model, new ViewerFilter[] { new TestTMVFilter("2", model.getRootElement()), new TestViewerFilter("1") }); + } + + public void testSimpleMultiLevel() throws InterruptedException { + TestModel model = TestModel.simpleMultiLevel(); + doTestSimpleLevel(model, new ViewerFilter[] { new TestViewerFilter(".1"), new TestViewerFilter(".2") }); + } + + public void testSimpleMultiLevelWithTMVFilter() throws InterruptedException { + TestModel model = TestModel.simpleMultiLevel(); + doTestSimpleLevel(model, new ViewerFilter[] { new TestTMVFilter(".1", null), new TestTMVFilter(".2", null) }); + } + + public void testSimpleMultiLevelWithMixedFilters() throws InterruptedException { + TestModel model = TestModel.simpleMultiLevel(); + doTestSimpleLevel(model, new ViewerFilter[] { new TestViewerFilter(".1"), new TestTMVFilter(".2", null) }); + } + + private void doTestSimpleLevel(TestModel model, ViewerFilter[] filters) throws InterruptedException { + + // Make sure that all elements are expanded + fViewer.setAutoExpandLevel(-1); + + fViewer.setFilters(filters); + + // Create the listener which determines when the view is finished updating. + // fListener.reset(TreePath.EMPTY, model.getRootElement(), filters, -1, false, false); + fListener.reset(TreePath.EMPTY, model.getRootElement(), filters, -1, true, true); + + // Set the viewer input (and trigger updates). + fViewer.setInput(model.getRootElement()); + + // Wait for the updates to complete. + while (!fListener.isFinished(ALL_UPDATES_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + + model.validateData(fViewer, TreePath.EMPTY, false, filters); + } + + public void testLargeSingleLevel() throws InterruptedException { + doTestLargeSingleLevel(new ViewerFilter[] { new TestViewerFilter("2") }); + } + + public void testLargeSingleLevelWithTMVFilter() throws InterruptedException { + doTestLargeSingleLevel(new ViewerFilter[] { new TestTMVFilter("2", null) }); + } + + private void doTestLargeSingleLevel(ViewerFilter[] filters) throws InterruptedException { + TestModel model = new TestModel(); + model.setRoot( new TestElement(model, "root", new TestElement[0] ) ); + model.setElementChildren(TreePath.EMPTY, TestModel.makeSingleLevelModelElements(model, 3000, "model.")); + + // Set filters + fViewer.setFilters(filters); + + fListener.setFailOnRedundantUpdates(false); + //fListener.setFailOnMultipleLabelUpdateSequences(false); + fListener.reset(); + + fViewer.setInput(model.getRootElement()); + + while (!fListener.isFinished(ALL_UPDATES_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + } + + + /** + * Replace an element that is not visible but filtered out. With an element that is NOT filtered out. + * Fire REPLACE delta. + */ + public void testReplacedUnrealizedFilteredElement() throws InterruptedException { + doTestReplacedUnrealizedFilteredElement(new ViewerFilter[] { new TestViewerFilter("2") }); + } + + + /** + * Replace an element that is not visible but filtered out. With an element that is NOT filtered out. + * Fire REPLACE delta. + */ + public void testReplacedUnrealizedFilteredElementWithTMVFilter() throws InterruptedException { + doTestReplacedUnrealizedFilteredElement(new ViewerFilter[] { new TestTMVFilter("2", null) }); + } + + private void doTestReplacedUnrealizedFilteredElement(ViewerFilter[] filters) throws InterruptedException { + + // Populate a view with a large model (only first 100 elements will be visible in virtual viewer). + TestModel model = new TestModel(); + model.setRoot( new TestElement(model, "root", new TestElement[0] ) ); + model.setElementChildren(TreePath.EMPTY, TestModel.makeSingleLevelModelElements(model, 300, "model.")); + + fViewer.setFilters(filters); + + fListener.setFailOnRedundantUpdates(false); + fListener.reset(); + + // Populate the view (all elements containing a "2" will be filtered out. + fViewer.setInput(model.getRootElement()); + + while (!fListener.isFinished(ALL_UPDATES_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + + // Switch out element "201" which is filtered out, with a "replaced element" which should NOT be + // filtered out. + TestElement replacedElement = new TestElement(model, "replaced element", new TestElement[0]); + IModelDelta replaceDelta = model.replaceElementChild(TreePath.EMPTY, 200, replacedElement); + fListener.reset(); + model.postDelta(replaceDelta); + while (!fListener.isFinished(MODEL_CHANGED_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + + // Reposition the viewer to make element 100 the top element, making the replaced element visible. + fListener.reset(); + ((IInternalTreeModelViewer) fViewer).reveal(TreePath.EMPTY, 150); + while (!fListener.isFinished(ALL_UPDATES_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + + // Verify that the replaced element is in viewer now (i.e. it's not filtered out. + TreePath[] replacedElementPaths = fViewer.getElementPaths(replacedElement); + Assert.assertTrue(replacedElementPaths.length != 0); + } + + + public void testRefreshUnrealizedFilteredElement() throws InterruptedException { + doTestRefreshUnrealizedFilteredElement(new ViewerFilter[] { new TestViewerFilter("2") }); + } + + public void testRefreshUnrealizedFilteredElementWithTMVFilter() throws InterruptedException { + doTestRefreshUnrealizedFilteredElement(new ViewerFilter[] { new TestTMVFilter("2", null) }); + } + + /** + * Replace an element that is not visible but filtered out. With an element that is NOT filtered out. + * Fire CONTENT delta on parent. + */ + private void doTestRefreshUnrealizedFilteredElement(ViewerFilter[] filters) throws InterruptedException { + // Populate a view with a large model (only first 100 elements will be visible in virtual viewer). + TestModel model = new TestModel(); + model.setRoot( new TestElement(model, "root", new TestElement[0] ) ); + model.setElementChildren(TreePath.EMPTY, TestModel.makeSingleLevelModelElements(model, 300, "model.")); + + fViewer.setFilters(filters); + + fListener.setFailOnRedundantUpdates(false); + fListener.reset(); + + // Populate the view (all elements containing a "2" will be filtered out. + fViewer.setInput(model.getRootElement()); + + while (!fListener.isFinished(ALL_UPDATES_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + + // Switch out element "201" which is filtered out, with a "replaced element" which should NOT be + // filtered out. + TestElement replacedElement = new TestElement(model, "replaced element", new TestElement[0]); + model.replaceElementChild(TreePath.EMPTY, 200, replacedElement); + fListener.reset(); + model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT)); + while (!fListener.isFinished(ALL_UPDATES_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + + // Reposition the viewer to make element 100 the top element, making the replaced element visible. + fListener.reset(); + ((IInternalTreeModelViewer) fViewer).reveal(TreePath.EMPTY, 150); + while (!fListener.isFinished(ALL_UPDATES_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + + // Verify that the replaced element is in viewer now (i.e. it's not filtered out. + TreePath[] replacedElementPaths = fViewer.getElementPaths(replacedElement); + Assert.assertTrue(replacedElementPaths.length != 0); + } + + public void testRefreshToUnfilterElements() throws InterruptedException { + doTestRefreshToUnfilterElements(new ViewerFilter[] { new TestViewerFilter(".1"), new TestViewerFilter(".2") }); + } + + public void testRefreshToUnfilterElementsWithTMVFilter() throws InterruptedException { + doTestRefreshToUnfilterElements(new ViewerFilter[] { new TestTMVFilter(".1", null), new TestTMVFilter(".2", null) }); + } + + public void testRefreshToUnfilterElementsWithMixedFilters() throws InterruptedException { + doTestRefreshToUnfilterElements(new ViewerFilter[] { new TestViewerFilter(".1"), new TestTMVFilter(".2", null) }); + } + + /** + * Replace an element that is not visible but filtered out. With an element that is NOT filtered out. + * Fire CONTENT delta on parent. + */ + private void doTestRefreshToUnfilterElements(ViewerFilter[] filters) throws InterruptedException { + // Populate a view with a large model (only first 100 elements will be visible in virtual viewer). + TestModel model = TestModel.simpleMultiLevel(); + + fViewer.setFilters(filters); + + fListener.setFailOnRedundantUpdates(false); + fListener.reset(); + + // Make sure that all elements are expanded + fViewer.setAutoExpandLevel(-1); + + // Populate the view (all elements containing a "2" will be filtered out. + fViewer.setInput(model.getRootElement()); + + while (!fListener.isFinished(ALL_UPDATES_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + + // Turn off filters and refresh. + filters = new ViewerFilter[0]; + fViewer.setFilters(filters); + fListener.reset(); + model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT)); + while (!fListener.isFinished(ALL_UPDATES_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + + model.validateData(fViewer, TreePath.EMPTY, false, filters); + } + + public void testPreserveExpandedOnMultLevelContent() throws InterruptedException { + //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer); + TestModel model = StateTests.alternatingSubsreesModel(6); + + // NOTE: WE ARE NOT EXPANDING ANY CHILDREN + + // Create the listener, only check the first level + fListener.reset(TreePath.EMPTY, model.getRootElement(), 1, true, false); + + // Set the input into the view and update the view. + fViewer.setInput(model.getRootElement()); + while (!fListener.isFinished()) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + model.validateData(fViewer, TreePath.EMPTY, true); + + StateTests.expandAlternateElements(fListener, model, true); + + // Set a selection in view + // Set a selection in view + TreeSelection originalSelection = new TreeSelection( + new TreePath[] { model.findElement("5"), model.findElement("5.1"), model.findElement("6") }); + fViewer.setSelection(originalSelection); + Assert.assertTrue( StateTests.areTreeSelectionsEqual(originalSelection, (ITreeSelection)fViewer.getSelection()) ); + + // Set a filter to remove element "1" + ViewerFilter[] filters = new ViewerFilter[] { new TestViewerFilter("^1$") }; + fViewer.setFilters(filters); + + // Note: Re-expanding nodes causes redundant updates. + fListener.reset(false, false); + fListener.addUpdates(getInternalViewer(), TreePath.EMPTY, model.getRootElement(), filters, -1, ALL_UPDATES_COMPLETE); + + // Post the refresh delta + model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT)); + while (!fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE)) + if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + + // Validate data + model.validateData(fViewer, TreePath.EMPTY, true, filters); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("2")) == false); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("3")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("3.1")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("4")) == false); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("5")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("5.1")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("6")) == false); + Assert.assertTrue( StateTests.areTreeSelectionsEqual(originalSelection, (ITreeSelection)fViewer.getSelection()) ); + + // Note: in past it was observed sub-optimal coalescing in this test due + // to scattered update requests from viewer. + Assert.assertTrue( fListener.checkCoalesced(TreePath.EMPTY, 0, 6) ); + + // Clear the filter, to re-add the element + filters = new ViewerFilter[0]; + fViewer.setFilters(filters); + + // Refresh again to get the filtered element back + fListener.reset(); + fListener.addUpdates(getInternalViewer(), TreePath.EMPTY, model.getRootElement(), filters, -1, ALL_UPDATES_COMPLETE); + model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT)); + while (!fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE)) + if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + + // Validate data + model.validateData(fViewer, TreePath.EMPTY, true, filters); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("2")) == false); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("3")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("3.1")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("4")) == false); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("5")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("5.1")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("6")) == false); + Assert.assertTrue( StateTests.areTreeSelectionsEqual(originalSelection, (ITreeSelection)fViewer.getSelection()) ); + + } + +} diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/ITestModelUpdatesListenerConstants.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/ITestModelUpdatesListenerConstants.java index 0d47d6ed5..4437d40e1 100644 --- 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 @@ -17,11 +17,11 @@ package org.eclipe.debug.tests.viewer.model; */ public interface ITestModelUpdatesListenerConstants { - public static final int LABEL_UPDATES_COMPLETE = 0X00000001; - public static final int CONTENT_UPDATES_COMPLETE = 0X00000002; - public static final int CONTENT_UPDATES_STARTED = 0X00020000; + public static final int LABEL_SEQUENCE_COMPLETE = 0X00000001; + public static final int CONTENT_SEQUENCE_COMPLETE = 0X00000002; + public static final int CONTENT_SEQUENCE_STARTED = 0X00020000; public static final int LABEL_UPDATES = 0X00000004; - public static final int LABEL_UPDATES_STARTED = 0X00040000; + public static final int LABEL_SEQUENCE_STARTED = 0X00040000; public static final int HAS_CHILDREN_UPDATES = 0X00000008; public static final int HAS_CHILDREN_UPDATES_STARTED = 0X00080000; public static final int CHILD_COUNT_UPDATES = 0X00000010; @@ -40,11 +40,11 @@ public interface ITestModelUpdatesListenerConstants { public static final int VIEWER_UPDATES_RUNNING = 0X00001000; public static final int LABEL_UPDATES_RUNNING = 0X00002000; - public static final int VIEWER_UPDATES_STARTED = HAS_CHILDREN_UPDATES_STARTED | CHILD_COUNT_UPDATES_STARTED | CHILDREN_UPDATES_STARTED; + public static final int ALL_VIEWER_UPDATES_STARTED = HAS_CHILDREN_UPDATES_STARTED | CHILD_COUNT_UPDATES_STARTED | CHILDREN_UPDATES_STARTED; - public static final int LABEL_COMPLETE = LABEL_UPDATES_COMPLETE | LABEL_UPDATES | LABEL_UPDATES_RUNNING; + public static final int LABEL_COMPLETE = LABEL_SEQUENCE_COMPLETE | LABEL_UPDATES | LABEL_UPDATES_RUNNING; public static final int CONTENT_COMPLETE = - CONTENT_UPDATES_COMPLETE | HAS_CHILDREN_UPDATES | CHILD_COUNT_UPDATES | CHILDREN_UPDATES | VIEWER_UPDATES_RUNNING; + CONTENT_SEQUENCE_COMPLETE | HAS_CHILDREN_UPDATES | CHILD_COUNT_UPDATES | CHILDREN_UPDATES | VIEWER_UPDATES_RUNNING; 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/JFaceViewerCheckTests.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerCheckTests.java index 7cb15a0ee..d596f1443 100644 --- a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerCheckTests.java +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerCheckTests.java @@ -10,7 +10,7 @@ *******************************************************************************/ package org.eclipe.debug.tests.viewer.model; -import org.eclipse.debug.internal.ui.viewers.model.ITreeModelContentProviderTarget; +import org.eclipse.debug.internal.ui.viewers.model.IInternalTreeModelViewer; 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; @@ -26,7 +26,7 @@ public class JFaceViewerCheckTests extends CheckTests { super(name); } - protected ITreeModelContentProviderTarget createViewer(Display display, Shell shell) { + protected IInternalTreeModelViewer createViewer(Display display, Shell shell) { return new TreeModelViewer(fShell, SWT.VIRTUAL | SWT.CHECK, new PresentationContext("TestViewer")); } } diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerContentTests.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerContentTests.java index 10213a3a3..29b127d2a 100644 --- a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerContentTests.java +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerContentTests.java @@ -10,7 +10,7 @@ *******************************************************************************/ package org.eclipe.debug.tests.viewer.model; -import org.eclipse.debug.internal.ui.viewers.model.ITreeModelContentProviderTarget; +import org.eclipse.debug.internal.ui.viewers.model.IInternalTreeModelViewer; 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; @@ -26,7 +26,7 @@ public class JFaceViewerContentTests extends ContentTests { super(name); } - protected ITreeModelContentProviderTarget createViewer(Display display, Shell shell) { + protected IInternalTreeModelViewer createViewer(Display display, Shell shell) { return new TreeModelViewer(fShell, SWT.VIRTUAL, new PresentationContext("TestViewer")); } } 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 ed613bd6f..c25961b3e 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 @@ -10,7 +10,7 @@ *******************************************************************************/ package org.eclipe.debug.tests.viewer.model; -import org.eclipse.debug.internal.ui.viewers.model.ITreeModelContentProviderTarget; +import org.eclipse.debug.internal.ui.viewers.model.IInternalTreeModelViewer; 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; @@ -26,7 +26,7 @@ public class JFaceViewerDeltaTests extends DeltaTests { super(name); } - protected ITreeModelContentProviderTarget createViewer(Display display, Shell shell) { + protected IInternalTreeModelViewer createViewer(Display display, Shell shell) { return new TreeModelViewer(fShell, SWT.VIRTUAL, new PresentationContext("TestViewer")); } diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerFilterTests.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerFilterTests.java new file mode 100644 index 000000000..853d2e7c1 --- /dev/null +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerFilterTests.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipe.debug.tests.viewer.model; + +import org.eclipse.debug.internal.ui.viewers.model.IInternalTreeModelViewer; +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.8 + */ +public class JFaceViewerFilterTests extends FilterTests { + + public JFaceViewerFilterTests(String name) { + super(name); + } + + protected IInternalTreeModelViewer createViewer(Display display, Shell shell) { + return new TreeModelViewer(fShell, SWT.VIRTUAL | SWT.MULTI, new PresentationContext("TestViewer")); + } +} diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerLazyTests.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerLazyTests.java index fa6183d61..e1955a726 100644 --- a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerLazyTests.java +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerLazyTests.java @@ -10,7 +10,7 @@ *******************************************************************************/ package org.eclipe.debug.tests.viewer.model; -import org.eclipse.debug.internal.ui.viewers.model.ITreeModelContentProviderTarget; +import org.eclipse.debug.internal.ui.viewers.model.IInternalTreeModelViewer; 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; @@ -26,7 +26,7 @@ public class JFaceViewerLazyTests extends LazyTests { super(name); } - protected ITreeModelContentProviderTarget createViewer(Display display, Shell shell) { + protected IInternalTreeModelViewer createViewer(Display display, Shell shell) { return new TreeModelViewer(fShell, SWT.VIRTUAL, new PresentationContext("TestViewer")); } } 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 index 865db9508..4b3bdc06f 100644 --- 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 @@ -10,7 +10,7 @@ *******************************************************************************/ package org.eclipe.debug.tests.viewer.model; -import org.eclipse.debug.internal.ui.viewers.model.ITreeModelContentProviderTarget; +import org.eclipse.debug.internal.ui.viewers.model.IInternalTreeModelViewer; 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; @@ -26,7 +26,7 @@ public class JFaceViewerPerformanceTests extends PerformanceTests { super(name); } - protected ITreeModelContentProviderTarget createViewer(Display display, Shell shell) { + protected IInternalTreeModelViewer createViewer(Display display, Shell shell) { return new TreeModelViewer(fShell, SWT.VIRTUAL, new PresentationContext("TestViewer")); } diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerPopupTests.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerPopupTests.java index 70f41bd3c..43954104d 100644 --- a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerPopupTests.java +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerPopupTests.java @@ -10,7 +10,7 @@ *******************************************************************************/ package org.eclipe.debug.tests.viewer.model; -import org.eclipse.debug.internal.ui.viewers.model.ITreeModelViewer; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer; 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; diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerSelectionTests.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerSelectionTests.java index 6cde1fbfc..511c497d7 100644 --- a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerSelectionTests.java +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerSelectionTests.java @@ -10,7 +10,7 @@ *******************************************************************************/ package org.eclipe.debug.tests.viewer.model; -import org.eclipse.debug.internal.ui.viewers.model.ITreeModelViewer; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer; 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; diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerStateTests.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerStateTests.java index 07104a79b..49fcb40bd 100644 --- a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerStateTests.java +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerStateTests.java @@ -10,7 +10,7 @@ *******************************************************************************/ package org.eclipe.debug.tests.viewer.model; -import org.eclipse.debug.internal.ui.viewers.model.ITreeModelViewer; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer; 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; diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerTopIndexTests.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerTopIndexTests.java index 668612860..51597c8a8 100644 --- a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerTopIndexTests.java +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerTopIndexTests.java @@ -17,7 +17,7 @@ import junit.framework.TestCase; import org.eclipe.debug.tests.viewer.model.TestModel.TestElement; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.runtime.Platform; -import org.eclipse.debug.internal.ui.viewers.model.ITreeModelContentProviderTarget; +import org.eclipse.debug.internal.ui.viewers.model.IInternalTreeModelViewer; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDeltaVisitor; import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; @@ -80,7 +80,7 @@ public class JFaceViewerTopIndexTests extends TestCase implements ITestModelUpda } } - protected ITreeModelContentProviderTarget getCTargetViewer() { + protected IInternalTreeModelViewer getCTargetViewer() { return fViewer; } @@ -211,7 +211,7 @@ public class JFaceViewerTopIndexTests extends TestCase implements ITestModelUpda model.postDelta(rootDelta); - while (!fListener.isFinished(CONTENT_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE)) + while (!fListener.isFinished(CONTENT_SEQUENCE_COMPLETE | MODEL_CHANGED_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); // Validate that the first node is expanded @@ -379,7 +379,7 @@ public class JFaceViewerTopIndexTests extends TestCase implements ITestModelUpda // Wait for the second model delta to process fListener.reset(); model.postDelta(revealDelta); - while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | CONTENT_UPDATES_COMPLETE)) + while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | CONTENT_SEQUENCE_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); // Clear view then reset it again. @@ -512,7 +512,7 @@ public class JFaceViewerTopIndexTests extends TestCase implements ITestModelUpda fListener.reset(false, false); fListener.addUpdates(getCTargetViewer(), originalTopPath, (TestElement)originalTopPath.getLastSegment(), 0, STATE_UPDATES); fViewer.setInput(model.getRootElement()); - while (!fListener.isFinished(STATE_UPDATES | CONTENT_UPDATES_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + while (!fListener.isFinished(STATE_UPDATES | CONTENT_SEQUENCE_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); while (fDisplay.readAndDispatch ()) {} // check if REVEAL was restored OK diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerUpdateTests.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerUpdateTests.java index 983b8d77f..ee06a4b46 100644 --- a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerUpdateTests.java +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerUpdateTests.java @@ -10,7 +10,7 @@ *******************************************************************************/ package org.eclipe.debug.tests.viewer.model; -import org.eclipse.debug.internal.ui.viewers.model.ITreeModelViewer; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer; 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; diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/LazyTests.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/LazyTests.java index f6566d9bc..986868bee 100644 --- a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/LazyTests.java +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/LazyTests.java @@ -15,7 +15,7 @@ import junit.framework.TestCase; import org.eclipe.debug.tests.viewer.model.TestModel.TestElement; import org.eclipse.core.commands.ExecutionException; -import org.eclipse.debug.internal.ui.viewers.model.ITreeModelContentProviderTarget; +import org.eclipse.debug.internal.ui.viewers.model.IInternalTreeModelViewer; 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.IStructuredSelection; @@ -36,7 +36,7 @@ abstract public class LazyTests extends TestCase implements ITestModelUpdatesLis Display fDisplay; Shell fShell; - ITreeModelContentProviderTarget fViewer; + IInternalTreeModelViewer fViewer; TestModelUpdatesListener fListener; public LazyTests(String name) { @@ -59,7 +59,7 @@ abstract public class LazyTests extends TestCase implements ITestModelUpdatesLis fShell.open (); } - abstract protected ITreeModelContentProviderTarget createViewer(Display display, Shell shell); + abstract protected IInternalTreeModelViewer createViewer(Display display, Shell shell); /** * @throws java.lang.Exception */ @@ -140,7 +140,7 @@ abstract public class LazyTests extends TestCase implements ITestModelUpdatesLis } model.postDelta(rootDelta); - while (!fListener.isFinished(CONTENT_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE | LABEL_UPDATES_COMPLETE)) + while (!fListener.isFinished(CONTENT_SEQUENCE_COMPLETE | MODEL_CHANGED_COMPLETE | LABEL_SEQUENCE_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); } @@ -165,7 +165,7 @@ abstract public class LazyTests extends TestCase implements ITestModelUpdatesLis fListener.setFailOnRedundantUpdates(false); fViewer.setInput(model.getRootElement()); fListener.addLabelUpdate(model.findElement("1.0")); - while (!fListener.isFinished(CONTENT_UPDATES_COMPLETE | LABEL_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + while (!fListener.isFinished(CONTENT_SEQUENCE_COMPLETE | LABEL_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); // Set selection so that the initial selection is not empty fViewer.setSelection(new TreeSelection(new TreePath[] { model.findElement("1.0")} )); @@ -209,7 +209,7 @@ abstract public class LazyTests extends TestCase implements ITestModelUpdatesLis // Populate initial view content fListener.reset(TreePath.EMPTY, model.getRootElement(), -1, true, true); fViewer.setInput(model.getRootElement()); - while (!fListener.isFinished(CONTENT_UPDATES_COMPLETE | LABEL_UPDATES_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + while (!fListener.isFinished(CONTENT_SEQUENCE_COMPLETE | LABEL_SEQUENCE_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); // Turn off autoexpand fViewer.setAutoExpandLevel(0); @@ -218,7 +218,7 @@ abstract public class LazyTests extends TestCase implements ITestModelUpdatesLis fListener.reset(); fListener.setFailOnRedundantUpdates(false); fViewer.reveal(model.findElement("1"), 500); - while (!fListener.isFinished(CONTENT_UPDATES_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + while (!fListener.isFinished(CONTENT_SEQUENCE_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); // Create delta to refresh the "1" element. TestElement rootElement = model.getRootElement(); @@ -248,7 +248,7 @@ abstract public class LazyTests extends TestCase implements ITestModelUpdatesLis } model.postDelta(rootDelta); - while (!fListener.isFinished(CONTENT_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE)) + while (!fListener.isFinished(CONTENT_SEQUENCE_COMPLETE | MODEL_CHANGED_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); } } diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/PerformanceTests.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/PerformanceTests.java index 55a140e2c..2a5b06759 100644 --- a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/PerformanceTests.java +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/PerformanceTests.java @@ -14,11 +14,14 @@ import junit.framework.TestCase; import org.eclipe.debug.tests.viewer.model.TestModel.TestElement; import org.eclipse.core.commands.ExecutionException; -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.IInternalTreeModelViewer; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer; import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; import org.eclipse.jface.viewers.TreePath; +import org.eclipse.jface.viewers.TreeSelection; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; @@ -55,7 +58,7 @@ abstract public class PerformanceTests extends TestCase implements ITestModelUpd fShell.open (); } - abstract protected ITreeModelContentProviderTarget createViewer(Display display, Shell shell); + abstract protected IInternalTreeModelViewer createViewer(Display display, Shell shell); /** * @throws java.lang.Exception @@ -87,7 +90,7 @@ abstract public class PerformanceTests extends TestCase implements ITestModelUpd public void testRefreshStruct() throws InterruptedException { TestModel model = new TestModel(); model.setRoot( new TestElement(model, "root", new TestElement[0] ) ); - model.setElementChildren(TreePath.EMPTY, makeModelElements(model, getTestModelDepth(), "model")); + model.setElementChildren(TreePath.EMPTY, TestModel.makeMultiLevelElements(model, getTestModelDepth(), "model.")); fViewer.setAutoExpandLevel(-1); @@ -105,7 +108,6 @@ abstract public class PerformanceTests extends TestCase implements ITestModelUpd 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); @@ -114,7 +116,6 @@ abstract public class PerformanceTests extends TestCase implements ITestModelUpd model.postDelta(new ModelDelta(element, IModelDelta.CONTENT)); while (!fListener.isFinished(ALL_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); - model.validateData(fViewer, TreePath.EMPTY); meter.stop(); System.gc(); } @@ -129,7 +130,7 @@ abstract public class PerformanceTests extends TestCase implements ITestModelUpd public void testRefreshStructReplaceElements() throws InterruptedException { TestModel model = new TestModel(); model.setRoot( new TestElement(model, "root", new TestElement[0] ) ); - model.setElementChildren(TreePath.EMPTY, makeModelElements(model, getTestModelDepth(), "model")); + model.setElementChildren(TreePath.EMPTY, TestModel.makeMultiLevelElements(model, getTestModelDepth(), "model.")); fViewer.setAutoExpandLevel(-1); @@ -144,9 +145,9 @@ abstract public class PerformanceTests extends TestCase implements ITestModelUpd Performance perf = Performance.getDefault(); PerformanceMeter meter = perf.createPerformanceMeter(perf.getDefaultScenarioId(this)); try { - for (int i = 0; i < 2000; i++) { + for (int i = 0; i < 100; i++) { // Update the model - model.setElementChildren(TreePath.EMPTY, makeModelElements(model, getTestModelDepth(), "pass " + i)); + model.setElementChildren(TreePath.EMPTY, TestModel.makeMultiLevelElements(model, getTestModelDepth(), "pass " + i + ".")); TestElement element = model.getRootElement(); fListener.reset(TreePath.EMPTY, element, -1, false, false); @@ -155,7 +156,6 @@ abstract public class PerformanceTests extends TestCase implements ITestModelUpd model.postDelta(new ModelDelta(element, IModelDelta.CONTENT)); while (!fListener.isFinished(ALL_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); - model.validateData(fViewer, TreePath.EMPTY); meter.stop(); System.gc(); } @@ -167,12 +167,156 @@ abstract public class PerformanceTests extends TestCase implements ITestModelUpd } } - 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)); + + public void testRefreshList() throws InterruptedException { + TestModel model = new TestModel(); + model.setRoot( new TestElement(model, "root", new TestElement[0] ) ); + int numElements = (int)Math.pow(2, getTestModelDepth()); + model.setElementChildren(TreePath.EMPTY, TestModel.makeSingleLevelModelElements(model, numElements, "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 ()) Thread.sleep(0); + model.validateData(fViewer, TreePath.EMPTY); + + Performance perf = Performance.getDefault(); + PerformanceMeter meter = perf.createPerformanceMeter(perf.getDefaultScenarioId(this)); + try { + for (int i = 0; i < 100; i++) { + // Update the model + model.setAllAppendix(" - pass " + i); + + TestElement element = model.getRootElement(); + fListener.reset(TreePath.EMPTY, element, -1, false, false); + + meter.start(); + model.postDelta(new ModelDelta(element, IModelDelta.CONTENT)); + while (!fListener.isFinished(ALL_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE)) + if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + meter.stop(); + System.gc(); + } + + meter.commit(); + perf.assertPerformance(meter); + } finally { + meter.dispose(); } - return elements; } + + public void testSaveAndRestore() throws InterruptedException { + //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer); + TestModel model = TestModel.simpleMultiLevel(); + + // expand all elements + fViewer.setAutoExpandLevel(-1); + + // Create the listener, only check the first level + fListener.reset(TreePath.EMPTY, model.getRootElement(), -1, true, false); + + // Set the input into the view and update the view. + fViewer.setInput(model.getRootElement()); + while (!fListener.isFinished()) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + model.validateData(fViewer, TreePath.EMPTY); + + // Set a selection in view + fViewer.setSelection(new TreeSelection(model.findElement("3.2.3"))); + + // Turn off the auto-expand now since we want to text the auto-expand logic + fViewer.setAutoExpandLevel(-1); + + 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); + + // Set the viewer input to null. This will trigger the view to save the viewer state. + fListener.reset(true, false); + + meter.start(); + fViewer.setInput(null); + while (!fListener.isFinished(STATE_SAVE_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + + // Set the viewer input back to the model. When view updates are complete + // the viewer + // Note: disable redundant updates because the reveal delta triggers one. + fListener.reset(TreePath.EMPTY, model.getRootElement(), 1, false, false); + // TODO: add state updates somehow? + fViewer.setInput(model.getRootElement()); + while (!fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + meter.stop(); + System.gc(); + } + + meter.commit(); + perf.assertPerformance(meter); + } finally { + meter.dispose(); + } + + } + + public void testRefreshListFiltered() throws InterruptedException { + TestModel model = new TestModel(); + model.setRoot( new TestElement(model, "root", new TestElement[0] ) ); + int numElements = (int)Math.pow(2, getTestModelDepth()); + model.setElementChildren(TreePath.EMPTY, TestModel.makeSingleLevelModelElements(model, 1000, "model.")); + + fViewer.setAutoExpandLevel(-1); + + // Create the listener + fListener.reset(TreePath.EMPTY, model.getRootElement(), -1, true, false); + + fViewer.addFilter(new ViewerFilter() { + public boolean select(Viewer viewer, Object parentElement, Object element) { + if (element instanceof TestElement) { + String id = ((TestElement)element).getID(); + if (id.startsWith("model.")) { + id = id.substring("model.".length()); + } + if (id.length() >= 2 && (id.charAt(1) == '1' || id.charAt(1) == '3' || id.charAt(1) == '5' || id.charAt(1) == '7' || id.charAt(1) == '9')) { + return false; + } + } + return true; + } + }); + + // Set the input into the view and update the view. + fViewer.setInput(model.getRootElement()); + while (!fListener.isFinished()) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + model.validateData(fViewer, TreePath.EMPTY); + + Performance perf = Performance.getDefault(); + PerformanceMeter meter = perf.createPerformanceMeter(perf.getDefaultScenarioId(this)); + try { + for (int i = 0; i < 100; i++) { + // Update the model + model.setAllAppendix(" - pass " + i); + + TestElement element = model.getRootElement(); + fListener.reset(TreePath.EMPTY, element, -1, false, false); + + meter.start(); + model.postDelta(new ModelDelta(element, IModelDelta.CONTENT)); + while (!fListener.isFinished(ALL_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE)) + if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + meter.stop(); + System.gc(); + } + + meter.commit(); + perf.assertPerformance(meter); + } finally { + meter.dispose(); + } + } + } diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/PopupTests.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/PopupTests.java index 19247755d..e760cd39c 100644 --- a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/PopupTests.java +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/PopupTests.java @@ -19,9 +19,9 @@ 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.IInternalTreeModelViewer; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer; import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ITreeSelection; @@ -65,8 +65,8 @@ abstract public class PopupTests extends TestCase implements ITestModelUpdatesLi fShell.open (); } - protected ITreeModelContentProviderTarget getCTargetViewer() { - return (ITreeModelContentProviderTarget)fViewer; + protected IInternalTreeModelViewer getCTargetViewer() { + return (IInternalTreeModelViewer)fViewer; } @@ -147,14 +147,14 @@ abstract public class PopupTests extends TestCase implements ITestModelUpdatesLi // Validate the expansion state BEFORE posting the delta. - ITreeModelContentProviderTarget contentProviderViewer = (ITreeModelContentProviderTarget)fViewer; + IInternalTreeModelViewer contentProviderViewer = (IInternalTreeModelViewer)fViewer; Assert.assertFalse(contentProviderViewer.getExpandedState(path_root_3)); model.postDelta(deltaRoot); while (true) { if (fListener.isFinished(MODEL_CHANGED_COMPLETE)) { - if (fListener.isFinished(CONTENT_UPDATES_STARTED)) { - if (fListener.isFinished(CONTENT_UPDATES_COMPLETE)) { + if (fListener.isFinished(CONTENT_SEQUENCE_STARTED)) { + if (fListener.isFinished(CONTENT_SEQUENCE_COMPLETE)) { break; } } else { diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/SelectionTests.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/SelectionTests.java index 3ebb062a0..3deb65e5d 100644 --- a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/SelectionTests.java +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/SelectionTests.java @@ -15,10 +15,10 @@ import java.util.List; import junit.framework.TestCase; -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.IModelSelectionPolicy; import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer; import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; 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 9f1b85aa4..3ad16f93b 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 @@ -19,9 +19,9 @@ import junit.framework.TestCase; import org.eclipe.debug.tests.viewer.model.TestModel.TestElement; import org.eclipse.core.commands.ExecutionException; -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.IInternalTreeModelViewer; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer; import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; import org.eclipse.jface.viewers.ITreeSelection; import org.eclipse.jface.viewers.TreePath; @@ -86,8 +86,8 @@ abstract public class StateTests extends TestCase implements ITestModelUpdatesLi } } - protected ITreeModelContentProviderTarget getCTargetViewer() { - return (ITreeModelContentProviderTarget)fViewer; + protected IInternalTreeModelViewer getInternalViewer() { + return (IInternalTreeModelViewer)fViewer; } public void testUpdateViewer() throws InterruptedException { @@ -138,7 +138,7 @@ abstract public class StateTests extends TestCase implements ITestModelUpdatesLi fListener.addLabelUpdate(path3); fViewer.updateViewer(updateDelta); - while (!fListener.isFinished(CONTENT_UPDATES_COMPLETE | LABEL_UPDATES)) + while (!fListener.isFinished(CONTENT_SEQUENCE_COMPLETE | LABEL_UPDATES)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); // Extract the new state from viewer @@ -189,7 +189,7 @@ abstract public class StateTests extends TestCase implements ITestModelUpdatesLi * (size).1 * (size).1.1 */ - private TestModel alternatingSubsreesModel(int size) { + static TestModel alternatingSubsreesModel(int size) { TestModel model = new TestModel(); TestElement[] elements = new TestElement[size]; @@ -207,7 +207,7 @@ abstract public class StateTests extends TestCase implements ITestModelUpdatesLi return model; } - private boolean areTreeSelectionsEqual(ITreeSelection sel1, ITreeSelection sel2) { + static boolean areTreeSelectionsEqual(ITreeSelection sel1, ITreeSelection sel2) { Set sel1Set = new HashSet(); sel1Set.addAll( Arrays.asList(sel1.getPaths()) ); @@ -217,9 +217,9 @@ abstract public class StateTests extends TestCase implements ITestModelUpdatesLi return sel1Set.equals(sel2Set); } - private void expandAlternateElements(TestModel model, boolean waitForAllUpdates) throws InterruptedException { - fListener.reset(); - fListener.setFailOnRedundantUpdates(false); + static void expandAlternateElements(TestModelUpdatesListener listener, TestModel model, boolean waitForAllUpdates) throws InterruptedException { + listener.reset(); + listener.setFailOnRedundantUpdates(false); TestElement rootElement = model.getRootElement(); TestElement[] children = rootElement.getChildren(); @@ -237,7 +237,7 @@ abstract public class StateTests extends TestCase implements ITestModelUpdatesLi int index = i; while (element.getChildren().length != 0) { TreePath elementPath = model.findElement(element.getLabel()); - fListener.addUpdates( + listener.addUpdates( elementPath, element, 1, CHILD_COUNT_UPDATES | (waitForAllUpdates ? CHILDREN_UPDATES : 0) ); delta = delta.addNode(element, index, IModelDelta.EXPAND, element.getChildren().length); @@ -247,8 +247,8 @@ abstract public class StateTests extends TestCase implements ITestModelUpdatesLi } model.postDelta(rootDelta); - while (!fListener.isFinished(CONTENT_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE)) - if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + while (!listener.isFinished(CONTENT_SEQUENCE_COMPLETE | MODEL_CHANGED_COMPLETE)) + if (!Display.getDefault().readAndDispatch ()) Thread.sleep(0); } public void testPreserveExpandedOnRemove() throws InterruptedException { @@ -265,7 +265,7 @@ abstract public class StateTests extends TestCase implements ITestModelUpdatesLi while (!fListener.isFinished()) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); model.validateData(fViewer, TreePath.EMPTY, true); - expandAlternateElements(model, true); + expandAlternateElements(fListener, model, true); // Set a selection in view TreeSelection originalSelection = new TreeSelection(model.findElement("5.1")); @@ -282,13 +282,13 @@ abstract public class StateTests extends TestCase implements ITestModelUpdatesLi // Validate data model.validateData(fViewer, TreePath.EMPTY, true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("2")) == false); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("3")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("3.1")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("4")) == false); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("5")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("5.1")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("6")) == false); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("2")) == false); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("3")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("3.1")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("4")) == false); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("5")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("5.1")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("6")) == false); Assert.assertTrue( areTreeSelectionsEqual(originalSelection, (ITreeSelection)fViewer.getSelection()) ); } @@ -306,7 +306,7 @@ abstract public class StateTests extends TestCase implements ITestModelUpdatesLi while (!fListener.isFinished()) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); model.validateData(fViewer, TreePath.EMPTY, true); - expandAlternateElements(model, true); + expandAlternateElements(fListener, model, true); // Set a selection in view TreeSelection originalSelection = new TreeSelection(model.findElement("5.1")); @@ -326,15 +326,15 @@ abstract public class StateTests extends TestCase implements ITestModelUpdatesLi // Validate data model.validateData(fViewer, TreePath.EMPTY, true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("1")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("1.1")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("2")) == false); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("3")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("3.1")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("4")) == false); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("5")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("5.1")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("6")) == false); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("1")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("1.1")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("2")) == false); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("3")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("3.1")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("4")) == false); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("5")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("5.1")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("6")) == false); Assert.assertTrue( areTreeSelectionsEqual(originalSelection, (ITreeSelection)fViewer.getSelection()) ); } @@ -352,7 +352,7 @@ abstract public class StateTests extends TestCase implements ITestModelUpdatesLi while (!fListener.isFinished()) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); model.validateData(fViewer, TreePath.EMPTY, true); - expandAlternateElements(model, true); + expandAlternateElements(fListener, model, true); // Set a selection in view // Set a selection in view @@ -366,7 +366,7 @@ abstract public class StateTests extends TestCase implements ITestModelUpdatesLi // Note: Re-expanding nodes causes redundant updates. fListener.reset(false, false); - fListener.addUpdates(getCTargetViewer(), TreePath.EMPTY, model.getRootElement(), -1, ALL_UPDATES_COMPLETE); + fListener.addUpdates(getInternalViewer(), TreePath.EMPTY, model.getRootElement(), -1, ALL_UPDATES_COMPLETE); // Create the delta which has nodes with CONTENT flag set at multiple levels. ModelDelta rootDelta = new ModelDelta(model.getRootElement(), IModelDelta.CONTENT); @@ -380,14 +380,18 @@ abstract public class StateTests extends TestCase implements ITestModelUpdatesLi // Validate data model.validateData(fViewer, TreePath.EMPTY, true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("2")) == false); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("3")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("3.1")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("4")) == false); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("5")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("5.1")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("6")) == false); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("2")) == false); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("3")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("3.1")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("4")) == false); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("5")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("5.1")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("6")) == false); Assert.assertTrue( areTreeSelectionsEqual(originalSelection, (ITreeSelection)fViewer.getSelection()) ); + + // Note: in past it was observed sub-optimal coalescing in this test due + // to scattered update requests from viewer. + Assert.assertTrue( fListener.checkCoalesced(TreePath.EMPTY, 0, 6) ); } @@ -424,7 +428,7 @@ abstract public class StateTests extends TestCase implements ITestModelUpdatesLi // Note: Re-expanding nodes causes redundant updates. fListener.reset(false, false); - fListener.addUpdates(getCTargetViewer(), elementPath, model.getElement(elementPath), -1, ALL_UPDATES_COMPLETE); + fListener.addUpdates(getInternalViewer(), elementPath, model.getElement(elementPath), -1, ALL_UPDATES_COMPLETE); // Post the sub-tree update model.postDelta(rootDelta); @@ -433,16 +437,16 @@ abstract public class StateTests extends TestCase implements ITestModelUpdatesLi // Validate data model.validateData(fViewer, TreePath.EMPTY, true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("3")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("3")) == true); // On windows, getExpandedState() may return true for an element with no children: // Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("3.0 - new")) == false); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("3.1")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("3.2")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("3.3")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("3.1")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("3.2")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("3.3")) == true); Assert.assertTrue( areTreeSelectionsEqual(originalSelection, (ITreeSelection)fViewer.getSelection()) ); } - public void _X_testPreserveExpandedOnContentStress() throws InterruptedException { + public void testPreserveExpandedOnContentStress() throws InterruptedException { //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer); TestModel model = alternatingSubsreesModel(6); @@ -456,7 +460,7 @@ abstract public class StateTests extends TestCase implements ITestModelUpdatesLi while (!fListener.isFinished()) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); model.validateData(fViewer, TreePath.EMPTY, true); - expandAlternateElements(model, true); + expandAlternateElements(fListener, model, true); // Set a selection in view // TreeSelection originalSelection = new TreeSelection( @@ -473,20 +477,20 @@ abstract public class StateTests extends TestCase implements ITestModelUpdatesLi // Note: Re-expanding nodes causes redundant updates. fListener.reset(false, false); - fListener.addUpdates(getCTargetViewer(), TreePath.EMPTY, model.getRootElement(), -1, ALL_UPDATES_COMPLETE); + fListener.addUpdates(getInternalViewer(), TreePath.EMPTY, model.getRootElement(), -1, ALL_UPDATES_COMPLETE); model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT)); while (!fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); // Validate data model.validateData(fViewer, TreePath.EMPTY, true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("2")) == false); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("3")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("3.1")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("4")) == false); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("5")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("5.1")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("6")) == false); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("2")) == false); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("3")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("3.1")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("4")) == false); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("5")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("5.1")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("6")) == false); Assert.assertTrue( areTreeSelectionsEqual(originalSelection, (ITreeSelection)fViewer.getSelection()) ); // Update the model again @@ -494,25 +498,25 @@ abstract public class StateTests extends TestCase implements ITestModelUpdatesLi // Note: Re-expanding nodes causes redundant updates. fListener.reset(false, false); - fListener.addUpdates(getCTargetViewer(), TreePath.EMPTY, model.getRootElement(), -1, ALL_UPDATES_COMPLETE); + fListener.addUpdates(getInternalViewer(), TreePath.EMPTY, model.getRootElement(), -1, ALL_UPDATES_COMPLETE); model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT)); while (!fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); // Validate data model.validateData(fViewer, TreePath.EMPTY, true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("2")) == false); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("3")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("3.1")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("4")) == false); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("5")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("5.1")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("6")) == false); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("2")) == false); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("3")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("3.1")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("4")) == false); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("5")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("5.1")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("6")) == false); Assert.assertTrue( areTreeSelectionsEqual(originalSelection, (ITreeSelection)fViewer.getSelection()) ); } } - public void _X_testPreserveLargeModelOnContent() throws InterruptedException { + public void testPreserveLargeModelOnContent() throws InterruptedException { //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer); TestModel model = alternatingSubsreesModel(100); @@ -523,10 +527,10 @@ abstract public class StateTests extends TestCase implements ITestModelUpdatesLi // Set the input into the view and update the view. fViewer.setInput(model.getRootElement()); - while (!fListener.isFinished(CONTENT_UPDATES_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + while (!fListener.isFinished(CONTENT_SEQUENCE_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); // model.validateData(fViewer, TreePath.EMPTY, true); - expandAlternateElements(model, false); + expandAlternateElements(fListener, model, false); // Set a selection in view TreeSelection originalSelection = new TreeSelection(model.findElement("5.1.1")); @@ -539,16 +543,16 @@ abstract public class StateTests extends TestCase implements ITestModelUpdatesLi // Note: Re-expanding nodes causes redundant updates. fListener.reset(false, false); model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT)); - while (!fListener.isFinished(CONTENT_UPDATES_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + while (!fListener.isFinished(CONTENT_SEQUENCE_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); // Validate data - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("2")) == false); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("3")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("3.1")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("4")) == false); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("5")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("5.1")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("6")) == false); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("2")) == false); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("3")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("3.1")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("4")) == false); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("5")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("5.1")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("6")) == false); Assert.assertTrue( areTreeSelectionsEqual(originalSelection, (ITreeSelection)fViewer.getSelection()) ); // Update the model again @@ -557,16 +561,16 @@ abstract public class StateTests extends TestCase implements ITestModelUpdatesLi // Note: Re-expanding nodes causes redundant updates. fListener.reset(false, false); model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT)); - while (!fListener.isFinished(CONTENT_UPDATES_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + while (!fListener.isFinished(CONTENT_SEQUENCE_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); // Validate data - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("2")) == false); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("3")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("3.1")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("4")) == false); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("5")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("5.1")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("6")) == false); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("2")) == false); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("3")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("3.1")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("4")) == false); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("5")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("5.1")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("6")) == false); Assert.assertTrue( areTreeSelectionsEqual(originalSelection, (ITreeSelection)fViewer.getSelection()) ); } @@ -675,7 +679,7 @@ abstract public class StateTests extends TestCase implements ITestModelUpdatesLi if (!fDisplay.readAndDispatch ()) Thread.sleep(0); // Check to make sure that the state restore didn't change the selection. - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("3.1")) == false); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("3.1")) == false); } public void testPreserveExpandDeltaAfterContent() throws InterruptedException { @@ -716,7 +720,7 @@ abstract public class StateTests extends TestCase implements ITestModelUpdatesLi if (!fDisplay.readAndDispatch ()) Thread.sleep(0); // Check to make sure that the state restore didn't change the selection. - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("3.1")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("3.1")) == true); } @@ -735,7 +739,7 @@ abstract public class StateTests extends TestCase implements ITestModelUpdatesLi model.validateData(fViewer, TreePath.EMPTY, true); // Expand some, but not all elements - expandAlternateElements(model, true); + expandAlternateElements(fListener, model, true); // Set a selection in view fViewer.setSelection(new TreeSelection(new TreePath[] { model.findElement("5.1"), model.findElement("5.1.1"), model.findElement("6.1.1") } )); @@ -746,7 +750,7 @@ abstract public class StateTests extends TestCase implements ITestModelUpdatesLi // Set the viewer input to null. This will trigger the view to save the viewer state. fListener.reset(false, false); - fListener.addStateUpdates(getCTargetViewer(), originalState, IModelDelta.EXPAND | IModelDelta.SELECT | IModelDelta.REVEAL); + fListener.addStateUpdates(getInternalViewer(), originalState, IModelDelta.EXPAND | IModelDelta.SELECT | IModelDelta.REVEAL); fViewer.setInput(null); while (!fListener.isFinished(STATE_SAVE_COMPLETE | STATE_UPDATES)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); @@ -796,7 +800,7 @@ abstract public class StateTests extends TestCase implements ITestModelUpdatesLi // Set the viewer input to null. This will trigger the view to save the viewer state. fListener.reset(true, false); - fListener.addStateUpdates(getCTargetViewer(), originalState, IModelDelta.EXPAND | IModelDelta.SELECT | IModelDelta.REVEAL); + fListener.addStateUpdates(getInternalViewer(), originalState, IModelDelta.EXPAND | IModelDelta.SELECT | IModelDelta.REVEAL); fViewer.setInput(null); while (!fListener.isFinished(STATE_SAVE_COMPLETE | STATE_UPDATES)) @@ -819,6 +823,93 @@ abstract public class StateTests extends TestCase implements ITestModelUpdatesLi } } + public void testSaveAndRestoreInputInstance() throws InterruptedException { + //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer); + TestModel model = alternatingSubsreesModel(6); + + // NOTE: WE ARE NOT EXPANDING ANY CHILDREN + + // Create the listener, only check the first level + fListener.reset(TreePath.EMPTY, model.getRootElement(), 1, true, false); + + // Set the input into the view and update the view. + fViewer.setInput(model.getRootElement()); + while (!fListener.isFinished()) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + model.validateData(fViewer, TreePath.EMPTY, true); + + // Expand some, but not all elements + expandAlternateElements(fListener, model, true); + + // Set a selection in view + fViewer.setSelection(new TreeSelection(new TreePath[] { model.findElement("5.1"), model.findElement("5.1.1"), model.findElement("6.1.1") } )); + + // Extract the original state from viewer + ModelDelta originalState = new ModelDelta(model.getRootElement(), IModelDelta.NO_CHANGE); + fViewer.saveElementState(TreePath.EMPTY, originalState, IModelDelta.EXPAND | IModelDelta.SELECT); + + // Do not reset to null, just reset input to the same object. + + // Set the viewer input back to the model. When view updates are complete + // the viewer + // Note: disable redundant updates because the reveal delta triggers one. + fListener.reset(TreePath.EMPTY, model.getRootElement(), 1, false, false); + fViewer.setInput(model.getRootElement()); + while (!fListener.isFinished()) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + + // Extract the restored state from viewer + ModelDelta restoredState = new ModelDelta(model.getRootElement(), IModelDelta.NO_CHANGE); + fViewer.saveElementState(TreePath.EMPTY, restoredState, IModelDelta.EXPAND | IModelDelta.SELECT); + + if (!deltaMatches(originalState, restoredState)) { + Assert.fail("Expected:\n" + originalState.toString() + "\nGot:\n" + restoredState); + } + } + + public void testSaveAndRestoreInputInstanceEquals() throws InterruptedException { + //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer); + TestModel model = alternatingSubsreesModel(6); + + // NOTE: WE ARE NOT EXPANDING ANY CHILDREN + + // Create the listener, only check the first level + fListener.reset(TreePath.EMPTY, model.getRootElement(), 1, true, false); + + // Set the input into the view and update the view. + fViewer.setInput(model.getRootElement()); + while (!fListener.isFinished()) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + model.validateData(fViewer, TreePath.EMPTY, true); + + // Expand some, but not all elements + expandAlternateElements(fListener, model, true); + + // Set a selection in view + fViewer.setSelection(new TreeSelection(new TreePath[] { model.findElement("5.1"), model.findElement("5.1.1"), model.findElement("6.1.1") } )); + + // Extract the original state from viewer + ModelDelta originalState = new ModelDelta(model.getRootElement(), IModelDelta.NO_CHANGE); + fViewer.saveElementState(TreePath.EMPTY, originalState, IModelDelta.EXPAND | IModelDelta.SELECT); + + // Create a copy of the input object and set it to model. + TestElement newRoot = new TestElement(model, model.getRootElement().getID(), model.getRootElement().getChildren()); + model.setRoot(newRoot); + + // Set the viewer input back to the model. When view updates are complete + // the viewer + // Note: disable redundant updates because the reveal delta triggers one. + fListener.reset(TreePath.EMPTY, model.getRootElement(), 1, false, false); + + fViewer.setInput(model.getRootElement()); + while (!fListener.isFinished()) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + + // Extract the restored state from viewer + ModelDelta restoredState = new ModelDelta(model.getRootElement(), IModelDelta.NO_CHANGE); + fViewer.saveElementState(TreePath.EMPTY, restoredState, IModelDelta.EXPAND | IModelDelta.SELECT); + + if (!deltaMatches(originalState, restoredState)) { + Assert.fail("Expected:\n" + originalState.toString() + "\nGot:\n" + restoredState); + } + } + public void testSaveAndRestoreLarge() throws InterruptedException { //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer); @@ -831,9 +922,9 @@ abstract public class StateTests extends TestCase implements ITestModelUpdatesLi // Set the input into the view and update the view. fViewer.setInput(model.getRootElement()); - while (!fListener.isFinished(CONTENT_UPDATES_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + while (!fListener.isFinished(CONTENT_SEQUENCE_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); - expandAlternateElements(model, false); + expandAlternateElements(fListener, model, false); // Set a selection in view TreeSelection originalSelection = new TreeSelection(model.findElement("5.1.1")); @@ -846,7 +937,7 @@ abstract public class StateTests extends TestCase implements ITestModelUpdatesLi // Set the viewer input to null. This will trigger the view to save the viewer state. fListener.reset(); - fListener.addStateUpdates(getCTargetViewer(), originalState, IModelDelta.EXPAND | IModelDelta.SELECT | IModelDelta.REVEAL); + fListener.addStateUpdates(getInternalViewer(), originalState, IModelDelta.EXPAND | IModelDelta.SELECT | IModelDelta.REVEAL); fViewer.setInput(null); while (!fListener.isFinished(STATE_SAVE_COMPLETE | STATE_UPDATES)) @@ -857,18 +948,18 @@ abstract public class StateTests extends TestCase implements ITestModelUpdatesLi // Note: disable redundant updates because the reveal delta triggers one. fListener.reset(); fViewer.setInput(model.getRootElement()); - while (!fListener.isFinished(CONTENT_UPDATES_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + while (!fListener.isFinished(CONTENT_SEQUENCE_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); // Validate data (only select visible elements). - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("1")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("1.1")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("2")) == false); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("3")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("3.1")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("4")) == false); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("5")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("5.1")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("6")) == false); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("1")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("1.1")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("2")) == false); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("3")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("3.1")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("4")) == false); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("5")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("5.1")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("6")) == false); Assert.assertTrue( areTreeSelectionsEqual(originalSelection, (ITreeSelection)fViewer.getSelection()) ); } @@ -888,9 +979,9 @@ abstract public class StateTests extends TestCase implements ITestModelUpdatesLi // Set the input into the view and update the view. fViewer.setInput(model.getRootElement()); - while (!fListener.isFinished(CONTENT_UPDATES_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + while (!fListener.isFinished(CONTENT_SEQUENCE_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); - expandAlternateElements(model, false); + expandAlternateElements(fListener, model, false); // Set a selection in view TreeSelection originalSelection = new TreeSelection(model.findElement("5.1.1")); @@ -903,7 +994,7 @@ abstract public class StateTests extends TestCase implements ITestModelUpdatesLi // Set the viewer input to null. This will trigger the view to save the viewer state. fListener.reset(); - fListener.addStateUpdates(getCTargetViewer(), originalState, IModelDelta.EXPAND | IModelDelta.SELECT | IModelDelta.REVEAL); + fListener.addStateUpdates(getInternalViewer(), originalState, IModelDelta.EXPAND | IModelDelta.SELECT | IModelDelta.REVEAL); fViewer.setInput(null); while (!fListener.isFinished(STATE_SAVE_COMPLETE | STATE_UPDATES)) @@ -922,18 +1013,18 @@ abstract public class StateTests extends TestCase implements ITestModelUpdatesLi fViewer.setInput(model.getRootElement()); // MONITOR FOR THE STATE RESTORE TO COMPLETE - while (!fListener.isFinished(CONTENT_UPDATES_COMPLETE| STATE_RESTORE_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + while (!fListener.isFinished(CONTENT_SEQUENCE_COMPLETE| STATE_RESTORE_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); // Validate data - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("1")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("1.1")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("2")) == false); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("3")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("3.1")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("4")) == false); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("5")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("5.1")) == true); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("6")) == false); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("1")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("1.1")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("2")) == false); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("3")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("3.1")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("4")) == false); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("5")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("5.1")) == true); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("6")) == false); Assert.assertTrue( areTreeSelectionsEqual(originalSelection, (ITreeSelection)fViewer.getSelection()) ); } @@ -998,9 +1089,39 @@ abstract public class StateTests extends TestCase implements ITestModelUpdatesLi if (!fDisplay.readAndDispatch ()) Thread.sleep(0); // Check to make sure that the state restore didn't change the selection. - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("2")) == false); - Assert.assertTrue(getCTargetViewer().getExpandedState(model.findElement("3")) == false); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("2")) == false); + Assert.assertTrue(getInternalViewer().getExpandedState(model.findElement("3")) == false); Assert.assertEquals(new TreeSelection(model.findElement("1")), fViewer.getSelection()); } + /** + * Test for bug 359859.<br> + * This test verifies that RESTORE state is handled after SAVE previous state was completed + */ + public void testSaveRestoreOrder() throws InterruptedException { + //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer); + TestModel model = TestModel.simpleMultiLevel(); + + // Expand all + fViewer.setAutoExpandLevel(-1); + + // Create the listener. + fListener.reset(TreePath.EMPTY, model.getRootElement(), -1, false, false); + + // Set the input into the view and update the view. + fViewer.setInput(model.getRootElement()); + while (!fListener.isFinished()) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + model.validateData(fViewer, TreePath.EMPTY, true); + + // a new similar model + TestModel copyModel = TestModel.simpleMultiLevel(); + + // Trigger save - restore sequence. + fListener.reset(); + fListener.expectRestoreAfterSaveComplete(); + fViewer.setInput(copyModel.getRootElement()); + while (!fListener.isFinished(STATE_RESTORE_STARTED)) Thread.sleep(0); + Assert.assertTrue("RESTORE started before SAVE to complete", fListener.isFinished(STATE_SAVE_COMPLETE)); + } + } 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 6647bd226..e497d5837 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 @@ -10,14 +10,15 @@ *******************************************************************************/ package org.eclipe.debug.tests.viewer.model; +import java.util.ArrayList; import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; import junit.framework.Assert; import org.eclipse.core.runtime.PlatformObject; -import org.eclipse.debug.internal.ui.viewers.model.ITreeModelCheckProviderTarget; -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.IInternalTreeModelViewer; import org.eclipse.debug.internal.ui.viewers.model.provisional.ICheckUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; @@ -34,10 +35,13 @@ import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxyFactor import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicy; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicyFactory; import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; import org.eclipse.debug.internal.ui.viewers.provisional.AbstractModelProxy; import org.eclipse.jface.viewers.TreePath; import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerFilter; /** * Test model for the use in unit tests. This test model contains a set of @@ -158,7 +162,8 @@ public class TestModel implements IElementContentProvider, IElementLabelProvider private TreePath fRootPath = TreePath.EMPTY; private ModelProxy fModelProxy; private IModelSelectionPolicy fModelSelectionPolicy; - + private boolean fQueueingUpdates = false; + private List fQueuedUpdates = new LinkedList(); /** * Constructor private. Use static factory methods instead. @@ -207,46 +212,109 @@ public class TestModel implements IElementContentProvider, IElementLabelProvider return depth; } + public void setQeueueingUpdate(boolean queueingUpdates) { + fQueueingUpdates = queueingUpdates; + if (!fQueueingUpdates) { + processQueuedUpdates(); + } + } + + public List getQueuedUpdates() { + return fQueuedUpdates; + } + + public void processQueuedUpdates() { + List updates = new ArrayList(fQueuedUpdates); + fQueuedUpdates.clear(); + for (int i = 0; i < updates.size(); i++) { + processUpdate((IViewerUpdate)updates.get(i)); + } + } + + public void processUpdate(IViewerUpdate update) { + if (update instanceof IHasChildrenUpdate) { + doUpdate((IHasChildrenUpdate)update); + } else if (update instanceof IChildrenCountUpdate) { + doUpdate((IChildrenCountUpdate)update); + } else if (update instanceof IChildrenUpdate) { + doUpdate((IChildrenUpdate)update); + } else if (update instanceof ILabelUpdate) { + doUpdate((ILabelUpdate)update); + } + } + public void update(IHasChildrenUpdate[] updates) { - for (int i = 0; i < updates.length; i++) { - TestElement element = (TestElement)updates[i].getElement(); - updates[i].setHasChilren(element.getChildren().length > 0); - updates[i].done(); + if (fQueueingUpdates) { + fQueuedUpdates.addAll(Arrays.asList(updates)); + } else { + for (int i = 0; i < updates.length; i++) { + doUpdate(updates[i]); + } } } + + private void doUpdate(IHasChildrenUpdate update) { + TestElement element = (TestElement)update.getElement(); + update.setHasChilren(element.getChildren().length > 0); + update.done(); + } public void update(IChildrenCountUpdate[] updates) { - for (int i = 0; i < updates.length; i++) { - TestElement element = (TestElement)updates[i].getElement(); - updates[i].setChildCount(element.getChildren().length); - updates[i].done(); + if (fQueueingUpdates) { + fQueuedUpdates.addAll(Arrays.asList(updates)); + } else { + for (int i = 0; i < updates.length; i++) { + doUpdate(updates[i]); + } } } + + private void doUpdate(IChildrenCountUpdate update) { + TestElement element = (TestElement)update.getElement(); + update.setChildCount(element.getChildren().length); + update.done(); + } public void update(IChildrenUpdate[] updates) { - for (int i = 0; i < updates.length; i++) { - TestElement element = (TestElement)updates[i].getElement(); - int endOffset = updates[i].getOffset() + updates[i].getLength(); - for (int j = updates[i].getOffset(); j < endOffset; j++) { - if (j < element.getChildren().length) { - updates[i].setChild(element.getChildren()[j], j); - } + if (fQueueingUpdates) { + fQueuedUpdates.addAll(Arrays.asList(updates)); + } else { + for (int i = 0; i < updates.length; i++) { + doUpdate(updates[i]); } - updates[i].done(); } } + + private void doUpdate(IChildrenUpdate update) { + TestElement element = (TestElement)update.getElement(); + int endOffset = update.getOffset() + update.getLength(); + for (int j = update.getOffset(); j < endOffset; j++) { + if (j < element.getChildren().length) { + update.setChild(element.getChildren()[j], j); + } + } + update.done(); + } public void update(ILabelUpdate[] updates) { - for (int i = 0; i < updates.length; i++) { - TestElement element = (TestElement)updates[i].getElement(); - updates[i].setLabel(element.getLabel(), 0); - if (updates[i] instanceof ICheckUpdate && - Boolean.TRUE.equals(updates[i].getPresentationContext().getProperty(ICheckUpdate.PROP_CHECK))) - { - ((ICheckUpdate)updates[i]).setChecked(element.getChecked(), element.getGrayed()); + if (fQueueingUpdates) { + fQueuedUpdates.addAll(Arrays.asList(updates)); + } else { + for (int i = 0; i < updates.length; i++) { + doUpdate(updates[i]); } - updates[i].done(); - } + } + } + + private void doUpdate(ILabelUpdate update) { + TestElement element = (TestElement)update.getElement(); + update.setLabel(element.getLabel(), 0); + if (update instanceof ICheckUpdate && + Boolean.TRUE.equals(update.getPresentationContext().getProperty(ICheckUpdate.PROP_CHECK))) + { + ((ICheckUpdate)update).setChecked(element.getChecked(), element.getGrayed()); + } + update.done(); } public final static String ELEMENT_MEMENTO_ID = "id"; @@ -326,24 +394,32 @@ public class TestModel implements IElementContentProvider, IElementLabelProvider public void validateData(ITreeModelViewer viewer, TreePath path) { validateData(viewer, path, false); } - + public void validateData(ITreeModelViewer _viewer, TreePath path, boolean expandedElementsOnly) { - ITreeModelContentProviderTarget viewer = (ITreeModelContentProviderTarget)_viewer; + validateData(_viewer, path, expandedElementsOnly, TestModelUpdatesListener.EMPTY_FILTER_ARRAY); + } + + public void validateData(ITreeModelViewer _viewer, TreePath path, boolean expandedElementsOnly, ViewerFilter[] filters) { + IInternalTreeModelViewer viewer = (IInternalTreeModelViewer)_viewer; TestElement element = getElement(path); if ( Boolean.TRUE.equals(_viewer.getPresentationContext().getProperty(ICheckUpdate.PROP_CHECK)) ) { - ITreeModelCheckProviderTarget checkTarget = (ITreeModelCheckProviderTarget)_viewer; - Assert.assertEquals(element.getChecked(), checkTarget.getElementChecked(path)); - Assert.assertEquals(element.getGrayed(), checkTarget.getElementGrayed(path)); + Assert.assertEquals(element.getChecked(), viewer.getElementChecked(path)); + Assert.assertEquals(element.getGrayed(), viewer.getElementGrayed(path)); } if (!expandedElementsOnly || path.getSegmentCount() == 0 || viewer.getExpandedState(path) ) { TestElement[] children = element.getChildren(); - Assert.assertEquals(children.length, viewer.getChildCount(path)); + int viewerIndex = 0; for (int i = 0; i < children.length; i++) { - Assert.assertEquals(children[i], viewer.getChildElement(path, i)); - validateData(viewer, path.createChildPath(children[i]), expandedElementsOnly); + if (TestModelUpdatesListener.isFiltered(children[i], filters)) { + continue; + } + Assert.assertEquals(children[i], viewer.getChildElement(path, viewerIndex)); + validateData(viewer, path.createChildPath(children[i]), expandedElementsOnly, filters); + viewerIndex++; } + Assert.assertEquals(viewerIndex, viewer.getChildCount(path)); } else if (!viewer.getExpandedState(path)) { // If element not expanded, verify the plus sign. Assert.assertEquals(viewer.getHasChildren(path), element.getChildren().length > 0); @@ -590,17 +666,28 @@ public class TestModel implements IElementContentProvider, IElementLabelProvider public static TestModel simpleSingleLevel() { TestModel model = new TestModel(); - model.setRoot( new TestElement(model, "root", new TestElement[] { - new TestElement(model, "1", true, true, new TestElement[0]), - new TestElement(model, "2", true, false, new TestElement[0]), - new TestElement(model, "3", false, true, new TestElement[0]), - new TestElement(model, "4", false, false, new TestElement[0]), - new TestElement(model, "5", new TestElement[0]), - new TestElement(model, "6", new TestElement[0]) - }) ); + model.setRoot( new TestElement(model, "root", makeSingleLevelModelElements(model, 6, ""))); return model; } + + public static TestElement[] makeSingleLevelModelElements(TestModel model, int length, String prefix) { + TestElement[] elements = new TestElement[length]; + for (int i = 1; i <= length; i++) { + String name = prefix + i; + elements[i - 1] = new TestElement(model, name, new TestElement[0]); + } + return elements; + } + public static TestElement[] makeMultiLevelElements(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, makeMultiLevelElements(model, i, name + ".")); + } + return elements; + } + public static TestModel simpleMultiLevel() { TestModel model = new TestModel(); model.setRoot( new TestElement(model, "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 31a3523f2..8b540b121 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 @@ -27,9 +27,8 @@ import org.eclipse.core.runtime.jobs.IJobChangeEvent; import org.eclipse.core.runtime.jobs.IJobChangeListener; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.debug.internal.ui.viewers.model.ElementCompareRequest; +import org.eclipse.debug.internal.ui.viewers.model.IInternalTreeModelViewer; 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.ITreeModelViewer; 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; @@ -38,14 +37,18 @@ import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelChangedList import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy; import org.eclipse.debug.internal.ui.viewers.model.provisional.IStateUpdateListener; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer; import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdateListener; import org.eclipse.jface.viewers.TreePath; +import org.eclipse.jface.viewers.ViewerFilter; public class TestModelUpdatesListener implements IViewerUpdateListener, ILabelUpdateListener, IModelChangedListener, ITestModelUpdatesListenerConstants, IStateUpdateListener, IJobChangeListener { + public static final ViewerFilter[] EMPTY_FILTER_ARRAY = new ViewerFilter[0]; + private final ITreeModelViewer fViewer; private IStatus fJobError; @@ -60,9 +63,7 @@ public class TestModelUpdatesListener private Set fRedundantLabelUpdateExceptions = new HashSet(); private boolean fFailOnMultipleModelUpdateSequences; - private boolean fMultipleModelUpdateSequencesObserved; private boolean fFailOnMultipleLabelUpdateSequences; - private boolean fMultipleLabelUpdateSequencesObserved; private Set fHasChildrenUpdatesScheduled = new HashSet(); private Set fHasChildrenUpdatesRunning = new HashSet(); @@ -78,10 +79,14 @@ public class TestModelUpdatesListener private Set fLabelUpdatesCompleted = new HashSet(); private Set fProxyModels = new HashSet(); private Set fStateUpdates = new HashSet(); - private boolean fViewerUpdatesStarted; - private boolean fViewerUpdatesComplete; - private boolean fLabelUpdatesStarted; - private boolean fLabelUpdatesComplete; + private int fViewerUpdatesStarted = 0; + private int fViewerUpdatesComplete = 0; + private int fViewerUpdatesStartedAtReset; + private int fViewerUpdatesCompleteAtReset; + private int fLabelUpdatesStarted = 0; + private int fLabelUpdatesComplete = 0; + private int fLabelUpdatesStartedAtReset; + private int fLabelUpdatesCompleteAtReset; private boolean fModelChangedComplete; private boolean fStateSaveStarted; private boolean fStateSaveComplete; @@ -91,6 +96,10 @@ public class TestModelUpdatesListener private int fLabelUpdatesCounter; private int fTimeoutInterval = 60000; private long fTimeoutTime; + + private boolean fExpectRestoreAfterSaveComplete; + + private RuntimeException fFailExpectation; public TestModelUpdatesListener(ITreeModelViewer viewer, boolean failOnRedundantUpdates, boolean failOnMultipleModelUpdateSequences) { @@ -143,6 +152,10 @@ public class TestModelUpdatesListener public void setFailOnMultipleLabelUpdateSequences(boolean failOnMultipleLabelUpdateSequences) { fFailOnMultipleLabelUpdateSequences = failOnMultipleLabelUpdateSequences; } + + public void expectRestoreAfterSaveComplete() { + fExpectRestoreAfterSaveComplete = true; + } /** * Sets the the maximum amount of time (in milliseconds) that the update listener @@ -153,8 +166,12 @@ public class TestModelUpdatesListener } public void reset(TreePath path, TestElement element, int levels, boolean failOnRedundantUpdates, boolean failOnMultipleUpdateSequences) { + reset(path, element, EMPTY_FILTER_ARRAY, levels, failOnRedundantUpdates, failOnMultipleUpdateSequences); + } + + public void reset(TreePath path, TestElement element, ViewerFilter[] filters, int levels, boolean failOnRedundantUpdates, boolean failOnMultipleUpdateSequences) { reset(); - addUpdates(path, element, levels); + addUpdates(path, element, filters, levels); addProxies(element); setFailOnRedundantUpdates(failOnRedundantUpdates); setFailOnMultipleModelUpdateSequences(failOnMultipleUpdateSequences); @@ -170,14 +187,13 @@ public class TestModelUpdatesListener public void reset() { fJobError = null; + fFailExpectation = null; fRedundantUpdates.clear(); fRedundantLabelUpdates.clear(); fRedundantHasChildrenUpdateExceptions.clear(); fRedundantChildCountUpdateExceptions.clear(); fRedundantChildrenUpdateExceptions.clear(); fRedundantLabelUpdateExceptions.clear(); - fMultipleLabelUpdateSequencesObserved = false; - fMultipleModelUpdateSequencesObserved = false; fHasChildrenUpdatesScheduled.clear(); fHasChildrenUpdatesRunning.clear(); fHasChildrenUpdatesCompleted.clear(); @@ -191,15 +207,16 @@ public class TestModelUpdatesListener fLabelUpdatesRunning.clear(); fLabelUpdatesCompleted.clear(); fProxyModels.clear(); - fViewerUpdatesStarted = false; - fViewerUpdatesComplete = false; - fLabelUpdatesStarted = false; - fLabelUpdatesComplete = false; + fViewerUpdatesStartedAtReset = fViewerUpdatesStarted; + fViewerUpdatesCompleteAtReset = fViewerUpdatesComplete; + fLabelUpdatesStartedAtReset = fLabelUpdatesStarted; + fLabelUpdatesCompleteAtReset = fLabelUpdatesComplete; fStateUpdates.clear(); fStateSaveStarted = false; fStateSaveComplete = false; fStateRestoreStarted = false; fStateRestoreComplete = false; + fExpectRestoreAfterSaveComplete = false; fTimeoutTime = System.currentTimeMillis() + fTimeoutInterval; resetModelChanged(); } @@ -252,14 +269,18 @@ public class TestModelUpdatesListener } public void addUpdates(TreePath path, TestElement element, int levels) { - addUpdates(path, element, levels, ALL_UPDATES_COMPLETE); + addUpdates(null, path, element, EMPTY_FILTER_ARRAY, levels, ALL_UPDATES_COMPLETE ); + } + + public void addUpdates(TreePath path, TestElement element, ViewerFilter[] filters, int levels) { + addUpdates(null, path, element, filters, levels, ALL_UPDATES_COMPLETE ); } - public void addStateUpdates(ITreeModelContentProviderTarget viewer, TreePath path, TestElement element) { + public void addStateUpdates(IInternalTreeModelViewer viewer, TreePath path, TestElement element) { addUpdates(viewer, path, element, -1, STATE_UPDATES); } - public void addStateUpdates(ITreeModelContentProviderTarget viewer, IModelDelta pendingDelta, int deltaFlags) { + public void addStateUpdates(IInternalTreeModelViewer viewer, IModelDelta pendingDelta, int deltaFlags) { TreePath treePath = getViewerTreePath(pendingDelta); if ( !TreePath.EMPTY.equals(treePath) && (pendingDelta.getFlags() & deltaFlags) != 0 ) { addUpdates(viewer, treePath, (TestElement)treePath.getLastSegment(), 0, STATE_UPDATES); @@ -286,6 +307,33 @@ public class TestModelUpdatesListener fRedundantLabelUpdateExceptions.add(path); } + public boolean checkCoalesced(TreePath path, int offset, int length) { + for (Iterator itr = fChildrenUpdatesCompleted.iterator(); itr.hasNext();) { + IChildrenUpdate update = (IChildrenUpdate)itr.next(); + if (path.equals( update.getElementPath() ) && + offset == update.getOffset() && + length == update.getLength()) + { + return true; + } + } + return false; + } + + + + public Set getHasChildrenUpdatesCompleted() { + return fHasChildrenUpdatesCompleted; + } + + public Set getChildCountUpdatesCompleted() { + return fChildCountUpdatesCompleted; + } + + public Set getChildrenUpdatesCompleted() { + return fChildrenUpdatesCompleted; + } + /** * Returns a tree path for the node, *not* including the root element. * @@ -308,7 +356,24 @@ public class TestModelUpdatesListener addUpdates(null, path, element, levels, flags); } - public void addUpdates(ITreeModelContentProviderTarget viewer, TreePath path, TestElement element, int levels, int flags) { + public void addUpdates(IInternalTreeModelViewer viewer, TreePath path, TestElement element, int levels, int flags) { + addUpdates(viewer, path, element, EMPTY_FILTER_ARRAY, levels, flags); + } + + public static boolean isFiltered(Object element, ViewerFilter[] filters) { + for (int i = 0; i < filters.length; i++) { + if (!filters[i].select(null, null, element)) { + return true; + } + } + return false; + } + + public void addUpdates(IInternalTreeModelViewer viewer, TreePath path, TestElement element, ViewerFilter[] filters, int levels, int flags) { + if (isFiltered(path.getLastSegment(), filters)) { + return; + } + if (!path.equals(TreePath.EMPTY)) { if ((flags & LABEL_UPDATES) != 0) { fLabelUpdates.add(path); @@ -331,13 +396,15 @@ public class TestModelUpdatesListener if ((flags & CHILDREN_UPDATES) != 0) { Set childrenIndexes = new HashSet(); for (int i = 0; i < children.length; i++) { - childrenIndexes.add(new Integer(i)); + if (!isFiltered(children[i], filters)) { + childrenIndexes.add(new Integer(i)); + } } fChildrenUpdatesScheduled.put(path, childrenIndexes); } for (int i = 0; i < children.length; i++) { - addUpdates(viewer, path.createChildPath(children[i]), children[i], levels, flags); + addUpdates(viewer, path.createChildPath(children[i]), children[i], filters, levels, flags); } } @@ -368,37 +435,41 @@ public class TestModelUpdatesListener throw new RuntimeException("Timed Out: " + toString(flags)); } + if (fFailExpectation != null) { + throw fFailExpectation; + } + if (fJobError != null) { throw new RuntimeException("Job Error: " + fJobError); } - + if (fFailOnRedundantUpdates && !fRedundantUpdates.isEmpty()) { Assert.fail("Redundant Updates: " + fRedundantUpdates.toString()); } if (fFailOnRedundantLabelUpdates && !fRedundantLabelUpdates.isEmpty()) { Assert.fail("Redundant Label Updates: " + fRedundantLabelUpdates.toString()); } - if (fFailOnMultipleLabelUpdateSequences && !fMultipleLabelUpdateSequencesObserved) { + if (fFailOnMultipleLabelUpdateSequences && fLabelUpdatesComplete > (fLabelUpdatesCompleteAtReset + 1)) { Assert.fail("Multiple label update sequences detected"); } - if (fFailOnMultipleModelUpdateSequences && fMultipleModelUpdateSequencesObserved) { + if (fFailOnMultipleModelUpdateSequences && fViewerUpdatesComplete > (fViewerUpdatesCompleteAtReset + 1)) { Assert.fail("Multiple viewer update sequences detected"); } - if ( (flags & LABEL_UPDATES_COMPLETE) != 0) { - if (!fLabelUpdatesComplete) return false; + if ( (flags & LABEL_SEQUENCE_COMPLETE) != 0) { + if (fLabelUpdatesComplete == fLabelUpdatesCompleteAtReset) return false; } - if ( (flags & LABEL_UPDATES_STARTED) != 0) { - if (!fLabelUpdatesStarted) return false; + if ( (flags & LABEL_SEQUENCE_STARTED) != 0) { + if (fLabelUpdatesStarted == fLabelUpdatesStartedAtReset) return false; } if ( (flags & LABEL_UPDATES) != 0) { if (!fLabelUpdates.isEmpty()) return false; } - if ( (flags & CONTENT_UPDATES_STARTED) != 0) { - if (!fViewerUpdatesStarted) return false; + if ( (flags & CONTENT_SEQUENCE_STARTED) != 0) { + if (fViewerUpdatesStarted == fViewerUpdatesStartedAtReset) return false; } - if ( (flags & CONTENT_UPDATES_COMPLETE) != 0) { - if (!fViewerUpdatesComplete) return false; + if ( (flags & CONTENT_SEQUENCE_COMPLETE) != 0) { + if (fViewerUpdatesComplete == fViewerUpdatesCompleteAtReset) return false; } if ( (flags & HAS_CHILDREN_UPDATES_STARTED) != 0) { if (fHasChildrenUpdatesRunning.isEmpty() && fHasChildrenUpdatesCompleted.isEmpty()) return false; @@ -517,22 +588,23 @@ public class TestModelUpdatesListener } public void viewerUpdatesBegin() { - if (fFailOnMultipleModelUpdateSequences && fViewerUpdatesComplete) { - fMultipleModelUpdateSequencesObserved = true; + if (fViewerUpdatesStarted > fViewerUpdatesComplete) { + fFailExpectation = new RuntimeException("Unmatched updatesStarted/updateCompleted notifications observed."); } - fViewerUpdatesStarted = true; + fViewerUpdatesStarted++; } public void viewerUpdatesComplete() { - fViewerUpdatesComplete = true; + if (fViewerUpdatesStarted <= fViewerUpdatesComplete) { + fFailExpectation = new RuntimeException("Unmatched updatesStarted/updateCompleted notifications observed."); + } + fViewerUpdatesComplete++; } public void labelUpdateComplete(ILabelUpdate update) { - synchronized (this) { - fLabelUpdatesRunning.remove(update); - fLabelUpdatesCompleted.add(update); - fLabelUpdatesCounter--; - } + fLabelUpdatesRunning.remove(update); + fLabelUpdatesCompleted.add(update); + fLabelUpdatesCounter--; if (!fLabelUpdates.remove(update.getElementPath()) && fFailOnRedundantLabelUpdates && !fRedundantLabelUpdateExceptions.contains(update.getElementPath())) @@ -543,21 +615,22 @@ public class TestModelUpdatesListener } public void labelUpdateStarted(ILabelUpdate update) { - synchronized (this) { - fLabelUpdatesRunning.add(update); - fLabelUpdatesCounter++; - } + fLabelUpdatesRunning.add(update); + fLabelUpdatesCounter++; } public void labelUpdatesBegin() { - if (fFailOnMultipleLabelUpdateSequences && fLabelUpdatesComplete) { - fMultipleLabelUpdateSequencesObserved = true; + if (fLabelUpdatesStarted > fLabelUpdatesComplete) { + fFailExpectation = new RuntimeException("Unmatched labelUpdatesStarted/labelUpdateCompleted notifications observed."); } - fLabelUpdatesStarted = true; + fLabelUpdatesStarted++; } public void labelUpdatesComplete() { - fLabelUpdatesComplete = true; + if (fLabelUpdatesStarted <= fLabelUpdatesComplete) { + fFailExpectation = new RuntimeException("Unmatched labelUpdatesStarted/labelUpdateCompleted notifications observed."); + } + fLabelUpdatesComplete++; } public void modelChanged(IModelDelta delta, IModelProxy proxy) { @@ -573,6 +646,9 @@ public class TestModelUpdatesListener } public void stateRestoreUpdatesBegin(Object input) { + if (fExpectRestoreAfterSaveComplete && !fStateSaveComplete) { + fFailExpectation = new RuntimeException("RESTORE should begin after SAVE completed!"); + } fStateRestoreStarted = true; } @@ -617,15 +693,7 @@ public class TestModelUpdatesListener buf.append("\n\t"); buf.append("fRedundantUpdates = " + fRedundantUpdates); } - if (fFailOnMultipleLabelUpdateSequences) { - buf.append("\n\t"); - buf.append("fMultipleLabelUpdateSequencesObserved = " + fMultipleLabelUpdateSequencesObserved); - } - if (fFailOnMultipleModelUpdateSequences) { - buf.append("\n\t"); - buf.append("fMultipleModelUpdateSequencesObserved = " + fMultipleModelUpdateSequencesObserved); - } - if ( (flags & LABEL_UPDATES_COMPLETE) != 0) { + if ( (flags & LABEL_SEQUENCE_COMPLETE) != 0) { buf.append("\n\t"); buf.append("fLabelUpdatesComplete = " + fLabelUpdatesComplete); } @@ -633,10 +701,10 @@ public class TestModelUpdatesListener buf.append("\n\t"); buf.append("fLabelUpdatesRunning = " + fLabelUpdatesCounter); } - if ( (flags & LABEL_UPDATES_STARTED) != 0) { + if ( (flags & LABEL_SEQUENCE_STARTED) != 0) { buf.append("\n\t"); - buf.append("fLabelUpdatesRunning = "); - buf.append( fLabelUpdatesRunning ); + buf.append("fLabelUpdatesStarted = "); + buf.append( fLabelUpdatesStarted ); buf.append("\n\t"); buf.append("fLabelUpdatesCompleted = "); buf.append( fLabelUpdatesCompleted ); @@ -646,14 +714,16 @@ public class TestModelUpdatesListener buf.append("fLabelUpdates = "); buf.append( toString(fLabelUpdates) ); } - if ( (flags & CONTENT_UPDATES_COMPLETE) != 0) { - buf.append("\n\t"); - buf.append("fViewerUpdatesComplete = " + fViewerUpdatesComplete); - } if ( (flags & VIEWER_UPDATES_RUNNING) != 0) { buf.append("\n\t"); + buf.append("fViewerUpdatesStarted = " + fViewerUpdatesStarted); + buf.append("\n\t"); buf.append("fViewerUpdatesRunning = " + fViewerUpdatesCounter); } + if ( (flags & CONTENT_SEQUENCE_COMPLETE) != 0) { + buf.append("\n\t"); + buf.append("fViewerUpdatesComplete = " + fViewerUpdatesComplete); + } if ( (flags & HAS_CHILDREN_UPDATES_STARTED) != 0) { buf.append("\n\t"); buf.append("fHasChildrenUpdatesRunning = "); @@ -761,8 +831,10 @@ public class TestModelUpdatesListener } public String toString() { - return toString(ALL_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE | STATE_RESTORE_COMPLETE | VIEWER_UPDATES_STARTED | LABEL_UPDATES_STARTED | STATE_UPDATES); + return toString(ALL_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE | STATE_SAVE_COMPLETE | STATE_RESTORE_COMPLETE | ALL_VIEWER_UPDATES_STARTED | LABEL_SEQUENCE_STARTED | STATE_UPDATES); } + + } diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/UpdateTests.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/UpdateTests.java index 2f6a0985a..7e465bdd0 100644 --- a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/UpdateTests.java +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/UpdateTests.java @@ -10,18 +10,27 @@ *******************************************************************************/ package org.eclipe.debug.tests.viewer.model; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + import junit.framework.TestCase; import org.eclipe.debug.tests.viewer.model.TestModel.TestElement; import org.eclipse.core.commands.ExecutionException; -import org.eclipse.debug.internal.ui.viewers.model.ITreeModelViewer; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; 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.ui.PlatformUI; +import org.junit.Assert; /** * Tests to verify that the viewer property updates following changes in the @@ -181,7 +190,7 @@ abstract public class UpdateTests extends TestCase implements ITestModelUpdatesL model.postDelta(delta); if (validate) { - while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | CONTENT_UPDATES_COMPLETE | LABEL_UPDATES_COMPLETE)) + while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | CONTENT_SEQUENCE_COMPLETE | LABEL_SEQUENCE_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); model.validateData(fViewer, TreePath.EMPTY); } else { @@ -198,7 +207,7 @@ abstract public class UpdateTests extends TestCase implements ITestModelUpdatesL model.postDelta(delta); if (validate) { - while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | CONTENT_UPDATES_COMPLETE | LABEL_UPDATES_COMPLETE)) + while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | CONTENT_SEQUENCE_COMPLETE | LABEL_SEQUENCE_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); model.validateData(fViewer, TreePath.EMPTY); } else { @@ -231,6 +240,33 @@ abstract public class UpdateTests extends TestCase implements ITestModelUpdatesL addElement(model, "4-new", 4, true); } + /** + * This test verifies that when the viewer processes a delta that causes viewer + * updates it initiates the model update sequence before it finishes processing + * the delta. + */ + public void testNotifyUpdatesTartedOnModelChanged() throws InterruptedException { + TestModel model = TestModel.simpleSingleLevel(); + fViewer.setAutoExpandLevel(-1); + + // Create the listener + fListener.reset(TreePath.EMPTY, model.getRootElement(), -1, false, false); + + // Set the input into the view and update the view. + fViewer.setInput(model.getRootElement()); + while (!fListener.isFinished()) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + model.validateData(fViewer, TreePath.EMPTY); + + // Refresh the viewer so that updates are generated. + fListener.reset(); + model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT)); + + // Wait for the delta to be processed. + while (!fListener.isFinished(MODEL_CHANGED_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + + Assert.assertTrue( fListener.isFinished(CONTENT_SEQUENCE_STARTED) ); + } + /** * This test case attempts to create a race condition between processing @@ -326,4 +362,186 @@ abstract public class UpdateTests extends TestCase implements ITestModelUpdatesL } + /** + * This test forces the viewer to cancel updates then process them at once. + * <p> + * - Wait until CHILDREN COUNT update started then refresh<br> + * - Process queued updates in order.<br> + * </p> + */ + public void testCanceledUpdates1() throws InterruptedException { + TestModel model = TestModel.simpleSingleLevel(); + fViewer.setAutoExpandLevel(-1); + + // Create the listener + fListener.reset(TreePath.EMPTY, model.getRootElement(), -1, false, false); + + // Set the input into the view and update the view. + fViewer.setInput(model.getRootElement()); + while (!fListener.isFinished()) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + model.validateData(fViewer, TreePath.EMPTY); + + + model.setQeueueingUpdate(true); + + for (int i = 0; i < 5; i++) { + // Refresh the viewer so that updates are generated. + fListener.reset(); + model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT)); + + // Wait for the delta to be processed. + while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | CHILD_COUNT_UPDATES_STARTED)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + + } + + model.setQeueueingUpdate(false); + while (!fListener.isFinished(ALL_UPDATES_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + + } + + /** + * This test forces the viewer to cancel updates then process them at once. + * <p> + * - Wait until CHILDREN COUNT update started then refresh<br> + * - Process queued updates in REVERSE order.<br> + * </p> + */ + public void testCanceledUpdates2() throws InterruptedException { + TestModel model = TestModel.simpleSingleLevel(); + fViewer.setAutoExpandLevel(-1); + + // Create the listener + fListener.reset(TreePath.EMPTY, model.getRootElement(), -1, false, false); + + // Set the input into the view and update the view. + fViewer.setInput(model.getRootElement()); + while (!fListener.isFinished()) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + model.validateData(fViewer, TreePath.EMPTY); + + + model.setQeueueingUpdate(true); + + for (int i = 0; i < 5; i++) { + // Refresh the viewer so that updates are generated. + fListener.reset(); + model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT)); + + // Wait for the delta to be processed. + while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | CHILD_COUNT_UPDATES_STARTED)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + + } + + List updates = new ArrayList(model.getQueuedUpdates()); + model.getQueuedUpdates().clear(); + for (int i = updates.size() - 1; i >= 0; i--) { + model.processUpdate((IViewerUpdate)updates.get(i)); + } + + model.setQeueueingUpdate(false); + while (!fListener.isFinished(ALL_UPDATES_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + } + + /** + * This test forces the viewer to cancel updates then process them at once. + * <p> + * - Wait until CHILDREN update started then refresh<br> + * - Process queued updates in order.<br> + * </p> + */ + public void testCanceledUpdates3() throws InterruptedException { + TestModel model = TestModel.simpleSingleLevel(); + fViewer.setAutoExpandLevel(-1); + + // Create the listener + fListener.reset(TreePath.EMPTY, model.getRootElement(), -1, false, false); + + // Set the input into the view and update the view. + fViewer.setInput(model.getRootElement()); + while (!fListener.isFinished()) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + model.validateData(fViewer, TreePath.EMPTY); + + + model.setQeueueingUpdate(true); + + for (int i = 0; i < 5; i++) { + // Refresh the viewer so that updates are generated. + fListener.reset(); + model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT)); + + // Wait for the delta to be processed. + while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | CHILDREN_UPDATES_STARTED)) { + completeQueuedUpdatesOfType(model, IChildrenCountUpdate.class); + completeQueuedUpdatesOfType(model, IHasChildrenUpdate.class); + if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + } + } + + model.setQeueueingUpdate(false); + while (!fListener.isFinished(ALL_UPDATES_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + + } + + /** + * This test forces the viewer to cancel updates then process them at once. + * <p> + * - Wait until CHILDREN update started then refresh<br> + * - Process queued updates in REVERSE order.<br> + * </p> + */ + public void testCanceledUpdates4() throws InterruptedException { + TestModel model = TestModel.simpleSingleLevel(); + fViewer.setAutoExpandLevel(-1); + + // Create the listener + fListener.reset(TreePath.EMPTY, model.getRootElement(), -1, false, false); + + // Set the input into the view and update the view. + fViewer.setInput(model.getRootElement()); + while (!fListener.isFinished()) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + model.validateData(fViewer, TreePath.EMPTY); + + + model.setQeueueingUpdate(true); + + for (int i = 0; i < 5; i++) { + // Refresh the viewer so that updates are generated. + fListener.reset(); + model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT)); + + // Wait for the delta to be processed. + while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | CHILDREN_UPDATES_STARTED)) { + completeQueuedUpdatesOfType(model, IChildrenCountUpdate.class); + completeQueuedUpdatesOfType(model, IHasChildrenUpdate.class); + if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + } + + } + + List updates = new ArrayList(model.getQueuedUpdates()); + model.getQueuedUpdates().clear(); + for (int i = updates.size() - 1; i >= 0; i--) { + model.processUpdate((IViewerUpdate)updates.get(i)); + } + + model.setQeueueingUpdate(false); + while (!fListener.isFinished(ALL_UPDATES_COMPLETE)) if (!fDisplay.readAndDispatch ()) Thread.sleep(0); + } + + private void completeQueuedUpdatesOfType(TestModel model, Class updateClass) { + List updatesToComplete = new LinkedList(); + + for (Iterator itr = model.getQueuedUpdates().iterator(); itr.hasNext();) { + IViewerUpdate update = (IViewerUpdate)itr.next(); + if (updateClass.isInstance(update)) { + updatesToComplete.add(update); + itr.remove(); + } + } + if (updatesToComplete != null) { + for (Iterator itr = updatesToComplete.iterator(); itr.hasNext();) { + model.processUpdate((IViewerUpdate)itr.next()); + } + } + } + } diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VirtualViewerContentTests.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VirtualViewerContentTests.java index 8cacbe173..994ba3fe7 100644 --- a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VirtualViewerContentTests.java +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VirtualViewerContentTests.java @@ -10,7 +10,7 @@ *******************************************************************************/ package org.eclipe.debug.tests.viewer.model; -import org.eclipse.debug.internal.ui.viewers.model.ITreeModelContentProviderTarget; +import org.eclipse.debug.internal.ui.viewers.model.IInternalTreeModelViewer; 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; @@ -25,7 +25,7 @@ public class VirtualViewerContentTests extends ContentTests { super(name); } - protected ITreeModelContentProviderTarget createViewer(Display display, Shell shell) { + protected IInternalTreeModelViewer createViewer(Display display, Shell shell) { return new VirtualTreeModelViewer(fDisplay, 0, new PresentationContext("TestViewer")); } } diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VirtualViewerDeltaTests.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VirtualViewerDeltaTests.java index f3101784f..8fee48bec 100644 --- a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VirtualViewerDeltaTests.java +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VirtualViewerDeltaTests.java @@ -10,7 +10,7 @@ *******************************************************************************/ package org.eclipe.debug.tests.viewer.model; -import org.eclipse.debug.internal.ui.viewers.model.ITreeModelContentProviderTarget; +import org.eclipse.debug.internal.ui.viewers.model.IInternalTreeModelViewer; 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; @@ -25,7 +25,7 @@ public class VirtualViewerDeltaTests extends DeltaTests { super(name); } - protected ITreeModelContentProviderTarget createViewer(Display display, Shell shell) { + protected IInternalTreeModelViewer createViewer(Display display, Shell shell) { return new VirtualTreeModelViewer(fDisplay, 0, new PresentationContext("TestViewer")); } } diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VirtualViewerFilterTests.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VirtualViewerFilterTests.java new file mode 100644 index 000000000..b53ac539d --- /dev/null +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VirtualViewerFilterTests.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * 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.IInternalTreeModelViewer; +import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualTreeModelViewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +/** + * @since 3.6 + */ +public class VirtualViewerFilterTests extends FilterTests { + + public VirtualViewerFilterTests(String name) { + super(name); + } + + protected IInternalTreeModelViewer createViewer(Display display, Shell shell) { + return new VirtualTreeModelViewer(fDisplay, SWT.VIRTUAL, new PresentationContext("TestViewer"), new VisibleVirtualItemValidator(0, 100)); + } +} 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 2eb78d354..5ea544562 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 @@ -12,7 +12,7 @@ package org.eclipe.debug.tests.viewer.model; import junit.framework.TestCase; -import org.eclipse.debug.internal.ui.viewers.model.ITreeModelViewer; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer; import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext; import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualTreeModelViewer; import org.eclipse.swt.SWT; 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 index 2c4e6cd6b..7bc589bf7 100644 --- 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 @@ -10,7 +10,7 @@ *******************************************************************************/ package org.eclipe.debug.tests.viewer.model; -import org.eclipse.debug.internal.ui.viewers.model.ITreeModelContentProviderTarget; +import org.eclipse.debug.internal.ui.viewers.model.IInternalTreeModelViewer; 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; @@ -25,7 +25,7 @@ public class VirtualViewerPerformanceTests extends PerformanceTests { super(name); } - protected ITreeModelContentProviderTarget createViewer(Display display, Shell shell) { + protected IInternalTreeModelViewer createViewer(Display display, Shell shell) { return new VirtualTreeModelViewer(fDisplay, 0, new PresentationContext("TestViewer")); } diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VirtualViewerPopupTests.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VirtualViewerPopupTests.java index ee8e9c745..ec6f152f8 100644 --- a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VirtualViewerPopupTests.java +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VirtualViewerPopupTests.java @@ -10,7 +10,7 @@ *******************************************************************************/ package org.eclipe.debug.tests.viewer.model; -import org.eclipse.debug.internal.ui.viewers.model.ITreeModelViewer; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer; 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; diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VirtualViewerSelectionTests.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VirtualViewerSelectionTests.java index e716acf03..3bf5b7736 100644 --- a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VirtualViewerSelectionTests.java +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VirtualViewerSelectionTests.java @@ -10,7 +10,7 @@ *******************************************************************************/ package org.eclipe.debug.tests.viewer.model; -import org.eclipse.debug.internal.ui.viewers.model.ITreeModelViewer; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer; 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; diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VirtualViewerStateTests.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VirtualViewerStateTests.java index 5a4e4d671..2c5d5c96c 100644 --- a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VirtualViewerStateTests.java +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VirtualViewerStateTests.java @@ -10,7 +10,7 @@ *******************************************************************************/ package org.eclipe.debug.tests.viewer.model; -import org.eclipse.debug.internal.ui.viewers.model.ITreeModelViewer; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer; 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; diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VirtualViewerUpdateTests.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VirtualViewerUpdateTests.java index 507be5192..5086fab2c 100644 --- a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VirtualViewerUpdateTests.java +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VirtualViewerUpdateTests.java @@ -10,7 +10,7 @@ *******************************************************************************/ package org.eclipe.debug.tests.viewer.model; -import org.eclipse.debug.internal.ui.viewers.model.ITreeModelViewer; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer; 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; diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VisibleVirtualItemValidator.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VisibleVirtualItemValidator.java new file mode 100644 index 000000000..b2c122440 --- /dev/null +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/VisibleVirtualItemValidator.java @@ -0,0 +1,95 @@ +/******************************************************************************* + * 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.provisional.IVirtualItemValidator; +import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualItem; +import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualTree; + +/** + * Item validator for the virtual viewer which specifies that the given + * range of items should be treated as visible. + */ +public class VisibleVirtualItemValidator implements IVirtualItemValidator { + + private int fStart = 0; + private int fEnd = 0; + + public VisibleVirtualItemValidator(int startPosition, int length) { + setVisibleRange(startPosition, length); + } + + public void setVisibleRange(int startPosition, int length) { + fStart = startPosition; + fEnd = startPosition + length; + } + + public int getStartPosition() { + return fStart; + } + + public int getLength() { + return fEnd - fStart; + } + + public boolean isItemVisible(VirtualItem item) { + int position = 0; + while (item.getParent() != null) { + position += item.getIndex().intValue(); + item = item.getParent(); + } + return position >= fStart && position < fEnd || isSelected(item); + } + + public void showItem(VirtualItem item) { + int length = fEnd - fStart; + fStart = calcPosition(item); + fEnd = fStart + length; + } + + private int calcPosition(VirtualItem item) { + int position = 0; + while (item.getParent() != null) { + position += item.getIndex().intValue(); + item = item.getParent(); + } + return position; + } + + private boolean isSelected(VirtualItem item) { + VirtualItem[] selection = getSelection(item); + for (int i = 0; i < selection.length; i++) { + VirtualItem selectionItem = selection[i]; + while (selectionItem != null) { + if (item.equals(selectionItem)) { + return true; + } + selectionItem = selectionItem.getParent(); + } + } + return false; + } + + private VirtualItem[] getSelection(VirtualItem item) { + VirtualTree tree = getTree(item); + if (tree != null) { + return tree.getSelection(); + } + return new VirtualItem[0]; + } + + private VirtualTree getTree(VirtualItem item) { + while (item != null && !(item instanceof VirtualTree)) { + item = item.getParent(); + } + return (VirtualTree)item; + } +} 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 82ba80b7a..a3cba9bf3 100644 --- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java +++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java @@ -25,6 +25,7 @@ import org.eclipe.debug.tests.viewer.model.FilterTransformTests; import org.eclipe.debug.tests.viewer.model.PresentationContextTests; import org.eclipe.debug.tests.viewer.model.VirtualViewerContentTests; import org.eclipe.debug.tests.viewer.model.VirtualViewerDeltaTests; +import org.eclipe.debug.tests.viewer.model.VirtualViewerFilterTests; import org.eclipe.debug.tests.viewer.model.VirtualViewerLazyModeTests; import org.eclipe.debug.tests.viewer.model.VirtualViewerSelectionTests; import org.eclipe.debug.tests.viewer.model.VirtualViewerStateTests; @@ -62,6 +63,7 @@ public class AutomatedSuite extends TestSuite { addTest(new TestSuite(VirtualViewerSelectionTests.class)); addTest(new TestSuite(VirtualViewerStateTests.class)); addTest(new TestSuite(VirtualViewerUpdateTests.class)); + addTest(new TestSuite(VirtualViewerFilterTests.class)); // Viewer neutral tests addTest(new TestSuite(FilterTransformTests.class)); diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/LocalSuite.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/LocalSuite.java index 55acd5731..6098eb840 100644 --- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/LocalSuite.java +++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/LocalSuite.java @@ -17,6 +17,7 @@ import org.eclipe.debug.tests.viewer.model.ColumnPresentationTests; import org.eclipe.debug.tests.viewer.model.JFaceViewerCheckTests; import org.eclipe.debug.tests.viewer.model.JFaceViewerContentTests; import org.eclipe.debug.tests.viewer.model.JFaceViewerDeltaTests; +import org.eclipe.debug.tests.viewer.model.JFaceViewerFilterTests; import org.eclipe.debug.tests.viewer.model.JFaceViewerLazyTests; import org.eclipe.debug.tests.viewer.model.JFaceViewerSelectionTests; import org.eclipe.debug.tests.viewer.model.JFaceViewerStateTests; @@ -53,6 +54,7 @@ public class LocalSuite extends TestSuite { addTest(new TestSuite(JFaceViewerUpdateTests.class)); addTest(new TestSuite(JFaceViewerLazyTests.class)); addTest(new TestSuite(JFaceViewerTopIndexTests.class)); + addTest(new TestSuite(JFaceViewerFilterTests.class)); addTest(new TestSuite(ColumnPresentationTests.class)); } } diff --git a/org.eclipse.debug.ui/.settings/.api_filters b/org.eclipse.debug.ui/.settings/.api_filters index 42b56487b..de6e83394 100644 --- a/org.eclipse.debug.ui/.settings/.api_filters +++ b/org.eclipse.debug.ui/.settings/.api_filters @@ -1,74 +1,58 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<component id="org.eclipse.debug.ui" version="2">
- <resource path="ui/org/eclipse/debug/internal/ui/launchConfigurations/LaunchConfigurationViewer.java" type="org.eclipse.debug.internal.ui.launchConfigurations.LaunchConfigurationViewer">
- <filter comment="Known illegal extension" id="571473929">
- <message_arguments>
- <message_argument value="TreeViewer"/>
- <message_argument value="LaunchConfigurationViewer"/>
- </message_arguments>
- </filter>
- </resource>
- <resource path="ui/org/eclipse/debug/internal/ui/preferences/LaunchPerspectivePreferencePage.java" type="org.eclipse.debug.internal.ui.preferences.LaunchPerspectivePreferencePage$PerspectivesTreeViewer">
- <filter comment="Known illegal extension" id="571473929">
- <message_arguments>
- <message_argument value="TreeViewer"/>
- <message_argument value="PerspectivesTreeViewer"/>
- </message_arguments>
- </filter>
- </resource>
- <resource path="ui/org/eclipse/debug/internal/ui/sourcelookup/SourceContainerViewer.java" type="org.eclipse.debug.internal.ui.sourcelookup.SourceContainerViewer">
- <filter comment="Known illegal extension" id="571473929">
- <message_arguments>
- <message_argument value="TreeViewer"/>
- <message_argument value="SourceContainerViewer"/>
- </message_arguments>
- </filter>
- </resource>
- <resource path="ui/org/eclipse/debug/internal/ui/sourcelookup/browsers/ProjectSourceContainerDialog.java" type="org.eclipse.debug.internal.ui.sourcelookup.browsers.ProjectSourceContainerDialog">
- <filter comment="Known illegal extension" id="571473929">
- <message_arguments>
- <message_argument value="ListSelectionDialog"/>
- <message_argument value="ProjectSourceContainerDialog"/>
- </message_arguments>
- </filter>
- </resource>
- <resource path="ui/org/eclipse/debug/internal/ui/viewers/model/InternalTreeModelViewer.java" type="org.eclipse.debug.internal.ui.viewers.model.InternalTreeModelViewer">
- <filter comment="Known illegal extension" id="571473929">
- <message_arguments>
- <message_argument value="TreeViewer"/>
- <message_argument value="InternalTreeModelViewer"/>
- </message_arguments>
- </filter>
- </resource>
- <resource path="ui/org/eclipse/debug/internal/ui/viewers/model/TreeCursor.java" type="org.eclipse.debug.internal.ui.viewers.model.TreeCursor">
- <filter comment="Known illegal reference" id="640708718">
- <message_arguments>
- <message_argument value="TypedListener(SWTEventListener)"/>
- <message_argument value="TreeCursor"/>
- </message_arguments>
- </filter>
- </resource>
- <resource path="ui/org/eclipse/debug/internal/ui/views/breakpoints/BreakpointsViewer.java" type="org.eclipse.debug.internal.ui.views.breakpoints.BreakpointsViewer">
- <filter comment="Known illegal extension" id="571473929">
- <message_arguments>
- <message_argument value="CheckboxTreeViewer"/>
- <message_argument value="BreakpointsViewer"/>
- </message_arguments>
- </filter>
- </resource>
- <resource path="ui/org/eclipse/debug/ui/actions/AbstractLaunchHistoryAction.java" type="org.eclipse.debug.ui.actions.AbstractLaunchHistoryAction">
- <filter comment="Known leaked internal types" id="576725006">
- <message_arguments>
- <message_argument value="ILaunchHistoryChangedListener"/>
- <message_argument value="AbstractLaunchHistoryAction"/>
- </message_arguments>
- </filter>
- <filter comment="Known leaked internal types" id="643842064">
- <message_arguments>
- <message_argument value="LaunchHistory"/>
- <message_argument value="AbstractLaunchHistoryAction"/>
- <message_argument value="getLaunchHistory()"/>
- </message_arguments>
- </filter>
- </resource>
-</component>
+<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<component id="org.eclipse.debug.ui" version="2"> + <resource path="ui/org/eclipse/debug/internal/ui/launchConfigurations/LaunchConfigurationViewer.java" type="org.eclipse.debug.internal.ui.launchConfigurations.LaunchConfigurationViewer"> + <filter comment="Known illegal extension" id="571473929"> + <message_arguments> + <message_argument value="TreeViewer"/> + <message_argument value="LaunchConfigurationViewer"/> + </message_arguments> + </filter> + </resource> + <resource path="ui/org/eclipse/debug/internal/ui/preferences/LaunchPerspectivePreferencePage.java" type="org.eclipse.debug.internal.ui.preferences.LaunchPerspectivePreferencePage$PerspectivesTreeViewer"> + <filter comment="Known illegal extension" id="571473929"> + <message_arguments> + <message_argument value="TreeViewer"/> + <message_argument value="PerspectivesTreeViewer"/> + </message_arguments> + </filter> + </resource> + <resource path="ui/org/eclipse/debug/internal/ui/sourcelookup/SourceContainerViewer.java" type="org.eclipse.debug.internal.ui.sourcelookup.SourceContainerViewer"> + <filter comment="Known illegal extension" id="571473929"> + <message_arguments> + <message_argument value="TreeViewer"/> + <message_argument value="SourceContainerViewer"/> + </message_arguments> + </filter> + </resource> + <resource path="ui/org/eclipse/debug/internal/ui/sourcelookup/browsers/ProjectSourceContainerDialog.java" type="org.eclipse.debug.internal.ui.sourcelookup.browsers.ProjectSourceContainerDialog"> + <filter comment="Known illegal extension" id="571473929"> + <message_arguments> + <message_argument value="ListSelectionDialog"/> + <message_argument value="ProjectSourceContainerDialog"/> + </message_arguments> + </filter> + </resource> + <resource path="ui/org/eclipse/debug/internal/ui/views/breakpoints/BreakpointsViewer.java" type="org.eclipse.debug.internal.ui.views.breakpoints.BreakpointsViewer"> + <filter comment="Known illegal extension" id="571473929"> + <message_arguments> + <message_argument value="CheckboxTreeViewer"/> + <message_argument value="BreakpointsViewer"/> + </message_arguments> + </filter> + </resource> + <resource path="ui/org/eclipse/debug/ui/actions/AbstractLaunchHistoryAction.java" type="org.eclipse.debug.ui.actions.AbstractLaunchHistoryAction"> + <filter comment="Known leaked internal types" id="576725006"> + <message_arguments> + <message_argument value="ILaunchHistoryChangedListener"/> + <message_argument value="AbstractLaunchHistoryAction"/> + </message_arguments> + </filter> + <filter comment="Known leaked internal types" id="643842064"> + <message_arguments> + <message_argument value="LaunchHistory"/> + <message_argument value="AbstractLaunchHistoryAction"/> + <message_argument value="getLaunchHistory()"/> + </message_arguments> + </filter> + </resource> +</component> diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/actions/ActionMessages.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/actions/ActionMessages.java index 5c3a331ac..d4ed16c31 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/actions/ActionMessages.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/actions/ActionMessages.java @@ -228,8 +228,8 @@ public class ActionMessages extends NLS { public static String ShowTypesAction_0; public static String VirtualFindAction_0; - public static String VirtualFindAction_1; + public static String VirtualFindAction_2; public static String ToggleBreakpointsTargetManager_defaultToggleTarget_name; public static String ToggleBreakpointsTargetManager_defaultToggleTarget_description; diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/actions/ActionMessages.properties b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/actions/ActionMessages.properties index ad9f0e542..7401d0707 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/actions/ActionMessages.properties +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/actions/ActionMessages.properties @@ -212,6 +212,7 @@ RunLastAction_3=Run the previously launched application OpenLaunchDialogAction_1={0} Configurations... VirtualFindAction_0=Error VirtualFindAction_1=Unable to locate {0} in viewer +VirtualFindAction_2=Element no longer in viewer. ToggleBreakpointsTargetManager_defaultToggleTarget_name = Default ToggleBreakpointsTargetManager_defaultToggleTarget_description = Default 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 e8201125f..561697522 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 @@ -17,7 +17,6 @@ import java.util.List; import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; import org.eclipse.jface.viewers.TreePath; /** @@ -25,23 +24,101 @@ import org.eclipse.jface.viewers.TreePath; */ class ChildrenCountUpdate extends ViewerUpdateMonitor implements IChildrenCountUpdate { + /** + * Child count result. + */ private int fCount = 0; + /** + * Other child count updates for the same content provider. Coalesced requests are + * batched together into an array. + */ private List fBatchedRequests = null; + /** + * Flag whether filtering is enabled in viewer. If filtering is enabled, then a + * children update is performed on child elements to filter them as part of the + * child count calculation. + */ + private boolean fShouldFilter = false; + + /** + * Children indexes which are currently filtered. When updating child count, also need + * to verify that currently filtered children are still filtered. + */ + private int[] fFilteredChildren = null; + + /** + * Children update used to filter children. + */ + private ChildrenUpdate fChildrenUpdate; + /** * Constructor - * @param contentProvider the content provider to use for the update + * @param provider the content provider to use for the update * @param viewerInput the current input * @param elementPath the path of the element to update * @param element the element to update * @param elementContentProvider the content provider for the element - * @param context the presentation context */ - public ChildrenCountUpdate(ModelContentProvider contentProvider, Object viewerInput, TreePath elementPath, Object element, IElementContentProvider elementContentProvider, IPresentationContext context) { - super(contentProvider, viewerInput, elementPath, element, elementContentProvider, context); + public ChildrenCountUpdate(TreeModelContentProvider provider, Object viewerInput, TreePath elementPath, Object element, IElementContentProvider elementContentProvider) { + super(provider, viewerInput, elementPath, element, elementContentProvider, provider.getPresentationContext()); + fShouldFilter = provider.areTreeModelViewerFiltersApplicable(element); + fFilteredChildren = provider.getFilteredChildren(elementPath); + } + + public synchronized void cancel() { + if (fChildrenUpdate != null) { + fChildrenUpdate.cancel(); + } + super.cancel(); } + protected synchronized void scheduleViewerUpdate() { + // If filtering is enabled perform child update on all children in order to update + // viewer filters. + if (fShouldFilter || fFilteredChildren != null) { + if (fChildrenUpdate == null) { + int startIdx; + int count; + if (fShouldFilter) { + startIdx = 0; + count = getCount(); + } else { + startIdx = fFilteredChildren[0]; + int endIdx = fFilteredChildren[fFilteredChildren.length - 1]; + count = endIdx - startIdx + 1; + } + + fChildrenUpdate = new ChildrenUpdate(getContentProvider(), getViewerInput(), getElementPath(), getElement(), startIdx, count, getElementContentProvider()) { + protected void performUpdate() { + performUpdate(true); + ChildrenCountUpdate.super.scheduleViewerUpdate(); + } + + protected void scheduleViewerUpdate() { + execInDisplayThread(new Runnable() { + public void run() { + if (!getContentProvider().isDisposed() && !isCanceled()) { + performUpdate(); + } + } + }); + } + }; + execInDisplayThread(new Runnable() { + public void run() { + fChildrenUpdate.startRequest(); + } + }); + return; + } + } else { + super.scheduleViewerUpdate(); + } + } + + /* (non-Javadoc) * @see org.eclipse.debug.internal.ui.viewers.model.provisional.viewers.ViewerUpdateMonitor#performUpdate() */ @@ -54,11 +131,17 @@ class ChildrenCountUpdate extends ViewerUpdateMonitor implements IChildrenCountU getContentProvider().setModelChildCount(elementPath, fCount); viewCount = getContentProvider().modelToViewChildCount(elementPath, fCount); } - if (ModelContentProvider.DEBUG_CONTENT_PROVIDER && ModelContentProvider.DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { + if (TreeModelContentProvider.DEBUG_CONTENT_PROVIDER && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { System.out.println("setChildCount(" + getElement() + ", modelCount: " + fCount + " viewCount: " + viewCount + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ } - getContentProvider().getViewer().setChildCount(elementPath, viewCount); - getContentProvider().restorePendingStateOnUpdate(getElementPath(), -1, true, true, false); + // Special case for element 0 in a set of filtered elements: + // Child 0 is automatically updated by the tree at the same time that the child count is requested. Therefore, + // If this child count update filtered out this element, it needs to be updated again. + if (fShouldFilter && getContentProvider().isFiltered(elementPath, 0)) { + getContentProvider().updateElement(elementPath, 0); + } + getContentProvider().getViewer().setChildCount(elementPath, viewCount); + getContentProvider().getStateTracker().restorePendingStateOnUpdate(getElementPath(), -1, true, true, false); } public void setChildCount(int numChildren) { @@ -140,4 +223,19 @@ class ChildrenCountUpdate extends ViewerUpdateMonitor implements IChildrenCountU } return path; } + + int getCount() { + return fCount; + } + + protected boolean doEquals(ViewerUpdateMonitor update) { + return + update instanceof ChildrenCountUpdate && + getViewerInput().equals(update.getViewerInput()) && + getElementPath().equals(getElementPath()); + } + + protected int doHashCode() { + return getClass().hashCode() + getViewerInput().hashCode() + getElementPath().hashCode(); + } } 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 6375f6bdd..766c7601f 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 @@ -14,7 +14,6 @@ package org.eclipse.debug.internal.ui.viewers.model; import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; import org.eclipse.jface.viewers.TreePath; /** @@ -37,24 +36,25 @@ public class ChildrenUpdate extends ViewerUpdateMonitor implements IChildrenUpda * @param element the element * @param index the index of the element * @param elementContentProvider the content provider for the element - * @param context the presentation context */ - public ChildrenUpdate(ModelContentProvider provider, Object viewerInput, TreePath elementPath, Object element, int index, IElementContentProvider elementContentProvider, IPresentationContext context) { - super(provider, viewerInput, elementPath, element, elementContentProvider, context); + public ChildrenUpdate(TreeModelContentProvider provider, Object viewerInput, TreePath elementPath, Object element, int index, IElementContentProvider elementContentProvider) { + super(provider, viewerInput, elementPath, element, elementContentProvider, provider.getPresentationContext()); fIndex = index; fLength = 1; } - - /* - * (non-Javadoc) - * - * @see org.eclipse.debug.ui.viewers.AsynchronousRequestMonitor#performUpdate() - */ - protected void performUpdate() { - TreeModelContentProvider provider = (TreeModelContentProvider) getContentProvider(); + + public ChildrenUpdate(TreeModelContentProvider provider, Object viewerInput, TreePath elementPath, Object element, int index, int length, IElementContentProvider elementContentProvider) { + super(provider, viewerInput, elementPath, element, elementContentProvider, provider.getPresentationContext()); + fIndex = index; + fLength = length; + } + + + protected void performUpdate(boolean updateFilterOnly) { + TreeModelContentProvider provider = getContentProvider(); TreePath elementPath = getElementPath(); if (fElements != null) { - ITreeModelContentProviderTarget viewer = provider.getViewer(); + IInternalTreeModelViewer viewer = provider.getViewer(); for (int i = 0; i < fElements.length; i++) { int modelIndex = fIndex + i; Object element = fElements[i]; @@ -62,36 +62,54 @@ public class ChildrenUpdate extends ViewerUpdateMonitor implements IChildrenUpda int viewIndex = provider.modelToViewIndex(elementPath, modelIndex); if (provider.shouldFilter(elementPath, element)) { if (provider.addFilteredIndex(elementPath, modelIndex, element)) { - if (ModelContentProvider.DEBUG_CONTENT_PROVIDER && ModelContentProvider.DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("REMOVE(" + getElement() + ", modelIndex: " + modelIndex + " viewIndex: " + viewIndex + ", " + element + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ - } - viewer.remove(elementPath, viewIndex); + if (!updateFilterOnly) { + if (TreeModelContentProvider.DEBUG_CONTENT_PROVIDER && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { + System.out.println("REMOVE(" + getElement() + ", modelIndex: " + modelIndex + " viewIndex: " + viewIndex + ", " + element + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + } + viewer.remove(elementPath, viewIndex); + } } } else { if (provider.isFiltered(elementPath, modelIndex)) { provider.clearFilteredChild(elementPath, modelIndex); - int insertIndex = provider.modelToViewIndex(elementPath, modelIndex); - if (ModelContentProvider.DEBUG_CONTENT_PROVIDER) { - System.out.println("insert(" + getElement() + ", modelIndex: " + modelIndex + " insertIndex: " + insertIndex + ", " + element + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ - } - viewer.insert(elementPath, element, insertIndex); - } else { - if (ModelContentProvider.DEBUG_CONTENT_PROVIDER && ModelContentProvider.DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { + if (!updateFilterOnly) { + int insertIndex = provider.modelToViewIndex(elementPath, modelIndex); + if (TreeModelContentProvider.DEBUG_CONTENT_PROVIDER) { + System.out.println("insert(" + getElement() + ", modelIndex: " + modelIndex + " insertIndex: " + insertIndex + ", " + element + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + } + viewer.insert(elementPath, element, insertIndex); + } + } else if (!updateFilterOnly){ + if (TreeModelContentProvider.DEBUG_CONTENT_PROVIDER && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { System.out.println("replace(" + getElement() + ", modelIndex: " + modelIndex + " viewIndex: " + viewIndex + ", " + element + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ } viewer.replace(elementPath, viewIndex, element); } - TreePath childPath = elementPath.createChildPath(element); - provider.updateHasChildren(childPath); - provider.restorePendingStateOnUpdate(childPath, modelIndex, false, false, false); + if (!updateFilterOnly) { + TreePath childPath = elementPath.createChildPath(element); + provider.updateHasChildren(childPath); + provider.getStateTracker().restorePendingStateOnUpdate(childPath, modelIndex, false, false, false); + } } } } - provider.restorePendingStateOnUpdate(elementPath, -1, true, true, true); - } else { + if (!updateFilterOnly) { + provider.getStateTracker().restorePendingStateOnUpdate(elementPath, -1, true, true, true); + } + } else if (!updateFilterOnly) { provider.updateHasChildren(elementPath); } + + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.ui.viewers.AsynchronousRequestMonitor#performUpdate() + */ + protected void performUpdate() { + performUpdate(false); } /* (non-Javadoc) @@ -122,7 +140,7 @@ public class ChildrenUpdate extends ViewerUpdateMonitor implements IChildrenUpda fIndex = Math.min(fIndex, otherStart); end = Math.max(end, otherEnd); fLength = end - fIndex; - if (ModelContentProvider.DEBUG_CONTENT_PROVIDER && ModelContentProvider.DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { + if (TreeModelContentProvider.DEBUG_CONTENT_PROVIDER && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { System.out.println("coalesced: " + this.toString()); //$NON-NLS-1$ } return true; @@ -166,7 +184,7 @@ public class ChildrenUpdate extends ViewerUpdateMonitor implements IChildrenUpda buf.append(" {"); //$NON-NLS-1$ buf.append(getOffset()); buf.append("->"); //$NON-NLS-1$ - buf.append(getOffset() + getLength()); + buf.append(getOffset() + getLength() - 1); buf.append("}"); //$NON-NLS-1$ return buf.toString(); } @@ -194,5 +212,25 @@ public class ChildrenUpdate extends ViewerUpdateMonitor implements IChildrenUpda void setOffset(int offset) { fIndex = offset; } -} + + Object[] getElements() { + return fElements; + } + + protected boolean doEquals(ViewerUpdateMonitor update) { + return + update instanceof ChildrenUpdate && + ((ChildrenUpdate)update).getOffset() == getOffset() && + ((ChildrenUpdate)update).getLength() == getLength() && + getViewerInput().equals(update.getViewerInput()) && + getElementPath().equals(getElementPath()); + } + + protected int doHashCode() { + return (int)Math.pow( + (getClass().hashCode() + getViewerInput().hashCode() + getElementPath().hashCode()) * (getOffset() + 2), + + getLength() + 2); + } +} 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 ffa062c1e..4aea1a9c2 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 @@ -11,14 +11,11 @@ *******************************************************************************/ package org.eclipse.debug.internal.ui.viewers.model; -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.provisional.IElementCompareRequest; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer; import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; import org.eclipse.jface.viewers.TreePath; import org.eclipse.ui.IMemento; -import org.eclipse.ui.progress.UIJob; /** * @since 3.3 @@ -33,20 +30,29 @@ public class ElementCompareRequest extends MementoUpdate implements IElementComp private boolean fCheckChildrenRealized; - /** - * @param context - * @param element - * @param memento - */ - public ElementCompareRequest(ModelContentProvider provider, Object viewerInput, Object element, + /** + * @param provider the content provider to use for the update + * @param viewerInput the current input + * @param element the element to update + * @param elementPath the path of the element to update + * @param memento Memento to encode result into + * @param delta Delta to write the result comparison into. + * @param modelIndex Index of element to compare. + * @param knowsHasChildren Flag indicating whether provider knows the has + * children state of element. + * @param knowsChildCount Flag indicating whether provider knows the + * child count state of element. + * @param checkChildrenRealized Flag indicating if any realized children should be checked + */ + public ElementCompareRequest(TreeModelContentProvider provider, Object viewerInput, Object element, TreePath elementPath, IMemento memento, ModelDelta delta, int modelIndex, - boolean hasChildren, boolean knowsChildCount, boolean checkChildrenRealized) + boolean knowsHasChildren, boolean knowsChildCount, boolean checkChildrenRealized) { super(provider, viewerInput, provider.getPresentationContext(), element, elementPath, memento); fProvider = provider; fDelta = delta; fModelIndex = modelIndex; - fKnowsHasChildren = hasChildren; + fKnowsHasChildren = knowsHasChildren; fKnowsChildCount = knowsChildCount; fCheckChildrenRealized = checkChildrenRealized; } @@ -62,17 +68,19 @@ public class ElementCompareRequest extends MementoUpdate implements IElementComp * @see org.eclipse.core.runtime.IProgressMonitor#done() */ public void done() { - UIJob job = new UIJob("restore delta") { //$NON-NLS-1$ - public IStatus runInUIThread(IProgressMonitor monitor) { - if (!isCanceled()) { - fProvider.compareFinished(ElementCompareRequest.this, fDelta); - } - return Status.OK_STATUS; - } - }; - job.setSystem(true); - job.schedule(); - } + ITreeModelViewer viewer = getContentProvider().getViewer(); + if (viewer == null) return; // disposed + if (viewer.getDisplay().getThread() == Thread.currentThread()) { + fProvider.getStateTracker().compareFinished(ElementCompareRequest.this, fDelta); + } else { + viewer.getDisplay().asyncExec(new Runnable() { + public void run() { + if (getContentProvider().isDisposed()) return; + fProvider.getStateTracker().compareFinished(ElementCompareRequest.this, fDelta); + } + }); + } + } public boolean isEqual() { return fEqual; @@ -86,6 +94,7 @@ public class ElementCompareRequest extends MementoUpdate implements IElementComp return fModelIndex; } + void setKnowsHasChildren(boolean hasChildren) { fKnowsHasChildren = hasChildren; } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ElementMementoRequest.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ElementMementoRequest.java index 8a09d52e7..684169b5b 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ElementMementoRequest.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ElementMementoRequest.java @@ -10,28 +10,35 @@ *******************************************************************************/ package org.eclipse.debug.internal.ui.viewers.model; +import org.eclipse.debug.internal.ui.viewers.model.ViewerStateTracker.IElementMementoCollector; import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoRequest; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer; import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; import org.eclipse.jface.viewers.TreePath; import org.eclipse.ui.IMemento; /** + * Request for element memento. + * * @since 3.3 */ class ElementMementoRequest extends MementoUpdate implements IElementMementoRequest { - private IMementoManager fManager; + private IElementMementoCollector fManager; private ModelDelta fDelta; /** - * @param context - * @param element - * @param memento + * @param provider the content provider to use for the update + * @param viewerInput the current input + * @param collector Collector to report the result to + * @param element the element to update + * @param elementPath the path of the element to update + * @param memento Memento to encode result into + * @param delta Delta to write the result comparison into. */ - public ElementMementoRequest(ModelContentProvider provider, Object viewerInput, IMementoManager manager, IPresentationContext context, Object element, TreePath elementPath, IMemento memento, ModelDelta delta) { - super(provider, viewerInput, context, element, elementPath, memento); - fManager = manager; + public ElementMementoRequest(TreeModelContentProvider provider, Object viewerInput, IElementMementoCollector collector, Object element, TreePath elementPath, IMemento memento, ModelDelta delta) { + super(provider, viewerInput, provider.getPresentationContext(), element, elementPath, memento); + fManager = collector; fDelta = delta; } @@ -39,11 +46,29 @@ class ElementMementoRequest extends MementoUpdate implements IElementMementoRequ * @see org.eclipse.core.runtime.IProgressMonitor#done() */ public void done() { - if (!isCanceled() && (getStatus() == null || getStatus().isOK())) { - // replace the element with a memento - fDelta.setElement(getMemento()); + + ITreeModelViewer viewer = getContentProvider().getViewer(); + if (viewer == null) return; // disposed + if (viewer.getDisplay().getThread() == Thread.currentThread()) { + doComplete(); + } else { + viewer.getDisplay().asyncExec(new Runnable() { + public void run() { + doComplete(); + } + }); } - fManager.requestComplete(this); + + } + + private void doComplete() { + if (getContentProvider().isDisposed()) return; + + if (!isCanceled() && (getStatus() == null || getStatus().isOK())) { + // replace the element with a memento + fDelta.setElement(getMemento()); + } + fManager.requestComplete(ElementMementoRequest.this); } public String toString() { diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/FilterTransform.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/FilterTransform.java index 547a7ea5c..9c64eeec9 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/FilterTransform.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/FilterTransform.java @@ -334,14 +334,14 @@ public class FilterTransform { * @return whether the filter was added - returns <code>true</code> if the filter is * added, and <code>false</code> if the index was already filtered */ - public synchronized boolean addFilteredIndex(TreePath parentPath, int childIndex, Object element) { + public boolean addFilteredIndex(TreePath parentPath, int childIndex, Object element) { return root.addFilter(parentPath, childIndex, 0, element); } /** * Clears all filtered elements. */ - public synchronized void clear() { + public void clear() { root = new Node(); } @@ -350,7 +350,7 @@ public class FilterTransform { * * @param path element path */ - public synchronized void clear(TreePath path) { + public void clear(TreePath path) { root.clear(path, 0); } @@ -358,13 +358,21 @@ public class FilterTransform { * Clears the given filtered index of the specified parent. I.e. * the child still exists, but is no longer filtered. * - * @param path parent path + * @param parentPath parent path * @param index index to clear */ - public synchronized void clear(TreePath parentPath, int index) { + public void clear(TreePath parentPath, int index) { root.clear(parentPath, index, 0); } + public int indexOfFilteredElement(TreePath parentPath, Object element) { + Node parentNode = root.find(parentPath, 0); + if (parentNode == null) { + return -1; + } + return parentNode.indexOfFilteredElement(element); + } + /** * Translates and returns the given model index (raw index) into * a view index (filtered index), or -1 if filtered. @@ -373,7 +381,7 @@ public class FilterTransform { * @param childIndex index of child element in model space * @return the given index in view coordinates, or -1 if filtered. */ - public synchronized int modelToViewIndex(TreePath parentPath, int childIndex) { + public int modelToViewIndex(TreePath parentPath, int childIndex) { Node parentNode = root.find(parentPath, 0); if (parentNode == null) { return childIndex; @@ -389,7 +397,7 @@ public class FilterTransform { * @param childIndex index of child element in view space * @return the given index in model coordinates */ - public synchronized int viewToModelIndex(TreePath parentPath, int childIndex) { + public int viewToModelIndex(TreePath parentPath, int childIndex) { Node parentNode = root.find(parentPath, 0); if (parentNode == null) { return childIndex; @@ -404,7 +412,7 @@ public class FilterTransform { * @param viewCount number of children in the view * @return number of children in the model */ - public synchronized int viewToModelCount(TreePath parentPath, int viewCount) { + public int viewToModelCount(TreePath parentPath, int viewCount) { Node parentNode = root.find(parentPath, 0); if (parentNode != null) { if (parentNode.filteredIndexes != null) { @@ -422,7 +430,7 @@ public class FilterTransform { * @param count child count in model space * @return the given count in view coordinates */ - public synchronized int modelToViewCount(TreePath parentPath, int count) { + public int modelToViewCount(TreePath parentPath, int count) { Node parentNode = root.find(parentPath, 0); if (parentNode == null) { return count; @@ -437,7 +445,7 @@ public class FilterTransform { * @param index index of child element * @return whether the child is currently filtered */ - public synchronized boolean isFiltered(TreePath parentPath, int index) { + public boolean isFiltered(TreePath parentPath, int index) { Node parentNode = root.find(parentPath, 0); if (parentNode == null) { return false; @@ -448,7 +456,7 @@ public class FilterTransform { /** * Returns filtered children of the given parent, or <code>null</code> if none. * - * @param parentPath + * @param parentPath Path of parent element * @return filtered children or <code>null</code> */ public int[] getFilteredChildren(TreePath parentPath) { @@ -465,7 +473,7 @@ public class FilterTransform { * @param parentPath path to parent element * @param childCount child count */ - public synchronized void setModelChildCount(TreePath parentPath, int childCount) { + public void setModelChildCount(TreePath parentPath, int childCount) { Node parentNode = root.find(parentPath, 0); if (parentNode != null) { parentNode.setModelChildCount(childCount); @@ -479,7 +487,7 @@ public class FilterTransform { * @param parentPath path to parent element * @param index index of child element in model coordinates */ - public synchronized void removeElementFromFilters(TreePath parentPath, int index) { + public void removeElementFromFilters(TreePath parentPath, int index) { Node parentNode = root.find(parentPath, 0); if (parentNode != null) { parentNode.removeElementFromFilters(index); @@ -492,8 +500,9 @@ public class FilterTransform { * * @param parentPath path to parent element * @param element removed element + * @return true if element was removed */ - public synchronized boolean removeElementFromFilters(TreePath parentPath, Object element) { + public boolean removeElementFromFilters(TreePath parentPath, Object element) { Node parentNode = root.find(parentPath, 0); if (parentNode != null) { int index = parentNode.indexOfFilteredElement(element); 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 e1437c988..a237a90b4 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 @@ -17,7 +17,6 @@ import java.util.List; import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider; import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; import org.eclipse.jface.viewers.TreePath; /** @@ -29,23 +28,29 @@ class HasChildrenUpdate extends ViewerUpdateMonitor implements IHasChildrenUpdat private List fBatchedRequests = null; - /** - * @param contentProvider - */ - public HasChildrenUpdate(ModelContentProvider contentProvider, Object viewerInput, TreePath elementPath, Object element, IElementContentProvider elementContentProvider, IPresentationContext context) { - super(contentProvider, viewerInput, elementPath, element, elementContentProvider, context); + /** + * Constructs a request to update an element + * + * @param provider the content provider + * @param viewerInput the current input + * @param elementPath the path to the element being update + * @param element the element + * @param elementContentProvider the content provider for the element + */ + public HasChildrenUpdate(TreeModelContentProvider provider, Object viewerInput, TreePath elementPath, Object element, IElementContentProvider elementContentProvider) { + super(provider, viewerInput, elementPath, element, elementContentProvider, provider.getPresentationContext()); } /* (non-Javadoc) * @see org.eclipse.debug.internal.ui.viewers.model.provisional.viewers.ViewerUpdateMonitor#performUpdate() */ protected void performUpdate() { - ModelContentProvider contentProvider = getContentProvider(); + TreeModelContentProvider contentProvider = getContentProvider(); TreePath elementPath = getElementPath(); if (!fHasChildren) { contentProvider.clearFilters(elementPath); } - if (ModelContentProvider.DEBUG_CONTENT_PROVIDER && ModelContentProvider.DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { + if (TreeModelContentProvider.DEBUG_CONTENT_PROVIDER && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { System.out.println("setHasChildren(" + getElement() + " >> " + fHasChildren); //$NON-NLS-1$ //$NON-NLS-2$ } contentProvider.getViewer().setHasChildren(elementPath, fHasChildren); @@ -53,7 +58,7 @@ class HasChildrenUpdate extends ViewerUpdateMonitor implements IHasChildrenUpdat contentProvider.getViewer().autoExpand(elementPath); } if (elementPath.getSegmentCount() > 0) { - getContentProvider().restorePendingStateOnUpdate(getElementPath(), -1, true, false, false); + getContentProvider().getStateTracker().restorePendingStateOnUpdate(getElementPath(), -1, true, false, false); } } @@ -137,4 +142,20 @@ class HasChildrenUpdate extends ViewerUpdateMonitor implements IHasChildrenUpdat } return path; } + + boolean hasChildren() { + return fHasChildren; + } + + protected boolean doEquals(ViewerUpdateMonitor update) { + return + update instanceof HasChildrenUpdate && + getViewerInput().equals(update.getViewerInput()) && + getElementPath().equals(getElementPath()); + } + + protected int doHashCode() { + return getClass().hashCode() + getViewerInput().hashCode() + getElementPath().hashCode(); + } + } 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/IInternalTreeModelViewer.java index 4e651832e..a0ed967ea 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/IInternalTreeModelViewer.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2009, 2010 Wind River Systems and others. + * Copyright (c) 2011 Wind River Systems and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -11,9 +11,13 @@ *******************************************************************************/ package org.eclipse.debug.internal.ui.viewers.model; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer; +import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.TreePath; import org.eclipse.jface.viewers.ViewerFilter; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.graphics.RGB; /** * This interface must be implemented by the viewer which uses the @@ -21,15 +25,15 @@ import org.eclipse.jface.viewers.ViewerFilter; * provider to update the viewer with information retrieved from the * content, proxy, memento, and other element-based providers. * - * @since 3.5 + * @since 3.8 */ -public interface ITreeModelContentProviderTarget extends ITreeModelViewer { +public interface IInternalTreeModelViewer extends ITreeModelViewer { /** * Returns this viewer's filters. * * @return an array of viewer filters - * @see StructuredViewer#setFilters(ViewerFilter[]) + * @see org.eclipse.jface.viewers.StructuredViewer#setFilters(ViewerFilter[]) */ public ViewerFilter[] getFilters(); @@ -49,22 +53,6 @@ public interface ITreeModelContentProviderTarget extends ITreeModelViewer { public void update(Object element); /** - * Triggers an update of the given element and its children. If - * multiple instances of the given element are found in the tree, - * they will all be updated. - * - * @param element Element to update. - */ - public void refresh(Object element); - - /** - * Triggers a full update of all the elements in the tree. - * - * @param element Element to update. - */ - public void refresh(); - - /** * Sets the given object to be the element at the given index of the given parent. * <p> * This method should only be called by the viewer framework. @@ -73,6 +61,7 @@ public interface ITreeModelContentProviderTarget extends ITreeModelViewer { * @param parentOrTreePath Parent object, or a tree path of the parent element. * @param index Index at which to set the new element. * @param element Element object. + * @noreference This method is not intended to be referenced by clients. */ public void replace(Object parentOrTreePath, final int index, Object element); @@ -85,7 +74,8 @@ public interface ITreeModelContentProviderTarget extends ITreeModelViewer { * </p> * * @param elementOrTreePath The element, or tree path. - * @param count + * @param count new value + * @noreference This method is not intended to be referenced by clients. */ public void setChildCount(final Object elementOrTreePath, final int count); @@ -97,9 +87,9 @@ public interface ITreeModelContentProviderTarget extends ITreeModelViewer { * This method should only be called by the viewer framework. * </p> * - * @param elementOrTreePath - * the element, or tree path - * @param hasChildren + * @param elementOrTreePath the element, or tree path + * @param hasChildren new value. + * @noreference This method is not intended to be referenced by clients. */ public void setHasChildren(final Object elementOrTreePath, final boolean hasChildren); @@ -111,6 +101,7 @@ public interface ITreeModelContentProviderTarget extends ITreeModelViewer { * </p> * * @param elementPath tree path to element to consider for expansion + * @noreference This method is not intended to be referenced by clients. */ public void autoExpand(TreePath elementPath); @@ -126,6 +117,8 @@ public interface ITreeModelContentProviderTarget extends ITreeModelViewer { * @param expanded * <code>true</code> if the node is expanded, and * <code>false</code> if collapsed + * + * @noreference This method is not intended to be referenced by clients. */ public void setExpandedState(Object elementOrTreePath, boolean expanded); @@ -142,20 +135,19 @@ public interface ITreeModelContentProviderTarget extends ITreeModelViewer { * @param level * non-negative level, or <code>ALL_LEVELS</code> to expand all * levels of the tree + * + * @noreference This method is not intended to be referenced by clients. */ public void expandToLevel(Object elementOrTreePath, int level); - - /** * Removes the given element from the viewer. The selection is updated if * necessary. * <p> * This method should only be called by the viewer framework. * </p> - * - * @param elementsOrTreePaths - * the element, or the tree path to the element + * @param elementOrTreePath the element, or the tree path to the element + * @noreference This method is not intended to be referenced by clients. */ public void remove(Object elementOrTreePath); @@ -167,6 +159,7 @@ public interface ITreeModelContentProviderTarget extends ITreeModelViewer { * * @param parentOrTreePath the parent element, the input element, or a tree path to the parent element * @param index child index + * @noreference This method is not intended to be referenced by clients. */ public void remove(Object parentOrTreePath, final int index); @@ -178,20 +171,21 @@ public interface ITreeModelContentProviderTarget extends ITreeModelViewer { * <p> * This method should only be called by the viewer framework. * </p> + * @param parentOrTreePath the parent element, or the tree path to the parent * - * @param parentElementOrTreePath - * the parent element, or the tree path to the parent - * @param element - * the element - * @param position - * a 0-based position relative to the model, or -1 to indicate - * the last position + * @param element the element + * @param position a 0-based position relative to the model, or -1 to indicate + * the last position + * @noreference This method is not intended to be referenced by clients. */ public void insert(Object parentOrTreePath, Object element, int position); /** * Returns whether the candidate selection should override the current * selection. + * @param current Current selection in viewer. + * @param candidate Proposed new selection. + * @return whether new selection should override the current */ public boolean overrideSelection(ISelection current, ISelection candidate); @@ -207,9 +201,11 @@ public interface ITreeModelContentProviderTarget extends ITreeModelViewer { public boolean getExpandedState(Object elementOrTreePath); /** - * Returns whether the given element has children. + * Returns whether the node corresponding to the given element or tree path + * has any child elements. * - * @since 3.6 + * @param elementOrTreePath Path to element + * @return Returns whether the given element has children. */ public boolean getHasChildren(Object elementOrTreePath); @@ -217,24 +213,37 @@ public interface ITreeModelContentProviderTarget extends ITreeModelViewer { * 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. + * + * @param path Path to get count for. + * @return The child count. */ public int getChildCount(TreePath path); /** * Returns the element which is a child of the element at the * given path, with the given index. + * + * @param path Path to parent element. + * @param index Index of child element. + * @return Child element. */ public Object getChildElement(TreePath path, int index); /** * Returns the tree path of the element that is at the top of the * viewer. + * + * @return the tree path of the element at the top of the + * viewer. */ public TreePath getTopElementPath(); /** * Finds the index of the given element with a parent of given path. * + * @param parentPath Path of parent element. + * @param element Element to find. + * * @return The element's index, or -1 if not found. */ public int findElementIndex(TreePath parentPath, Object element); @@ -243,10 +252,8 @@ public interface ITreeModelContentProviderTarget extends ITreeModelViewer { * Returns a boolean indicating whether all the child elements of the * given parent have been realized already. * - * @param parentPath - * @return - * - * @since 3.6 + * @param parentPath Path of parent element. + * @return true if all children realized */ public boolean getElementChildrenRealized(TreePath parentPath); @@ -254,8 +261,59 @@ public interface ITreeModelContentProviderTarget extends ITreeModelViewer { * Clears the selection in the viewer, if any, without firing * selection change notification. This is only to be used by * the platform. - * - * @since 3.6 */ public void clearSelectionQuiet(); + + /** + * Sets the element's display information. + * <p> + * This method should only be called by the viewer framework. + * </p> + * + * @param path Element path. + * @param numColumns Number of columns in the data. + * @param labels Array of labels. The array cannot to be + * <code>null</code>, but values within the array may be. + * @param images Array of image descriptors, may be <code>null</code>. + * @param fontDatas Array of fond data objects, may be <code>null</code>. + * @param foregrounds Array of RGB values for foreground colors, may be + * <code>null</code>. + * @param backgrounds Array of RGB values for background colors, may be + * <code>null</code>. + * @noreference This method is not intended to be referenced by clients. + */ + public void setElementData(TreePath path, int numColumns, String[] labels, ImageDescriptor[] images, FontData[] fontDatas, RGB[] foregrounds, RGB[] backgrounds); + + /** + * Returns identifiers of the visible columns in this viewer, or <code>null</code> + * if there is currently no column presentation. + * + * @return visible columns or <code>null</code> + */ + public String[] getVisibleColumns(); + + /** + * Sets the element check state data. + * + * @param path Path of element to check. + * @param checked if true, item will be checked + * @param grayed if true item will be grayed + */ + public void setElementChecked(TreePath path, boolean checked, boolean grayed); + + /** + * Retrieves the element check state. + * + * @param path Path of element to return check state for. + * @return the element checked state + */ + public boolean getElementChecked(TreePath path); + + /** + * Retrieves the element's check box grayed state. + * + * @param path Path of element to return grayed state for. + * @return the element grayed state + */ + public boolean getElementGrayed(TreePath path); } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/IMementoManager.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/IMementoManager.java deleted file mode 100644 index 665c3698b..000000000 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/IMementoManager.java +++ /dev/null @@ -1,43 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2006, 2010 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * IBM Corporation - initial API and implementation - *******************************************************************************/ -package org.eclipse.debug.internal.ui.viewers.model; - -import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoRequest; - -/** - * @since 3.3 - */ -interface IMementoManager { - - /** - * Adds the request to this manager. - * - * @param memento request - */ - public void addRequest(IElementMementoRequest request); - - /** - * Notification the request is complete. - * - * @param request - */ - public void requestComplete(IElementMementoRequest request); - - /** - * Process the queued requests. Accepts no more new requests. - */ - public void processReqeusts(); - - /** - * Cancels the requests in progress. - */ - public void cancel(); -} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelCheckProviderTarget.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelCheckProviderTarget.java deleted file mode 100644 index 1348f254c..000000000 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelCheckProviderTarget.java +++ /dev/null @@ -1,50 +0,0 @@ -/***************************************************************** - * Copyright (c) 2009 Texas Instruments 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: - * Patrick Chuong (Texas Instruments) - Initial API and implementation (Bug 286310) - *****************************************************************/ -package org.eclipse.debug.internal.ui.viewers.model; - -import org.eclipse.jface.viewers.TreePath; - -/** - * This interface can be implemented by the viewer which uses the - * {@link TreeModelLabelProvider} label provider and supports the - * {@link org.eclipse.swt.SWT.CHECK} style. It allows the label provider to - * update the viewer with check-box information retrieved from the - * element-based label providers. - * - * @since 3.6 - */ -public interface ITreeModelCheckProviderTarget extends ITreeModelLabelProviderTarget { - - /** - * Sets the element check state data. - * - * @param path - * @param checked - * @param grayed - */ - public void setElementChecked(TreePath path, boolean checked, boolean grayed); - - /** - * Retrieves the element check state. - * - * @param path - * @return checked - */ - public boolean getElementChecked(TreePath path); - - /** - * Retrieves the element's check box grayed state. - * - * @param path - * @return grayed - */ - public boolean getElementGrayed(TreePath path); -} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelContentProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelContentProvider.java index bad08fb73..d92934949 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelContentProvider.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelContentProvider.java @@ -15,6 +15,8 @@ import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; import org.eclipse.debug.internal.ui.viewers.model.provisional.IStateUpdateListener; import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdateListener; import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer; +import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewerFilter; +import org.eclipse.jface.viewers.IContentProvider; import org.eclipse.jface.viewers.ILazyTreePathContentProvider; import org.eclipse.jface.viewers.TreePath; import org.eclipse.jface.viewers.Viewer; @@ -85,6 +87,20 @@ public interface ITreeModelContentProvider extends ILazyTreePathContentProvider public int modelToViewIndex(TreePath parentPath, int index); /** + * Returns whether the children of given element should be filtered. + * <p>This method is used to determine whether any of the registered filters + * that extend {@link TreeModelViewerFilter} are applicable to the given + * element. If so, then children of given element should be filtered + * prior to populating them in the viewer. + * + * @param parentElement + * the parent element + * @return whether there are any {@link TreeModelViewerFilter} filters + * applicable to given parent + */ + public boolean areTreeModelViewerFiltersApplicable(Object parentElement); + + /** * Returns whether the given element is filtered. * * @param parentElementOrTreePath @@ -98,7 +114,7 @@ public interface ITreeModelContentProvider extends ILazyTreePathContentProvider /** * Notification the given element is being unmapped. * - * @param path + * @param path Path to unmap */ public void unmapPath(TreePath path); @@ -107,7 +123,7 @@ public interface ITreeModelContentProvider extends ILazyTreePathContentProvider * coming from the model. Any delta flags which are hidden by the mask * will be ignored. * - * @param the bit mask for <code>IModelDelta</code> flags + * @param mask for <code>IModelDelta</code> flags * * @since 3.6 */ @@ -135,11 +151,13 @@ public interface ITreeModelContentProvider extends ILazyTreePathContentProvider /** * Registers the specified listener for view update notifications. + * @param listener Listener to add */ public void addViewerUpdateListener(IViewerUpdateListener listener); /** * Removes the specified listener from update notifications. + * @param listener Listener to remove */ public void removeViewerUpdateListener(IViewerUpdateListener listener); @@ -147,23 +165,34 @@ public interface ITreeModelContentProvider extends ILazyTreePathContentProvider * Registers the given listener for model delta notification. * This listener is called immediately after the viewer processes * the delta. + * @param listener Listener to add */ public void addModelChangedListener(IModelChangedListener listener); /** * Removes the given listener from model delta notification. + * @param listener Listener to remove */ public void removeModelChangedListener(IModelChangedListener listener); + /** + * Causes the content provider to save the expansion and selection state + * of given element. The state is then restored as the tree is lazily + * re-populated. + * @param path Path of the element to save. + */ + public void preserveState(TreePath path); /** * Registers the specified listener for state update notifications. + * @param listener Listener to add * @since 3.6 */ public void addStateUpdateListener(IStateUpdateListener listener); /** * Removes the specified listener from state update notifications. + * @param listener Listener to remove * @since 3.6 */ public void removeStateUpdateListener(IStateUpdateListener listener); @@ -196,13 +225,24 @@ public interface ITreeModelContentProvider extends ILazyTreePathContentProvider /** * Notifies the content provider that a client called {@link Viewer#setInput(Object)}, - * and the viewer input is about to change. + * and the viewer input is changed. + * This method is guaranteed to be called after {@link IContentProvider#inputChanged(Viewer, Object, Object)} * * @param viewer The viewer that uses this content provider. * @param oldInput Old input object. * @param newInput New input object. * - * @since 3.7 + * @since 3.8 + */ + public void postInputChanged(IInternalTreeModelViewer viewer, Object oldInput, Object newInput); + + /** + * Notifies the receiver that the given element has had its + * checked state modified in the viewer. + * + * @param path Path of the element that had its checked state changed + * @param checked The new checked state of the element + * @return false if the check state should not change */ - public void inputAboutToChange(ITreeModelContentProviderTarget viewer, Object oldInput, Object newInput); + public boolean setChecked(TreePath path, boolean checked); } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelLabelProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelLabelProvider.java index b2c2d5cd9..cf7c9419f 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelLabelProvider.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelLabelProvider.java @@ -20,7 +20,7 @@ import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.RGB; /** - * {@link TreeModelViewer} label provider interface. In addition to + * {@link InternalTreeModelViewer} label provider interface. In addition to * implementing this interface, the label provider for the TreeModelViewer * must also extend CellLabelProvider. * @@ -28,15 +28,23 @@ import org.eclipse.swt.graphics.RGB; */ public interface ITreeModelLabelProvider extends IBaseLabelProvider { + /** + * Requests an label update label of the given element. + * @param elementPath Element to update. + * @return true if element label provider is found and update will + * be requested. + */ public boolean update(TreePath elementPath); /** * Registers the specified listener for view label update notifications. + * @param listener Listener to add */ public void addLabelUpdateListener(ILabelUpdateListener listener); /** * Removes the specified listener from view label update notifications. + * @param listener Listener to remove */ public void removeLabelUpdateListener(ILabelUpdateListener listener); diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelLabelProviderTarget.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelLabelProviderTarget.java deleted file mode 100644 index add8a89be..000000000 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelLabelProviderTarget.java +++ /dev/null @@ -1,54 +0,0 @@ -/******************************************************************************* - * 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.internal.ui.viewers.model; - -import org.eclipse.jface.resource.ImageDescriptor; -import org.eclipse.jface.viewers.TreePath; -import org.eclipse.swt.graphics.FontData; -import org.eclipse.swt.graphics.RGB; - -/** - * This interface must be implemented by the viewer which uses the - * {@link TreeModelLabelProvider} label provider. It allows the label - * provider to update the viewer with information retrieved from the - * element-based label providers. - * - * @since 3.5 - */ -public interface ITreeModelLabelProviderTarget extends ITreeModelViewer { - - /** - * Sets the element's display information. - * <p> - * This method should only be called by the viewer framework. - * </p> - * - * @param path Element path. - * @param numColumns Number of columns in the data. - * @param labels Array of labels. The array cannot to be - * <code>null</code>, but values within the array may be. - * @param images Array of image descriptors, may be <code>null</code>. - * @param fontDatas Array of fond data objects, may be <code>null</code>. - * @param foregrounds Array of RGB values for foreground colors, may be - * <code>null</code>. - * @param backgrounds Array of RGB values for background colors, may be - * <code>null</code>. - */ - public void setElementData(TreePath path, int numColumns, String[] labels, ImageDescriptor[] images, FontData[] fontDatas, RGB[] foregrounds, RGB[] backgrounds); - - /** - * Returns identifiers of the visible columns in this viewer, or <code>null</code> - * if there is currently no column presentation. - * - * @return visible columns or <code>null</code> - */ - public String[] getVisibleColumns(); -} 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 95b345b30..e85d818fa 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2009, 2010 Wind River Systems and others. + * Copyright (c) 2011 Wind River Systems and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -7,213 +7,16 @@ * * Contributors: * Wind River Systems - initial API and implementation - * IBM Corporation - ongoing bug fixes and enhancements *******************************************************************************/ package org.eclipse.debug.internal.ui.viewers.model; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelChangedListener; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IStateUpdateListener; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdateListener; -import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; -import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer; -import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualTreeModelViewer; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.ISelectionProvider; -import org.eclipse.jface.viewers.TreePath; -import org.eclipse.jface.viewers.ViewerLabel; -import org.eclipse.swt.widgets.Display; - /** - * Interface of an tree model viewer. It declares the common methods for the - * JFace-based {@link TreeModelViewer} and the UI-less - * {@link VirtualTreeModelViewer}. - * - * @since 3.5 + * This interface was moved to the {@link org.eclipse.debug.internal.ui.viewers.model.provisional} + * package. This stub was left for backward compatibility for packages that + * referenced this internal interface. + * + * @deprecated This internal interface was replaced by {@link org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer}. */ -public interface ITreeModelViewer extends ISelectionProvider { - - /** - * Constant indicating that all levels of the tree should be expanded or - * collapsed. - * - * @see #setAutoExpandLevel(int) - * @see #getAutoExpandLevel() - */ - public static final int ALL_LEVELS = -1; - - /** - * Returns the Display object that this viewer is in. The - * display object can be used by clients to access the display thread - * to call the viewer methods. - */ - public Display getDisplay(); - - /** - * Returns this viewer's presentation context. - * - * @return presentation context - */ - public IPresentationContext getPresentationContext(); - - /** - * Returns the current input of this viewer, or <code>null</code> - * if none. The viewer's input provides the "model" for the viewer's - * content. - */ - public Object getInput(); - - /** - * Sets the input of this viewer. Setting the input resets the - * viewer's contents and triggers an update starting at the input - * element. - * @param object Input element, or <code>null</code> if none. - */ - public void setInput(Object object); - - /** - * Returns the current selection in viewer. - */ - public ISelection getSelection(); - - /** - * Sets a new selection for this viewer and optionally makes it visible. - * The selection is not set if the model selection policy overrides the - * attempt to set the selection. - * - * @param selection the new selection - * @param reveal <code>true</code> if the selection is to be made - * visible, and <code>false</code> otherwise - * @param force <code>true</code> if the selection should override the - * model selection policy - */ - public void setSelection(ISelection selection, boolean reveal, boolean force); - - /** - * Attempts to set the selection for this viewer and optionally makes it visible. - * The selection is not set if the model selection policy overrides the - * attempt to set the selection. - * - * @param selection the new selection - * @param reveal whether to make the selection visible after successfully setting - * the selection - * @param force whether to force the selection (override the model selection policy) - * @return <code>true</code> if the selection was set and <code>false</code> if the - * model selection policy overrides the selection attempt - */ - public boolean trySelection(ISelection selection, boolean reveal, boolean force); - - /** - * Returns the auto-expand level. - * - * @return non-negative level, or <code>ALL_LEVELS</code> if all levels of - * the tree are expanded automatically - * @see #setAutoExpandLevel - */ - public int getAutoExpandLevel(); +public interface ITreeModelViewer extends org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer { - /** - * Sets the auto-expand level to be used when the input of the viewer is set - * using {@link #setInput(Object)}. The value 0 means that there is no - * auto-expand; 1 means that the invisible root element is expanded (since - * most concrete implementations do not show the root element, there is usually - * no practical difference between using the values 0 and 1); 2 means that - * top-level elements are expanded, but not their children; 3 means that - * top-level elements are expanded, and their children, but not - * grandchildren; and so on. - * <p> - * The value <code>ALL_LEVELS</code> means that all subtrees should be - * expanded. - * </p> - * - * @param level - * non-negative level, or <code>ALL_LEVELS</code> to expand all - * levels of the tree - */ - public void setAutoExpandLevel(int level); - - /** - * Returns the label data for the given element and for the given column, - * Returns <code>null</code> if the given element is not found or is not - * materialized in the virtual viewer. Clients may listen to label update - * events to be notified when element labels are updated. - * - * @param path Path of the element. - * @param columnIdx ID of the column for which to return the label data. - * @return Label object containing the label information. Can be - * <code>null</code> if the given element is not found or is not - * materialized in the virtual viewer. - */ - public ViewerLabel getElementLabel(TreePath path, String columnId); - - /** - * Registers the specified listener for view update notifications. - */ - public void addViewerUpdateListener(IViewerUpdateListener listener); - - /** - * Removes the specified listener from update notifications. - */ - public void removeViewerUpdateListener(IViewerUpdateListener listener); - - /** - * Registers the specified listener for state update notifications. - * @since 3.6 - */ - public void addStateUpdateListener(IStateUpdateListener listener); - - /** - * Removes the specified listener from state update notifications. - * @since 3.6 - */ - public void removeStateUpdateListener(IStateUpdateListener listener); - - /** - * Registers the specified listener for view label update notifications. - */ - public void addLabelUpdateListener(ILabelUpdateListener listener); - - /** - * Removes the specified listener from view label update notifications. - */ - public void removeLabelUpdateListener(ILabelUpdateListener listener); - - /** - * Registers the given listener for model delta notification. - * This listener is called immediately after the viewer processes - * the delta. - */ - public void addModelChangedListener(IModelChangedListener listener); - - /** - * Removes the given listener from model delta notification. - */ - public void removeModelChangedListener(IModelChangedListener listener); - - /** - * Writes state information into a delta for the sub-tree at the given - * path. It adds delta nodes and IModelDelta.EXPAND and IModelDelta.SELECT - * 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>. - * @return Returns whether the state was saved for the given path. Will - * return <code>false</code> if an element at the given path cannot - * be found. - * - * @since 3.6 - */ - public boolean saveElementState(TreePath path, ModelDelta delta, int flagsToSave); - - /** - * Causes the viewer to process the given delta as if it came from a - * model proxy. This method is intended to be used to restore state - * saved using {@link #saveElementState(TreePath, ModelDelta)}. - * - * @param delta Delta to process. - */ - public void updateViewer(IModelDelta delta); } 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 f07334d9f..9ddd2bada 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 @@ -12,36 +12,26 @@ *******************************************************************************/ package org.eclipse.debug.internal.ui.viewers.model; -import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; -import java.util.List; import java.util.Map; -import java.util.Set; import java.util.Map.Entry; +import java.util.Set; -import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.debug.internal.core.IInternalDebugCoreConstants; -import org.eclipse.debug.internal.core.commands.Request; import org.eclipse.debug.internal.ui.viewers.model.provisional.ICheckUpdate; -import org.eclipse.debug.internal.ui.viewers.model.provisional.ICheckboxModelProxy; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation; import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation2; import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentationFactory; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider; import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementEditor; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider; -import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelChangedListener; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicy; import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; import org.eclipse.debug.internal.ui.viewers.model.provisional.IStateUpdateListener; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer; import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdateListener; import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext; @@ -87,7 +77,7 @@ import org.eclipse.ui.IMemento; * @since 3.3 */ public class InternalTreeModelViewer extends TreeViewer - implements ITreeModelViewer, ITreeModelContentProviderTarget, ITreeModelLabelProviderTarget, ITreeModelCheckProviderTarget + implements IInternalTreeModelViewer, org.eclipse.debug.internal.ui.viewers.model.ITreeModelViewer { private IPresentationContext fContext; @@ -271,734 +261,6 @@ public class InternalTreeModelViewer extends TreeViewer private CellModifierProxy fCellModifier; - /** - * A handle to an element in a model. - */ - class VirtualElement { - /** - * Tree item associated with the element, or <code>null</code> for the root element - */ - private TreeItem fItem; - - /** - * Model element (can be <code>null</code> until retrieved) - */ - private Object fElement; - - /** - * Associated label update or <code>null</code> if the element was already - * present in the tree. - */ - private VirtualLabelUpdate fLabel; - - /** - * Children elements or <code>null</code> if none - */ - private VirtualElement[] fChildren = null; - - /** - * Whether this element would be filtered from the viewer - */ - private boolean fFiltered = false; - - /** - * Listens for update when populating this virtual element in the tree viewer - */ - class UpdateListener implements IViewerUpdateListener { - - /** - * The parent pending update - */ - private TreePath fParentPath; - - /** - * The child index pending update - */ - private int fIndex; - - /** - * Whether the update has completed - */ - private boolean fDone = false; - - /** - * Constructs a new listener waiting - * @param parentPath the parent path - * @param childIndex the current child index - */ - UpdateListener(TreePath parentPath, int childIndex) { - fParentPath = parentPath; - fIndex = childIndex; - } - - /* (non-Javadoc) - * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdateListener#updateComplete(org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate) - */ - public void updateComplete(IViewerUpdate update) { - if (update instanceof IChildrenUpdate) { - IChildrenUpdate cu = (IChildrenUpdate) update; - if (cu.getElementPath().equals(fParentPath)) { - if (fIndex >= cu.getOffset() && fIndex <= (cu.getOffset() + cu.getLength())) { - fDone = true; - } - } - } - } - - /** - * Returns whether the update has completed. - * - * @return whether the update has completed - */ - boolean isComplete() { - return fDone; - } - - public void updateStarted(IViewerUpdate update) { - } - public void viewerUpdatesBegin() { - } - public void viewerUpdatesComplete() { - } - } - - /** - * Constructs a new virtual element for the given tree item and all - * of its expanded children. Has to be called in the UI thread. - * The model is used to count unrealized elements. - * - * Note only never re-use objects already in the tree as they may be out - * of synch. - * - * @param item tree item - * @param model virtual model - * @param root subtree to consider or <code>null</code> if all - * @param indexes children to consider or <code>null</code> - */ - VirtualElement(TreeItem item, VirtualModel model, TreePath root, int[] indexes) { - fItem = item; - model.incVirtual(); - if (item.getExpanded()) { - TreeItem[] items = item.getItems(); - fChildren = createChildren(items, model, root, indexes); - } - } - - /** - * Constructs a new virtual element for the given tree and all expanded - * children. The model is passed in to count unrealized elements. - * - * @param tree tree - * @param model virtual model - * @param root subtree scope or <code>null</code> for all - * @param indexes child indexes to consider or <code>null</code> - */ - VirtualElement(Tree tree, VirtualModel model, TreePath root, int[] indexes) { - fElement = tree.getData(); - TreeItem[] items = tree.getItems(); - if (items.length > 0) { - fChildren = createChildren(items, model, root, indexes); - } - } - - /** - * Creates and returns children elements. - * - * @param items tree items - * @param model model - * @param root subtree to consider or all if <code>null</code> - * @param indexes children of the root to consider or <code>null</code> - * @return children - */ - private VirtualElement[] createChildren(TreeItem[] items, VirtualModel model, TreePath root, int[] indexes) { - VirtualElement[] kids = new VirtualElement[items.length]; - if (root == null || root.getSegmentCount() == 0) { - if (indexes == null) { - for (int i = 0; i < items.length; i++) { - kids[i] = new VirtualElement(items[i], model, null, null); - } - } else { - for (int i = 0; i < indexes.length; i++) { - int index = indexes[i]; - kids[index] = new VirtualElement(items[index], model, null, null); - } - } - } else { - for (int i = 0; i < items.length; i++) { - TreeItem treeItem = items[i]; - if (treeItem.getData() != null) { - TreePath path = getTreePathFromItem(treeItem); - if (root.startsWith(path, null)) { - if (root.getSegmentCount() > path.getSegmentCount()) { - kids[i] = new VirtualElement(treeItem, model, root, indexes); - } else { - kids[i] = new VirtualElement(treeItem, model, null, indexes); - } - break; - } - } - } - } - return kids; - } - - /** - * Returns whether this element would be filtered from the viewer. - * - * @return whether filtered - */ - public boolean isFiltered() { - return fFiltered; - } - - /** - * Causes the tree item associated with this model element to be realized. - * Must be called in the UI thread. - * - * @return tree path to associated element, or <code>null</code> if the operation - * fails - */ - public TreePath realize() { - if (fItem.getData() != null) { - return getTreePathFromItem(fItem); - } - int index = -1; - TreePath parentPath = null; - if (fItem.getParentItem() == null) { - index = getTree().indexOf(fItem); - parentPath = TreePath.EMPTY; - } else { - index = fItem.getParentItem().indexOf(fItem); - parentPath = getTreePathFromItem(fItem.getParentItem()); - } - UpdateListener listener = new UpdateListener(parentPath, index); - addViewerUpdateListener(listener); - ((ILazyTreePathContentProvider)getContentProvider()).updateElement(parentPath, index); - Display display = getTree().getDisplay(); - while (!listener.isComplete()) { - if (!display.readAndDispatch()) { - display.sleep(); - } - } - removeViewerUpdateListener(listener); - if (fItem.getData() != null) { - return getTreePathFromItem(fItem); - } - return null; - } - - /** - * Schedules updates to retrieve unrealized children of this node. - * - * @param parentPath path to this element - * @param model model - */ - void retrieveChildren(TreePath parentPath, VirtualModel model) { - VirtualChildrenUpdate update = null; - if (fChildren != null) { - int prevModelIndex = Integer.MAX_VALUE; - for (int i = 0; i < fChildren.length; i++) { - VirtualElement element = fChildren[i]; - if (element == null) { - if (update != null) { - // non-consecutive updates, schedule and keep going - model.scheduleUpdate(update); - update = null; - } - } else { - int modelIndex = ((ITreeModelContentProvider)getContentProvider()).viewToModelIndex(parentPath, i); - if (update == null) { - update = new VirtualChildrenUpdate(parentPath, this, model); - } else if ((modelIndex - prevModelIndex) > 1) { - // non-consecutive updates, schedule and keep going - model.scheduleUpdate(update); - update = new VirtualChildrenUpdate(parentPath, this, model); - } - update.add(modelIndex); - prevModelIndex = modelIndex; - } - } - } - if (update != null) { - model.scheduleUpdate(update); - } - } - - /** - * Sets the underlying model object. - * @param data the model object - */ - void setElement(Object data) { - fElement = data; - } - - /** - * Sets the label update associated with this element - * - * @param update the new label update - */ - void setLabelUpdate(VirtualLabelUpdate update) { - fLabel = update; - } - - /** - * Returns the image for this element or <code>null</code> if none - * - * @return image or <code>null</code> if none - */ - public Image getImage() { - if (fLabel == null) { - return fItem.getImage(); - } else { - return ((ITreeModelLabelProvider)getLabelProvider()).getImage(fLabel.fImage); - } - } - - /** - * Returns the labels for the element - one for each column requested. - * - * @return column labels - */ - public String[] getLabel() { - if (fLabel == null) { - String[] visibleColumns = getVisibleColumns(); - String[] label = new String[visibleColumns == null ? 1 : visibleColumns.length]; - for (int i = 0; i < label.length; i++) { - label[i] = fItem.getText(i); - } - return label; - } - return fLabel.fText; - - } - - /** - * Returns the children of this element or <code>null</code> if none. - * - * @return children or <code>null</code> if none - */ - public VirtualElement[] getChildren() { - return fChildren; - } - - } - - /** - * Common function for virtual updates. - */ - class VirtualUpdate extends Request implements IViewerUpdate { - - /** - * Path to the element being updated, or EMPTY for viewer input. - */ - private TreePath fPath; - - /** - * Element being updated. - */ - VirtualElement fVirtualElement; - - /** - * Associated virtual model - */ - VirtualModel fModel = null; - - /** - * Creates a new update based for the given element and model - * @param model the backing model - * @param element the element to update - * @param elementPath the path of the element - */ - public VirtualUpdate(VirtualModel model, VirtualElement element, TreePath elementPath) { - fPath = elementPath; - fModel = model; - fVirtualElement = element; - } - - /** - * Returns the associated virtual element. - * - * @return virtual element - */ - protected VirtualElement getVirtualElement() { - return fVirtualElement; - } - - /* (non-Javadoc) - * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate#getElement() - */ - public Object getElement() { - if (fPath.getSegmentCount() == 0) { - return getViewerInput(); - } - return fPath.getLastSegment(); - } - - /* (non-Javadoc) - * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate#getElementPath() - */ - public TreePath getElementPath() { - return fPath; - } - - /* (non-Javadoc) - * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate#getPresentationContext() - */ - public IPresentationContext getPresentationContext() { - return InternalTreeModelViewer.this.getPresentationContext(); - } - - /* (non-Javadoc) - * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate#getViewerInput() - */ - public Object getViewerInput() { - return InternalTreeModelViewer.this.getInput(); - } - - } - - /** - * Represents all expanded items in this viewer's tree. The model is virtual - i.e. not - * all items have their associated model elements/labels retrieved from the model. The - * model can be populated in a non-UI thread. - */ - class VirtualModel { - - /** - * Update requests in progress (children updates and label updates) - */ - private List fPendingUpdates = new ArrayList(); - - /** - * Whether population has been canceled. - */ - private boolean fCanceled = false; - - /** - * Root element in the model. - */ - private VirtualElement fRoot = null; - - /** - * Progress monitor to use during retrieval of virtual elements - */ - private IProgressMonitor fMonitor = null; - - /** - * Column IDs to generate labels for or <code>null</code> if no columns. - */ - private String[] fColumnIds; - - /** - * Count of the number of virtual (unrealized) elements in this model - * when it was created. - */ - private int fVirtualCount = 0; - - /** - * Creates a virtual model for this tree viewer limited to the given - * subtrees. Includes the entire model if root is <code>null</code>. - * - * @param root limits model to given subtree scope, or <code>null</code> - * @param childIndexes children of the root to consider or <code>null</code> - */ - VirtualModel(TreePath root, int[] childIndexes) { - fRoot = new VirtualElement(getTree(), this, root, childIndexes); - } - - /** - * Increments the counter of virtual elements in the model. - */ - void incVirtual() { - fVirtualCount++; - } - - /** - * update progress monitor - * - * @param work the number of units worked - */ - void worked(int work) { - fMonitor.worked(work); - } - - /** - * Schedules a children update. - * @param update the update to schedule - */ - private synchronized void scheduleUpdate(IChildrenUpdate update) { - Object element = update.getElement(); - if (element == null) { - element = getInput(); - } - IElementContentProvider provider = ViewerAdapterService.getContentProvider(element); - if (provider != null) { - fPendingUpdates.add(update); - provider.update(new IChildrenUpdate[]{update}); - } - } - - /** - * Populates this models elements that have not yet been retrieved from the model, - * returning all elements in the model, or <code>null</code> if canceled. - * - * @param monitor progress monitor for progress reporting and for checking if canceled - * @param taskName used in progress monitor, for example "Find..." - * @param columnIds labels to generate or <code>null</code> if no columns are to be used - * @return model elements or <code>null</code> if canceled - */ - public VirtualElement populate(IProgressMonitor monitor, String taskName, String[] columnIds) { - fMonitor = monitor; - fColumnIds = columnIds; - monitor.beginTask(taskName, fVirtualCount * 2); - boolean done = false; - fRoot.retrieveChildren(TreePath.EMPTY, this); - synchronized (this) { - done = fPendingUpdates.isEmpty(); - } - while (!done) { - synchronized (this) { - try { - wait(500); - } catch (InterruptedException e) { - } - } - if (monitor.isCanceled()) { - cancel(); - return null; - } - synchronized (this) { - done = fPendingUpdates.isEmpty(); - } - } - monitor.done(); - return fRoot; - } - - /** - * Cancels all pending updates. - */ - void cancel() { - synchronized (this) { - fCanceled = true; - Iterator iterator = fPendingUpdates.iterator(); - while (iterator.hasNext()) { - IViewerUpdate update = (IViewerUpdate) iterator.next(); - update.cancel(); - } - fPendingUpdates.clear(); - } - } - private synchronized boolean isCanceled() { - return fCanceled; - } - - /** - * Notification the given children update is complete. Schedules associated label - * updates if the request or the population of the model has not been canceled. - * - * @param update completed children update request - */ - synchronized void done(VirtualChildrenUpdate update) { - if (!isCanceled()) { - fPendingUpdates.remove(update); - if (!update.isCanceled()) { - VirtualElement[] children = update.fVirtualElement.fChildren; - TreePath parent = update.getElementPath(); - IElementLabelProvider provider = null; - List requests = new ArrayList(); - int start = update.getOffset(); - int end = start + update.getLength(); - for (int i = start; i < end; i++) { - int viewIndex = ((ITreeModelContentProvider)getContentProvider()).modelToViewIndex(parent, i); - VirtualElement proxy = children[viewIndex]; - if (proxy.fFiltered) { - fMonitor.worked(1); // don't need the label, this one is already done - } else { - Object element = proxy.fElement; - if (element != null) { // null indicates other updates coming later - VirtualLabelUpdate labelUpdate = new VirtualLabelUpdate(update.fModel, proxy, parent.createChildPath(element)); - proxy.setLabelUpdate(labelUpdate); - IElementLabelProvider next = ViewerAdapterService.getLabelProvider(element); - if (next != null) { - fPendingUpdates.add(labelUpdate); - } - if (provider == null) { - provider = next; - requests.add(labelUpdate); - } else if (next != null) { - if (provider.equals(next)) { - requests.add(labelUpdate); - } else { - // schedule queued requests, label provider has changed - provider.update((ILabelUpdate[])requests.toArray(new ILabelUpdate[requests.size()])); - requests.clear(); - requests.add(labelUpdate); - provider = next; - } - } - } - } - } - if (provider != null && !requests.isEmpty()) { - provider.update((ILabelUpdate[])requests.toArray(new ILabelUpdate[requests.size()])); - } - } - notifyAll(); - } - } - - /** - * Notification the given label update is complete. Updates progress reporting - * and updates pending updates. - * - * @param update label update that was completed - */ - synchronized void done(VirtualLabelUpdate update) { - if (!isCanceled()) { - fPendingUpdates.remove(update); - fMonitor.worked(1); - } - if (fPendingUpdates.isEmpty()) { - notifyAll(); - } - } - } - - /** - * Request to update a range of children. - */ - class VirtualChildrenUpdate extends VirtualUpdate implements IChildrenUpdate { - - private int fOffset = -1; - private int fLength = 0; - - /** - * @param parentPath the parent path - * @param parent the parent element - * @param model the model - */ - public VirtualChildrenUpdate(TreePath parentPath, VirtualElement parent, VirtualModel model) { - super(model, parent, parentPath); - } - - /* (non-Javadoc) - * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate#getLength() - */ - public int getLength() { - return fLength; - } - - /* (non-Javadoc) - * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate#getOffset() - */ - public int getOffset() { - return fOffset; - } - - /* (non-Javadoc) - * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate#setChild(java.lang.Object, int) - */ - public void setChild(Object child, int offset) { - int viewIndex = ((ITreeModelContentProvider)getContentProvider()).modelToViewIndex(getElementPath(), offset); - VirtualElement virtualChild = getVirtualElement().fChildren[viewIndex]; - virtualChild.setElement(child); - ITreeModelContentProvider provider = (ITreeModelContentProvider) getContentProvider(); - virtualChild.fFiltered = provider.shouldFilter(getElementPath(), child); - if (!virtualChild.fFiltered) { - virtualChild.retrieveChildren(getElementPath().createChildPath(child), fModel); - } - fModel.worked(1); - } - - /* (non-Javadoc) - * @see org.eclipse.debug.internal.core.commands.Request#done() - */ - public void done() { - fModel.done(this); - } - - /** - * @param i the new offset - */ - void add(int i) { - if (fOffset == -1) { - fOffset = i; - } - fLength++; - } - - } - - class VirtualLabelUpdate extends VirtualUpdate implements ILabelUpdate { - - /** - * Constructs a label request for the given element; - * @param coordinator the model - * @param element the element to update - * @param elementPath the element path - */ - public VirtualLabelUpdate(VirtualModel coordinator, VirtualElement element, TreePath elementPath) { - super(coordinator, element, elementPath); - } - - private String[] fText; - private ImageDescriptor fImage; - - /* (non-Javadoc) - * @see org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate#getColumnIds() - */ - public String[] getColumnIds() { - return fModel.fColumnIds; - } - - /* (non-Javadoc) - * @see org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate#setBackground(org.eclipse.swt.graphics.RGB, int) - */ - public void setBackground(RGB background, int columnIndex) { - } - - /* (non-Javadoc) - * @see org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate#setFontData(org.eclipse.swt.graphics.FontData, int) - */ - public void setFontData(FontData fontData, int columnIndex) { - } - - /* (non-Javadoc) - * @see org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate#setForeground(org.eclipse.swt.graphics.RGB, int) - */ - public void setForeground(RGB foreground, int columnIndex) { - } - - /* (non-Javadoc) - * @see org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate#setImageDescriptor(org.eclipse.jface.resource.ImageDescriptor, int) - */ - public void setImageDescriptor(ImageDescriptor image, int columnIndex) { - fImage = image; - } - - /* (non-Javadoc) - * @see org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate#setLabel(java.lang.String, int) - */ - public void setLabel(String text, int columnIndex) { - if (fText == null) { - if (getColumnIds() == null) { - fText = new String[1]; - } else { - fText = new String[getColumnIds().length]; - } - } - fText[columnIndex] = text; - } - - /* (non-Javadoc) - * @see org.eclipse.debug.internal.core.commands.Request#done() - */ - public void done() { - fModel.done(this); - } - - } /** * @param parent the parent composite @@ -1192,7 +454,7 @@ public class InternalTreeModelViewer extends TreeViewer if (widget instanceof Item) { widget.setData(TREE_PATH_KEY, getTreePathFromItem((Item)widget)); } else { - widget.setData(TREE_PATH_KEY, ModelContentProvider.EMPTY_TREE_PATH); + widget.setData(TREE_PATH_KEY, TreeModelContentProvider.EMPTY_TREE_PATH); } } @@ -1237,12 +499,13 @@ public class InternalTreeModelViewer extends TreeViewer * @see org.eclipse.jface.viewers.AbstractTreeViewer#inputChanged(java.lang.Object, java.lang.Object) */ protected void inputChanged(Object input, Object oldInput) { - ((ITreeModelContentProvider)getContentProvider()).inputAboutToChange(this, oldInput, input); - // Clear items map now that we've called inputAboutToChange. + // Clear items map now that ITreeModelContentProvider.inputChanged() was already called. // Bug 326917 super.unmapAllElements(); + ((ITreeModelContentProvider)getContentProvider()).postInputChanged(this, oldInput, input); super.inputChanged(input, oldInput); - resetColumns(input); + + resetColumns(input); } /** @@ -1362,6 +625,27 @@ public class InternalTreeModelViewer extends TreeViewer refreshColumns(); } } + + protected void internalRefresh(Object element, boolean updateLabels) { + ITreeModelContentProvider contentProvider = (ITreeModelContentProvider)getContentProvider(); + + if (element == null) { + internalRefresh(getControl(), getRoot(), true, updateLabels); + contentProvider.preserveState(TreePath.EMPTY); + } else { + Widget[] items = findItems(element); + if (items.length != 0) { + for (int i = 0; i < items.length; i++) { + if (items[i] instanceof TreeItem) { + contentProvider.preserveState(getTreePathFromItem((TreeItem)items[i])); + } else { + contentProvider.preserveState(TreePath.EMPTY); + } + } + } + } + super.internalRefresh(element, updateLabels); + } /** * Refreshes the columns in the view, based on the viewer input. @@ -1845,22 +1129,6 @@ public class InternalTreeModelViewer extends TreeViewer } } - /** - * Collects all expanded items from this tree viewer and returns them as part of - * a virtual model. This must be called in a UI thread to traverse the tree items. - * The model can the be populated in a non-UI thread. - * - * Alternatively a root element can be specified with a set of child indexes to - * consider. All children of the specified children are added to the model. - * - * @param root subtree to consider or <code>null</code> if all - * @param childIndexes indexes of root element to consider, or <code>null</code> if all - * @return virtual model - */ - VirtualModel buildVirtualModel(TreePath root, int[] childIndexes) { - return new VirtualModel(root, childIndexes); - } - public void addLabelUpdateListener(ILabelUpdateListener listener) { ((ITreeModelLabelProvider)getLabelProvider()).addLabelUpdateListener(listener); } @@ -2479,6 +1747,19 @@ public class InternalTreeModelViewer extends TreeViewer return false; } + public TreePath[] getElementPaths(Object element) { + Widget[] items = internalFindItems(element); + TreePath[] paths = new TreePath[items.length]; + for (int i = 0; i < items.length; i++) { + if (items[i] instanceof Tree) { + paths[i] = TreePath.EMPTY; + } else { + paths[i] = getTreePathFromItem((Item)items[i]); + } + } + return paths; + } + /* * (non-Javadoc) * @see org.eclipse.jface.viewers.StructuredViewer#handleSelect(org.eclipse.swt.events.SelectionEvent) @@ -2495,11 +1776,7 @@ public class InternalTreeModelViewer extends TreeViewer if (event.detail == SWT.CHECK) { boolean checked = item.getChecked(); - boolean accepted = false; - IModelProxy elementProxy = ((TreeModelContentProvider) contentProvider).getElementProxy(path); - if (elementProxy instanceof ICheckboxModelProxy) { - accepted = ((ICheckboxModelProxy) elementProxy).setChecked(getPresentationContext(), getInput(), path, checked); - } + boolean accepted = ((ITreeModelContentProvider) contentProvider).setChecked(path, checked); // if the listen rejects the change or there is not ICheckboxModelProxy, than revert the check state if (!accepted) { 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 301bc3840..0b7083b74 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 @@ -17,13 +17,13 @@ import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.Map.Entry; +import java.util.Set; -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; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelChangedListener; @@ -31,9 +31,15 @@ import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicy; import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; import org.eclipse.debug.internal.ui.viewers.model.provisional.IStateUpdateListener; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer; import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdateListener; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IVirtualItemListener; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IVirtualItemValidator; 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.VirtualItem; +import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualItem.Index; +import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualTree; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.IContentProvider; import org.eclipse.jface.viewers.ISelection; @@ -59,10 +65,9 @@ import org.eclipse.ui.IMemento; * @since 3.5 */ public class InternalVirtualTreeModelViewer extends Viewer - implements VirtualTree.IVirtualItemListener, + implements IVirtualItemListener, ITreeModelViewer, - ITreeModelContentProviderTarget, - ITreeModelLabelProviderTarget + IInternalTreeModelViewer { /** @@ -92,6 +97,11 @@ public class InternalVirtualTreeModelViewer extends Viewer private static final String TREE_PATH_KEY = "TREE_PATH_KEY"; //$NON-NLS-1$ /** + * Viewer filters currently configured for viewer. + */ + private ViewerFilter[] fFilters = new ViewerFilter[0]; + + /** * The display that this virtual tree viewer is associated with. It is used * for access to the UI thread. */ @@ -178,10 +188,10 @@ public class InternalVirtualTreeModelViewer extends Viewer */ private Runnable fValidateRunnable; - public InternalVirtualTreeModelViewer(Display display, int style, IPresentationContext context) { + public InternalVirtualTreeModelViewer(Display display, int style, IPresentationContext context, IVirtualItemValidator itemValidator) { fDisplay = display; fContext = context; - fTree = new VirtualTree(style); + fTree = new VirtualTree(style, itemValidator); fTree.addItemListener(this); fContentProvider = new TreeModelContentProvider(); @@ -208,12 +218,13 @@ public class InternalVirtualTreeModelViewer extends Viewer public void setInput(Object input) { Object oldInput = fInput; - getContentProvider().inputAboutToChange(this, oldInput , input); - fItemsMap.clear(); getContentProvider().inputChanged(this, oldInput, input); + fItemsMap.clear(); fInput = input; + getContentProvider().postInputChanged(this, oldInput , input); fTree.setData(fInput); - inputChanged(oldInput, fInput); + fTree.setSelection(EMPTY_ITEMS_ARRAY); + inputChanged(fInput, oldInput); refresh(); } @@ -265,7 +276,7 @@ public class InternalVirtualTreeModelViewer extends Viewer validate(); } - VirtualTree getTree() { + public VirtualTree getTree() { return fTree; } @@ -376,8 +387,66 @@ public class InternalVirtualTreeModelViewer extends Viewer return selection; } +// private VirtualTreeSelection adjustSelectionForReplace(VirtualTreeSelection selection, VirtualItem item, +// Object element, Object parentElement) +// { +// if (selection.getItems().containsKey(item)) { +// if (item.getData() == null) { +// // The current item was selected, but its data is null. +// // The data will be replaced by the given element, so to keep +// // it selected, we have to add it to the selection. +// +// // set the element temporarily so that we can call getTreePathFromItem +// item.setData(element); +// TreePath path = getTreePathFromItem(item); +// item.setData(null); +// +// Map map = new LinkedHashMap(selection.getItems()); +// map.put(item, path); +// TreePath[] paths = new TreePath[selection.getPaths().length + 1]; +// int i = 0; +// for (Iterator itr = map.values().iterator(); itr.hasNext();) { +// TreePath nextPath = (TreePath)itr.next(); +// if (nextPath != null) { +// paths[i++] = nextPath; +// } +// } +// return new VirtualTreeSelection(map, paths); +// } else if (!item.getData().equals(element)) { +// // The current item was selected by the new element is +// // different than the previous element in the item. +// // Remove this item from selection. +// Map map = new LinkedHashMap(selection.getItems()); +// map.remove(item); +// TreePath[] paths = new TreePath[selection.getPaths().length - 1]; +// int i = 0; +// for (Iterator itr = map.values().iterator(); itr.hasNext();) { +// TreePath nextPath = (TreePath)itr.next(); +// if (nextPath != null) { +// paths[i++] = nextPath; +// } +// } +// return new VirtualTreeSelection(map, paths); +// } +// } +// if (item.getData() != null || selection.getItems().size() == selection.size() || parentElement == null) { +// // Don't do anything - we are not seeing an instance of bug 185673 +// return selection; +// } +// if (item.getData() == null && selection.getItems().containsKey(item)) { +// } +// // The item was not selected, return the given selection +// return selection; +// } + public void reveal(TreePath path, final int index) { + VirtualItem parentItem = findItem(path); + if (parentItem != null && parentItem.getItemCount() >= index) { + VirtualItem revealItem = parentItem.getItem(new Index(index)); + getTree().showItem(revealItem); + getTree().validate(); + } // TODO: implement reveal() } @@ -425,6 +494,8 @@ public class InternalVirtualTreeModelViewer extends Viewer } private void refresh(VirtualItem item) { + getContentProvider().preserveState(getTreePathFromItem(item)); + if (!item.needsDataUpdate()) { if (item.getParent() != null) { item.setNeedsLabelUpdate(); @@ -488,7 +559,7 @@ public class InternalVirtualTreeModelViewer extends Viewer fAutoExpandToLevel = level; } - private VirtualItem findItem(TreePath path) { + public VirtualItem findItem(TreePath path) { VirtualItem item = fTree; if (path.getSegmentCount() == 0) { return fTree; @@ -503,7 +574,7 @@ public class InternalVirtualTreeModelViewer extends Viewer static private final VirtualItem[] EMPTY_ITEMS_ARRAY = new VirtualItem[0]; - private VirtualItem[] findItems(Object elementOrTreePath) { + public VirtualItem[] findItems(Object elementOrTreePath) { if (elementOrTreePath instanceof TreePath) { VirtualItem item = findItem((TreePath) elementOrTreePath); return item == null ? EMPTY_ITEMS_ARRAY : new VirtualItem[] { item }; @@ -626,8 +697,8 @@ public class InternalVirtualTreeModelViewer extends Viewer if (fNotifyUnmap) { // TODO: should we update the filter with the "new non-identical element"? IContentProvider provider = getContentProvider(); - if (provider instanceof ModelContentProvider) { - ((ModelContentProvider) provider).unmapPath((TreePath) item.getData(TREE_PATH_KEY)); + if (provider instanceof TreeModelContentProvider) { + ((TreeModelContentProvider) provider).unmapPath((TreePath) item.getData(TREE_PATH_KEY)); } } @@ -820,11 +891,18 @@ public class InternalVirtualTreeModelViewer extends Viewer } VirtualItem[] items = fTree.getSelection(); ArrayList list = new ArrayList(items.length); + Map map = new LinkedHashMap(items.length * 4/3); + for (int i = 0; i < items.length; i++) { + TreePath path = null; + if (items[i].getData() != null) { - list.add(getTreePathFromItem(items[i])); + path = getTreePathFromItem(items[i]); + list.add(path); } + map.put(items[i], path); } + //return new VirtualTreeSelection(map, (TreePath[]) list.toArray(new TreePath[list.size()])); return new TreeSelection((TreePath[]) list.toArray(new TreePath[list.size()])); } @@ -929,9 +1007,9 @@ public class InternalVirtualTreeModelViewer extends Viewer * Returns whether the candidate selection should override the current * selection. * - * @param current - * @param curr - * @return + * @param current Current selection in viewer + * @param candidate New potential selection requested by model. + * @return true if candidate selection should be set to viewer. */ public boolean overrideSelection(ISelection current, ISelection candidate) { IModelSelectionPolicy selectionPolicy = ViewerAdapterService.getSelectionPolicy(current, getPresentationContext()); @@ -944,11 +1022,19 @@ public class InternalVirtualTreeModelViewer extends Viewer return !selectionPolicy.isSticky(current, getPresentationContext()); } - private static ViewerFilter[] EMPTY_FILTER_ARRAY = new ViewerFilter[0]; - public ViewerFilter[] getFilters() { - // TODO: Add filter support - return EMPTY_FILTER_ARRAY; + return fFilters; + } + + public void addFilter(ViewerFilter filter) { + ViewerFilter[] newFilters = new ViewerFilter[fFilters.length + 1]; + System.arraycopy(fFilters, 0, newFilters, 0, fFilters.length); + newFilters[fFilters.length] = filter; + fFilters = newFilters; + } + + public void setFilters(ViewerFilter[] filters) { + fFilters = filters; } public void dispose() { @@ -981,7 +1067,7 @@ public class InternalVirtualTreeModelViewer extends Viewer /** * Configures the columns for the given viewer input. * - * @param input + * @param input new viewer input */ private void resetColumns(Object input) { if (input != null) { @@ -1019,8 +1105,6 @@ public class InternalVirtualTreeModelViewer extends Viewer /** * Configures the columns based on the current settings. - * - * @param input */ protected void configureColumns() { if (fColumnPresentation != null) { @@ -1063,9 +1147,7 @@ public class InternalVirtualTreeModelViewer extends Viewer } /** - * Returns whether columns are being displayed currently. - * - * @return + * @return Returns true if columns are being displayed currently. */ public boolean isShowColumns() { if (fColumnPresentation != null) { @@ -1094,7 +1176,7 @@ public class InternalVirtualTreeModelViewer extends Viewer /** * Creates new columns for the given presentation. * - * @param presentation + * @param presentation presentation context to build columns for. */ protected void buildColumns(IColumnPresentation presentation) { PresentationContext presentationContext = (PresentationContext) getPresentationContext(); @@ -1158,9 +1240,6 @@ public class InternalVirtualTreeModelViewer extends Viewer } } - - - /** * Returns the current column presentation for this viewer, or <code>null</code> * if none. @@ -1174,7 +1253,7 @@ public class InternalVirtualTreeModelViewer extends Viewer /** * Save viewer state into the given memento. * - * @param memento + * @param memento Memento to write state to. */ public void saveState(IMemento memento) { if (!fShowColumns.isEmpty()) { @@ -1210,7 +1289,7 @@ public class InternalVirtualTreeModelViewer extends Viewer /** * Initializes viewer state from the memento * - * @param memento + * @param memento Memento to read state from. */ public void initState(IMemento memento) { IMemento[] mementos = memento.getChildren(SHOW_COLUMNS); @@ -1342,7 +1421,7 @@ public class InternalVirtualTreeModelViewer extends Viewer VirtualItem parent = findItem(path); if (parent != null) { - delta.setChildCount(((ModelContentProvider)getContentProvider()).viewToModelCount(path, parent.getItemCount())); + delta.setChildCount(((TreeModelContentProvider)getContentProvider()).viewToModelCount(path, parent.getItemCount())); if (parent.getExpanded()) { if ((flagsToSave & IModelDelta.EXPAND) != 0) { delta.setFlags(delta.getFlags() | IModelDelta.EXPAND); @@ -1381,9 +1460,9 @@ public class InternalVirtualTreeModelViewer extends Viewer flags = flags | IModelDelta.SELECT; } if (expanded || flags != IModelDelta.NO_CHANGE) { - int modelIndex = ((ModelContentProvider)getContentProvider()).viewToModelIndex(parentPath, item.getIndex().intValue()); + int modelIndex = ((TreeModelContentProvider)getContentProvider()).viewToModelIndex(parentPath, item.getIndex().intValue()); TreePath elementPath = parentPath.createChildPath(element); - int numChildren = ((ModelContentProvider)getContentProvider()).viewToModelCount(elementPath, item.getItemCount()); + int numChildren = ((TreeModelContentProvider)getContentProvider()).viewToModelCount(elementPath, item.getItemCount()); ModelDelta childDelta = delta.addNode(element, modelIndex, flags, numChildren); if (expanded) { VirtualItem[] items = item.getItems(); @@ -1432,7 +1511,17 @@ public class InternalVirtualTreeModelViewer extends Viewer return null; } - private String getText(VirtualItem item, int columnIdx) { + public TreePath[] getElementPaths(Object element) { + VirtualItem[] items = findItems(element); + TreePath[] paths = new TreePath[items.length]; + for (int i = 0; i < items.length; i++) { + paths[i] = getTreePathFromItem(items[i]); + } + return paths; + } + + + public String getText(VirtualItem item, int columnIdx) { String[] texts = (String[])item.getData(VirtualItem.LABEL_KEY); if (texts != null && texts.length > columnIdx) { return texts[columnIdx]; @@ -1440,7 +1529,7 @@ public class InternalVirtualTreeModelViewer extends Viewer return null; } - private Image getImage(VirtualItem item, int columnIdx) { + public Image getImage(VirtualItem item, int columnIdx) { ImageDescriptor[] imageDescriptors = (ImageDescriptor[]) item.getData(VirtualItem.IMAGE_KEY); if (imageDescriptors != null && imageDescriptors.length > columnIdx) { return getLabelProvider().getImage(imageDescriptors[columnIdx]); @@ -1448,7 +1537,7 @@ public class InternalVirtualTreeModelViewer extends Viewer return null; } - private Font getFont(VirtualItem item, int columnIdx) { + public Font getFont(VirtualItem item, int columnIdx) { FontData[] fontDatas = (FontData[]) item.getData(VirtualItem.FONT_KEY); if (fontDatas != null) { return getLabelProvider().getFont(fontDatas[columnIdx]); @@ -1478,4 +1567,20 @@ public class InternalVirtualTreeModelViewer extends Viewer public void clearSelectionQuiet() { getTree().setSelection(EMPTY_ITEMS_ARRAY); } + + public boolean getElementChecked(TreePath path) { + // Not supported + return false; + } + + public boolean getElementGrayed(TreePath path) { + // Not supported + return false; + } + + public void setElementChecked(TreePath path, boolean checked, boolean grayed) { + // Not supported + } + + } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/LabelUpdate.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/LabelUpdate.java index 764eecc60..d323b006e 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/LabelUpdate.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/LabelUpdate.java @@ -44,7 +44,6 @@ class LabelUpdate extends Request implements ILabelUpdate, ICheckUpdate { /** * @param viewerInput input at the time the request was made * @param elementPath element the label is for - * @param item item the label is for * @param provider label provider to callback to * @param columnIds column identifiers or <code>null</code> * @param context presentation context @@ -152,7 +151,7 @@ class LabelUpdate extends Request implements ILabelUpdate, ICheckUpdate { /** * Applies settings to viewer cell */ - public void update() { + public void performUpdate() { fProvider.setElementData(fElementPath, fNumColumns, fLabels, fImageDescriptors, fFontDatas, fForegrounds, fBackgrounds, fChecked, fGrayed); fProvider.updateComplete(this); diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/MementoUpdate.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/MementoUpdate.java index 67681686e..13f822129 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/MementoUpdate.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/MementoUpdate.java @@ -25,17 +25,20 @@ abstract class MementoUpdate extends Request implements IViewerUpdate { private Object fElement; private TreePath fElementPath; private IMemento fMemento; - protected ModelContentProvider fProvider; + protected TreeModelContentProvider fProvider; protected Object fViewerInput; /** * Constructs a viewer state request. + * @param provider the content provider to use for the update + * @param viewerInput the current input + * @param elementPath the path of the element to update + * @param element the element to update + * @param memento Memento to update + * @param context the presentation context * - * @param viewer viewer - * @param element element - * @param memento memento */ - public MementoUpdate(ModelContentProvider provider, Object viewerInput, IPresentationContext context, Object element, TreePath elementPath, IMemento memento) { + public MementoUpdate(TreeModelContentProvider provider, Object viewerInput, IPresentationContext context, Object element, TreePath elementPath, IMemento memento) { fContext = context; fElement = element; fElementPath = elementPath; @@ -69,7 +72,7 @@ abstract class MementoUpdate extends Request implements IViewerUpdate { return fMemento; } - public ModelContentProvider getContentProvider() { + public TreeModelContentProvider getContentProvider() { return fProvider; } 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 deleted file mode 100644 index 29f769ec1..000000000 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ModelContentProvider.java +++ /dev/null @@ -1,2285 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2006, 2011 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * IBM Corporation - initial API and implementation - * Pawel Piech - Wind River - Bug 205335: ModelContentProvider does not cancel stale updates when switching viewer input - * Wind River Systems - Fix for viewer state save/restore [188704] - * Pawel Piech (Wind River) - added support for a virtual tree model viewer (Bug 242489) - * Dorin Ciuca - Top index fix (Bug 324100) - *******************************************************************************/ -package org.eclipse.debug.internal.ui.viewers.model; - -import java.io.IOException; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.ISafeRunnable; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.ListenerList; -import org.eclipse.core.runtime.Platform; -import org.eclipse.core.runtime.SafeRunner; -import org.eclipse.core.runtime.Status; -import org.eclipse.core.runtime.jobs.Job; -import org.eclipse.debug.core.IRequest; -import org.eclipse.debug.internal.ui.DebugUIPlugin; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementCompareRequest; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoProvider; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoRequest; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelChangedListener; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDeltaVisitor; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxyFactory; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxyFactory2; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IStateUpdateListener; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdateListener; -import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; -import org.eclipse.jface.viewers.IContentProvider; -import org.eclipse.jface.viewers.TreePath; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.jface.viewers.ViewerFilter; -import org.eclipse.swt.widgets.Display; -import org.eclipse.ui.IMemento; -import org.eclipse.ui.XMLMemento; -import org.eclipse.ui.progress.UIJob; -import org.eclipse.ui.progress.WorkbenchJob; - -/** - * Content provider for a virtual viewer. - * - * @since 3.3 - */ -abstract class ModelContentProvider implements IContentProvider, IModelChangedListener { - - private ITreeModelContentProviderTarget fViewer; - - /** - * Mask used to filter delta updates coming from the model. - */ - private int fModelDeltaMask = ~0; - - /** - * Map tree paths to model proxy responsible for element - * - * Used to install different model proxy instances for one element depending - * on the tree path. - */ - private Map fTreeModelProxies = new HashMap(); // tree model proxy by - // element tree path - - /** - * Map element to model proxy responsible for it. - * - * Used to install a single model proxy which is responsible for all - * instances of an element in the model tree. - */ - private Map fModelProxies = new HashMap(); // model proxy by element - - /** - * Map of nodes that have been filtered from the viewer. - */ - private FilterTransform fTransform = new FilterTransform(); - - /** - * Model listeners - */ - private ListenerList fModelListeners = new ListenerList(); - - /** - * Viewer update listeners - */ - private ListenerList fUpdateListeners = new ListenerList(); - - /** - * State update listeners - */ - private ListenerList fStateUpdateListeners = new ListenerList(); - - /** - * Map of updates in progress: element path -> list of requests - */ - protected Map fRequestsInProgress = new HashMap(); - - /** - * Map of dependent requests waiting for parent requests to complete: - * element path -> list of requests - */ - protected Map fWaitingRequests = new HashMap(); - - /** - * Map of viewer states keyed by viewer input mementos - */ - private Map fViewerStates = Collections.synchronizedMap(new LRUMap(20)); - - /** - * Pending viewer state to be restored - */ - protected ModelDelta fPendingState = null; - - /** - * Flag indicating that the content provider is performing - * state restore operations. - */ - private boolean fInStateRestore = false; - - protected interface IPendingRevealDelta extends IViewerUpdateListener { - /** - * - * @return delta that should be revealed - */ - ModelDelta getDelta(); - - /** - * Dispose the pending operation - */ - void dispose(); - } - - /** - * Postpone restoring REVEAL element until the current updates are complete. - * See bug 324100 - */ - protected IPendingRevealDelta fPendingSetTopItem = null; - - private static class CompareRequestKey { - CompareRequestKey(TreePath path, IModelDelta delta) { - fPath = path; - fDelta = delta; - } - - TreePath fPath; - - IModelDelta fDelta; - - public boolean equals(Object obj) { - if (obj instanceof CompareRequestKey) { - CompareRequestKey key = (CompareRequestKey) obj; - return key.fDelta.equals(fDelta) && key.fPath.equals(fPath); - } - return false; - } - - public int hashCode() { - return fDelta.hashCode() + fPath.hashCode(); - } - } - - private Map fCompareRequestsInProgress = new LinkedHashMap(); - - /** - * Set of IMementoManager's that are currently saving state - */ - private Set fPendingStateSaves = new HashSet(); - - /** - * Used to queue a viewer input for state restore - */ - 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$ - - /** - * LRU cache for viewer states - */ - class LRUMap extends LinkedHashMap { - private static final long serialVersionUID = 1L; - - private int fMaxSize; - - LRUMap(int maxSize) { - super(); - fMaxSize = maxSize; - } - - protected boolean removeEldestEntry(Entry eldest) { - return size() > fMaxSize; - } - } - - /** - * Update type constants - */ - static final int UPDATE_SEQUENCE_BEGINS = 0; - - static final int UPDATE_SEQUENCE_COMPLETE = 1; - - static final int UPDATE_BEGINS = 2; - - static final int UPDATE_COMPLETE = 3; - - /** - * Additional state update type constants - */ - static final int STATE_SAVE_SEQUENCE_BEGINS = 4; - - static final int STATE_SAVE_SEQUENCE_COMPLETE = 5; - - static final int STATE_RESTORE_SEQUENCE_BEGINS = 6; - - static final int STATE_RESTORE_SEQUENCE_COMPLETE = 7; - - /** - * Constant for an empty tree path. - */ - protected static final TreePath EMPTY_TREE_PATH = new TreePath(new Object[] {}); - - // debug flags - public static String DEBUG_PRESENTATION_ID = null; - - public static boolean DEBUG_CONTENT_PROVIDER = false; - - public static boolean DEBUG_UPDATE_SEQUENCE = false; - - public static boolean DEBUG_STATE_SAVE_RESTORE = false; - - public static boolean DEBUG_DELTAS = false; - - public static boolean DEBUG_TEST_PRESENTATION_ID(IPresentationContext context) { - if (context == null) { - return true; - } - return DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(context.getId()); - } - - static { - DEBUG_PRESENTATION_ID = Platform.getDebugOption("org.eclipse.debug.ui/debug/viewers/presentationId"); //$NON-NLS-1$ - if (!DebugUIPlugin.DEBUG || "".equals(DEBUG_PRESENTATION_ID)) { //$NON-NLS-1$ - DEBUG_PRESENTATION_ID = null; - } - DEBUG_CONTENT_PROVIDER = DebugUIPlugin.DEBUG && "true".equals( //$NON-NLS-1$ - Platform.getDebugOption("org.eclipse.debug.ui/debug/viewers/contentProvider")); //$NON-NLS-1$ - DEBUG_UPDATE_SEQUENCE = DebugUIPlugin.DEBUG && "true".equals( //$NON-NLS-1$ - Platform.getDebugOption("org.eclipse.debug.ui/debug/viewers/updateSequence")); //$NON-NLS-1$ - DEBUG_STATE_SAVE_RESTORE = DebugUIPlugin.DEBUG && "true".equals( //$NON-NLS-1$ - Platform.getDebugOption("org.eclipse.debug.ui/debug/viewers/stateSaveRestore")); //$NON-NLS-1$ - DEBUG_DELTAS = DebugUIPlugin.DEBUG && "true".equals( //$NON-NLS-1$ - Platform.getDebugOption("org.eclipse.debug.ui/debug/viewers/deltas")); //$NON-NLS-1$ - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.jface.viewers.IContentProvider#dispose() - */ - public void dispose() { - // cancel pending updates - synchronized (fRequestsInProgress) { - Iterator iterator = fRequestsInProgress.values().iterator(); - while (iterator.hasNext()) { - List requests = (List) iterator.next(); - Iterator reqIter = requests.iterator(); - while (reqIter.hasNext()) { - ((IRequest) reqIter.next()).cancel(); - } - } - fWaitingRequests.clear(); - } - - synchronized(this) { - for (Iterator itr = fPendingStateSaves.iterator(); itr.hasNext(); ) { - ((IMementoManager)itr.next()).cancel(); - } - - fModelListeners.clear(); - fUpdateListeners.clear(); - fStateUpdateListeners.clear(); - disposeAllModelProxies(); - fViewer = null; - } - } - - public synchronized boolean isDisposed() { - return fViewer == null; - } - - /** - * @param viewer the viewer whose input will change - * @param oldInput the old input - * @param newInput the new input - */ - public synchronized void inputAboutToChange(ITreeModelContentProviderTarget viewer, Object oldInput, Object newInput) { - if (newInput != oldInput && oldInput != null) { - for (Iterator itr = fCompareRequestsInProgress.values().iterator(); itr.hasNext();) { - ((ElementCompareRequest) itr.next()).cancel(); - itr.remove(); - } - saveViewerState(oldInput); - } - } - - /* - * (non-Javadoc) - * - * @see - * org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface - * .viewers.Viewer, java.lang.Object, java.lang.Object) - */ - public synchronized void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - fViewer = (ITreeModelContentProviderTarget) viewer; - if (newInput != oldInput) { - cancelSubtreeUpdates(TreePath.EMPTY); - disposeAllModelProxies(); - cancelSubtreeUpdates(TreePath.EMPTY); - fTransform.clear(); - if (newInput != null) { - installModelProxy(newInput, TreePath.EMPTY); - restoreViewerState(newInput); - } - } - } - - /** - * Restores the viewer state unless a save is taking place. If a save is - * taking place, the restore is queued. - * - * @param input - * viewer input - */ - protected synchronized void restoreViewerState(final Object input) { - fPendingState = null; - if (isSavingState()) { - fQueuedRestore = input; - } else { - startRestoreViewerState(input); - } - } - - /** - * Restores viewer state for the given input - * - * @param input - * viewer input - */ - private synchronized void startRestoreViewerState(final Object input) { - fPendingState = null; - final IElementMementoProvider defaultProvider = ViewerAdapterService.getMementoProvider(input); - if (defaultProvider != null) { - // build a model delta representing expansion and selection state - final ModelDelta delta = new ModelDelta(input, IModelDelta.NO_CHANGE); - final XMLMemento inputMemento = XMLMemento.createWriteRoot("VIEWER_INPUT_MEMENTO"); //$NON-NLS-1$ - final IMementoManager manager = new IMementoManager() { - - private IElementMementoRequest fRequest; - - /* - * (non-Javadoc) - * - * @see - * org.eclipse.debug.internal.ui.viewers.model.provisional.viewers - * . - * IMementoManager#requestComplete(org.eclipse.debug.internal.ui - * .viewers.model.provisional.IElementMementoRequest) - */ - public void requestComplete(IElementMementoRequest request) { - notifyStateUpdate(input, UPDATE_COMPLETE, request); - - if (!request.isCanceled() && (request.getStatus() == null || request.getStatus().isOK())) { - XMLMemento keyMemento = (XMLMemento) delta.getElement(); - StringWriter writer = new StringWriter(); - try { - keyMemento.save(writer); - final String keyMementoString = writer.toString(); - ModelDelta stateDelta = (ModelDelta) fViewerStates.get(keyMementoString); - if (stateDelta != null) { - if (DEBUG_STATE_SAVE_RESTORE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("STATE RESTORE INPUT COMARE ENDED : " + fRequest + " - MATCHING STATE FOUND"); //$NON-NLS-1$ //$NON-NLS-2$ - } - - // begin restoration - UIJob job = new UIJob("restore state") { //$NON-NLS-1$ - public IStatus runInUIThread(IProgressMonitor monitor) { - if (!isDisposed() && input.equals(getViewer().getInput())) { - ModelDelta stateDelta2 = (ModelDelta) fViewerStates.remove(keyMementoString); - if (stateDelta2 != null) { - if (DEBUG_STATE_SAVE_RESTORE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("STATE RESTORE BEGINS"); //$NON-NLS-1$ - System.out.println("\tRESTORE: " + stateDelta2.toString()); //$NON-NLS-1$ - notifyStateUpdate(input, STATE_RESTORE_SEQUENCE_BEGINS, null); - } - stateDelta2.setElement(input); - fPendingState = stateDelta2; - doInitialRestore(fPendingState); - } - } else { - if (DEBUG_STATE_SAVE_RESTORE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("STATE RESTORE CANCELED."); //$NON-NLS-1$ - } - } - return Status.OK_STATUS; - } - - }; - job.setSystem(true); - job.schedule(); - } else { - if (DEBUG_STATE_SAVE_RESTORE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("STATE RESTORE INPUT COMARE ENDED : " + fRequest + " - NO MATCHING STATE"); //$NON-NLS-1$ //$NON-NLS-2$ - } - } - } catch (IOException e) { - DebugUIPlugin.log(e); - } - } else { - notifyStateUpdate(input, STATE_RESTORE_SEQUENCE_BEGINS, null); - } - } - - /* - * (non-Javadoc) - * - * @see - * org.eclipse.debug.internal.ui.viewers.model.provisional.viewers - * .IMementoManager#processReqeusts() - */ - public void processReqeusts() { - notifyStateUpdate(input, STATE_RESTORE_SEQUENCE_BEGINS, null); - if (DEBUG_STATE_SAVE_RESTORE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("STATE RESTORE INPUT COMARE BEGIN : " + fRequest); //$NON-NLS-1$ - } - notifyStateUpdate(input, UPDATE_BEGINS, fRequest); - defaultProvider.encodeElements(new IElementMementoRequest[] { fRequest }); - } - - /* - * (non-Javadoc) - * - * @see - * org.eclipse.debug.internal.ui.viewers.model.provisional.viewers - * . - * IMementoManager#addRequest(org.eclipse.debug.internal.ui.viewers - * .model.provisional.IElementMementoRequest) - */ - public void addRequest(IElementMementoRequest req) { - fRequest = req; - } - - public void cancel() { - // not used - } - - }; - manager.addRequest(new ElementMementoRequest(ModelContentProvider.this, getViewer().getInput(), manager, - getPresentationContext(), delta.getElement(), getViewerTreePath(delta), inputMemento, delta)); - manager.processReqeusts(); - } else { - if (DEBUG_STATE_SAVE_RESTORE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("STATE RESTORE: No input memento provider"); //$NON-NLS-1$ - } - } - } - - /** - * Restore selection/expansion based on items already in the viewer - * @param delta the {@link ModelDelta} to restore from - */ - protected abstract void doInitialRestore(ModelDelta delta); - - /** - * @param delta the {@link ModelDelta} to restore from - * @param knowsHasChildren if the content provider has computed its children - * @param knowsChildCount if the content provider has already computed the child count - * @param checkChildrenRealized if any realized children should be checked - */ - abstract void restorePendingStateNode(final ModelDelta delta, boolean knowsHasChildren, boolean knowsChildCount, boolean checkChildrenRealized); - - public void cancelRestore(final TreePath path, final int flags) { - if (fInStateRestore) { - // If we are currently processing pending state already, ignore - // cancelRestore requests. These requests may be triggered in the viewer - // by changes to the tree state (Bug 295585). - return; - } - - if ((flags & IModelDelta.REVEAL) != 0 && fPendingSetTopItem != null) { - fPendingSetTopItem.dispose(); - return; - } - - // Nothing else to do - if (fPendingState == null) { - return; - } - - if ((flags & (IModelDelta.SELECT | IModelDelta.REVEAL)) != 0) { - // If we're canceling reveal and this is waiting for updates to complete - // then just cancel it and return - - // If we're canceling select or reveal, cancel it for all of pending deltas - final int mask = flags & (IModelDelta.SELECT | IModelDelta.REVEAL); - fPendingState.accept(new IModelDeltaVisitor() { - public boolean visit(IModelDelta delta, int depth) { - int deltaFlags = delta.getFlags(); - int newFlags = deltaFlags & ~mask; - if (DEBUG_STATE_SAVE_RESTORE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - if (deltaFlags != newFlags) { - System.out.println("\tCANCEL: " + delta.getElement() + "(" + Integer.toHexString(deltaFlags & mask) + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - } - } - ((ModelDelta)delta).setFlags(newFlags); - return true; - } - }); - } - if ((flags & ~(IModelDelta.SELECT | IModelDelta.REVEAL)) != 0) { - final int mask = flags & ~(IModelDelta.SELECT | IModelDelta.REVEAL); - // For other flags (EXPAND/COLLAPSE), cancel only from the matching path. - fPendingState.accept(new IModelDeltaVisitor() { - public boolean visit(IModelDelta delta, int depth) { - if (depth < path.getSegmentCount()) { - // Descend until we reach a matching depth. - TreePath deltaPath = getViewerTreePath(delta); - if (path.startsWith(deltaPath, null)) { - return true; - } else { - return false; - } - } - else if (depth == path.getSegmentCount()) { - TreePath deltaPath = getViewerTreePath(delta); - if (deltaPath.equals(path)) { - int deltaFlags = delta.getFlags(); - int newFlags = deltaFlags & ~mask; - if (DEBUG_STATE_SAVE_RESTORE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - if (deltaFlags != newFlags) { - System.out.println("\tCANCEL: " + delta.getElement() + "(" + Integer.toHexString(deltaFlags & mask) + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - } - } - ((ModelDelta)delta).setFlags(newFlags); - if ((flags & IModelDelta.EXPAND) != 0) { - // Descend delta to clear the EXPAND flags of a canceled expand - return true; - } - } - return false; - } else { - // We're clearing out flags of a matching sub-tree - // assert (flags & IModelDelta.EXPAND) != 0; - - if (DEBUG_STATE_SAVE_RESTORE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - if (delta.getFlags() != IModelDelta.NO_CHANGE) { - System.out.println("\tCANCEL: " + delta.getElement() + "(" + Integer.toHexString(delta.getFlags()) + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - } - } - ((ModelDelta)delta).setFlags(IModelDelta.NO_CHANGE); - return true; - } - } - }); - } - } - - protected void appendToPendingStateDelta(final TreePath path) { - if (DEBUG_STATE_SAVE_RESTORE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - 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); - } - - if (!fViewer.saveElementState(path, delta, IModelDelta.COLLAPSE | IModelDelta.EXPAND | IModelDelta.SELECT)) { - // Path to save the state was not found or there was no - // (expansion) state to save! Abort. - if (DEBUG_STATE_SAVE_RESTORE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("STATE APPEND CANCEL: Element " + path.getLastSegment() + " not found."); //$NON-NLS-1$ //$NON-NLS-2$ - } - return; - } - - // 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 d, int depth) { - if ((d.getFlags() & IModelDelta.EXPAND) != 0) { - ((ModelDelta) d).setFlags(d.getFlags() | IModelDelta.CONTENT); - } - return true; - } - }); - - if (DEBUG_STATE_SAVE_RESTORE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("\tAPPEND DELTA: " + 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_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("\tAPPEND OUTSTANDING RESTORE: " + fPendingState); //$NON-NLS-1$ - } - - // If the append delta is generated for a sub-tree, copy the pending dela - // attributes into the pending delta. - if (path.getSegmentCount() > 0) { - fPendingState.accept( new IModelDeltaVisitor() { - public boolean visit(IModelDelta pendingDeltaNode, int depth) { - TreePath pendingDeltaPath = getViewerTreePath(pendingDeltaNode); - if (path.startsWith(pendingDeltaPath, null)) - { - ModelDelta appendDelta = findDeltaForPath(appendDeltaRoot, pendingDeltaPath); - appendDelta.setFlags(pendingDeltaNode.getFlags()); - appendDelta.setChildCount(pendingDeltaNode.getChildCount()); - appendDelta.setIndex(pendingDeltaNode.getIndex()); - return true; - } - return false; - } - }); - } - - // Copy the pending state into the new appended state. - fPendingState.accept( new IModelDeltaVisitor() { - public boolean visit(IModelDelta pendingDeltaNode, int depth) { - // Skip 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 = findSubDeltaParent(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_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("\tSKIPPED: " + 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; - } - } - - }); - } - - if (appendDeltaRoot.getChildDeltas().length > 0) { - // Set the new delta root as the pending state delta. - if (fPendingState == null) { - notifyStateUpdate(appendDeltaRoot.getElement(), STATE_RESTORE_SEQUENCE_BEGINS, null); - } - fPendingState = appendDeltaRoot; - if (DEBUG_STATE_SAVE_RESTORE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("STATE APPEND COMPLETE " + fPendingState); //$NON-NLS-1$ - } - } else { - if (DEBUG_STATE_SAVE_RESTORE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("STATE APPEND CANCELED: No Data"); //$NON-NLS-1$ - } - } - } - - /** - * Perform any restoration required for the given tree path. - * - * @param path the tree path to update - * @param modelIndex the index in the current model - * @param knowsHasChildren if the content provider knows it has children already - * @param knowsChildCount if the content provider knows the current child count already - * @param checkChildrenRealized if any realized children should be checked or not - */ - protected synchronized void restorePendingStateOnUpdate(final TreePath path, final int modelIndex, final boolean knowsHasChildren, - final boolean knowsChildCount, final boolean checkChildrenRealized) { - if (fPendingState == null) { - return; - } - - IModelDeltaVisitor visitor = new IModelDeltaVisitor() { - public boolean visit(final IModelDelta delta, int depth) { - - Object element = delta.getElement(); - Object potentialMatch = depth != 0 ? path.getSegment(depth - 1) : getViewer().getInput(); - // Only process if the depth in the delta matches the tree path. - if (depth == path.getSegmentCount()) { - if (element instanceof IMemento) { - IElementMementoProvider provider = ViewerAdapterService.getMementoProvider(potentialMatch); - if (provider == null) { - provider = ViewerAdapterService.getMementoProvider(getViewer().getInput()); - } - if (provider != null) { - CompareRequestKey key = new CompareRequestKey(path, delta); - ElementCompareRequest existingRequest = (ElementCompareRequest) fCompareRequestsInProgress - .get(key); - if (existingRequest != null) { - // Check all the running compare updates for a - // matching tree path. - // 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, checkChildrenRealized); - fCompareRequestsInProgress.put(key, compareRequest); - if (DEBUG_STATE_SAVE_RESTORE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("\tSTATE BEGIN: " + compareRequest); //$NON-NLS-1$ - } - notifyStateUpdate(element, UPDATE_BEGINS, compareRequest); - provider.compareElements(new IElementCompareRequest[] { compareRequest }); - } - } - } else if (element.equals(potentialMatch)) { - // Element comparison already succeeded, and it matches - // our element. - // Call restore with delta to process the delta flags. - restorePendingStateNode((ModelDelta) delta, knowsHasChildren, knowsChildCount, checkChildrenRealized); - } - return false; - } - // Only follow the paths that match the delta. - return element.equals(potentialMatch); - } - }; - - try { - fInStateRestore = true; - fPendingState.accept(visitor); - } - finally { - fInStateRestore = false; - } - checkIfRestoreComplete(); - } - - void compareFinished(ElementCompareRequest request, ModelDelta delta) { - notifyStateUpdate(request.getViewerInput(), UPDATE_COMPLETE, request); - if (DEBUG_STATE_SAVE_RESTORE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("\tSTATE END: " + request + " = " + false); //$NON-NLS-1$ //$NON-NLS-2$ - } - - fCompareRequestsInProgress.remove(new CompareRequestKey(request.getElementPath(), delta)); - if (!request.isCanceled()) { - if (request.isEqual()) { - delta.setElement(request.getElement()); - restorePendingStateNode(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 of the element that it was compared - // against. If this is the case, strip the reveal flag from - // the delta as it is most likely not applicable anymore. - if ((delta.getFlags() & IModelDelta.REVEAL) != 0 && delta.getIndex() == request.getModelIndex()) { - delta.setFlags(delta.getFlags() & ~IModelDelta.REVEAL); - } - } - } - checkIfRestoreComplete(); - } - - /** - * Saves the viewer's state for the previous input. * @param oldInput - * @param input the {@link ModelDelta} input - */ - protected void saveViewerState(Object input) { - IElementMementoProvider stateProvider = ViewerAdapterService.getMementoProvider(input); - if (stateProvider != null) { - if (DEBUG_STATE_SAVE_RESTORE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - 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_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("\tSAVE DELTA FROM VIEW:\n" + saveDeltaRoot); //$NON-NLS-1$ - } - - // check if pending restore reveal - if (fPendingSetTopItem != null) { - // set back the pending reveal flag - ModelDelta revealDelta = fPendingSetTopItem.getDelta(); - revealDelta.setFlags(revealDelta.getFlags() | IModelDelta.REVEAL); - - fPendingSetTopItem.dispose(); - - ModelDelta saveDeltaNode = findSubDeltaParent(saveDeltaRoot, revealDelta); - if (saveDeltaNode != null) { - clearRevealFlag(saveDeltaRoot); - boolean childFounded = false; - for (int i = 0; i < saveDeltaNode.getChildDeltas().length; i++) { - ModelDelta child = (ModelDelta)saveDeltaNode.getChildDeltas()[i]; - if (deltasEqual(child, revealDelta)) { - child.setFlags(child.getFlags() | IModelDelta.REVEAL); - childFounded = true; - break; - } - } - - // the node should be added if not found - if (!childFounded) { - saveDeltaNode.setChildCount(revealDelta.getParentDelta().getChildCount()); - copyIntoDelta(revealDelta, saveDeltaNode); - } - } else { - if (DEBUG_STATE_SAVE_RESTORE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("\tSKIPPED: " + revealDelta.getElement()); //$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_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("\tSAVE 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 = findSubDeltaParent(saveDeltaRoot, pendingDeltaNode); - if (saveDeltaNode != null && !isDeltaInParent(pendingDeltaNode, saveDeltaNode) - && pendingDeltaNode.getFlags() != IModelDelta.NO_CHANGE) { - // There should be only one delta element with - // the REVEAL flag in the entire save delta. The - // reveal flag in the pending delta trumps the one - // in the save delta because most likely the restore - // operation did not yet complete the reveal - // operation. - if ((pendingDeltaNode.getFlags() & IModelDelta.REVEAL) != 0) { - clearRevealFlag(saveDeltaRoot); - } - saveDeltaNode.setChildCount(pendingDeltaNode.getParentDelta().getChildCount()); - copyIntoDelta(pendingDeltaNode, saveDeltaNode); - } else { - if (DEBUG_STATE_SAVE_RESTORE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("\tSKIPPED: " + 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 (saveDeltaRoot.getChildDeltas().length > 0) { - // encode delta with mementos in place of elements, in non-UI - // thread - encodeDelta(saveDeltaRoot, stateProvider); - } else { - if (DEBUG_STATE_SAVE_RESTORE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("STATE SAVE CANCELED, NO DATA"); //$NON-NLS-1$ - } - } - } - } - - private void clearRevealFlag(ModelDelta saveRootDelta) { - IModelDeltaVisitor clearDeltaVisitor = new IModelDeltaVisitor() { - public boolean visit(IModelDelta delta, int depth) { - if ((delta.getFlags() & IModelDelta.REVEAL) != 0) { - ((ModelDelta) delta).setFlags(delta.getFlags() & ~IModelDelta.REVEAL); - } - return true; - } - }; - saveRootDelta.accept(clearDeltaVisitor); - } - - private ModelDelta findSubDeltaParent(ModelDelta destinationDeltaRoot, IModelDelta subDelta) { - // Create a path of elements to the sub-delta. - LinkedList deltaPath = new LinkedList(); - IModelDelta delta = subDelta; - while (delta.getParentDelta() != null) { - delta = delta.getParentDelta(); - deltaPath.addFirst(delta); - } - - // For each element in the path of the sub-delta, find the corresponding - // element in the destination delta - Iterator itr = deltaPath.iterator(); - // Skip the root element - itr.next(); - ModelDelta saveDelta = destinationDeltaRoot; - outer: while (itr.hasNext()) { - IModelDelta itrDelta = (IModelDelta) itr.next(); - for (int i = 0; i < saveDelta.getChildDeltas().length; i++) { - if (deltasEqual(saveDelta.getChildDeltas()[i], itrDelta)) { - saveDelta = (ModelDelta) saveDelta.getChildDeltas()[i]; - continue outer; - } - } - return null; - } - return saveDelta; - } - - private ModelDelta findDeltaForPath(ModelDelta root, TreePath path) { - ModelDelta delta = root; - for (int i = 0; i < path.getSegmentCount(); i++) { - delta = delta.getChildDelta(path.getSegment(i)); - if (delta == null) { - return null; - } - } - return delta; - } - - private boolean deltasEqual(IModelDelta d1, IModelDelta d2) { - // Note: don't compare the child count, because it is - // incorrect for nodes which have not been expanded yet. - return d1.getElement().equals(d2.getElement()) && d1.getIndex() == d2.getIndex(); - } - - private boolean isDeltaInParent(IModelDelta delta, ModelDelta destParent) { - for (int i = 0; i < destParent.getChildDeltas().length; i++) { - if (deltasEqual(destParent.getChildDeltas()[i], delta)) { - return true; - } - } - return false; - } - - private void copyIntoDelta(IModelDelta delta, ModelDelta destParent) { - ModelDelta newDelta = destParent.addNode(delta.getElement(), delta.getIndex(), delta.getFlags(), delta - .getChildCount()); - for (int i = 0; i < delta.getChildDeltas().length; i++) { - copyIntoDelta(delta.getChildDeltas()[i], newDelta); - } - } - - /** - * Encodes delta elements into mementos using the given provider. - * @param rootDelta the {@link ModelDelta} to encode - * @param defaultProvider the default provider to use when processing the given delta - * - */ - protected void encodeDelta(final ModelDelta rootDelta, final IElementMementoProvider defaultProvider) { - final Object input = rootDelta.getElement(); - final XMLMemento inputMemento = XMLMemento.createWriteRoot("VIEWER_INPUT_MEMENTO"); //$NON-NLS-1$ - final XMLMemento childrenMemento = XMLMemento.createWriteRoot("CHILDREN_MEMENTO"); //$NON-NLS-1$ - final IMementoManager manager = new IMementoManager() { - - /** - * list of memento fRequests - */ - private List fRequests = new ArrayList(); - - /** - * Flag indicating whether the encoding of delta has been canceled. - */ - private boolean fCanceled = false; - - /* - * (non-Javadoc) - * - * @see - * org.eclipse.debug.internal.ui.viewers.model.provisional.viewers - * .IMementoManager - * #requestComplete(org.eclipse.debug.internal.ui.viewers - * .model.provisional.IElementMementoRequest) - */ - public void requestComplete(IElementMementoRequest request) { - notifyStateUpdate(input, UPDATE_COMPLETE, request); - if (DEBUG_STATE_SAVE_RESTORE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("\tSTATE END: " + request); //$NON-NLS-1$ - } - - if (!request.isCanceled() && (request.getStatus() == null || request.getStatus().isOK())) { - boolean requestsComplted = false; - synchronized(this) { - if (!fCanceled) { - fRequests.remove(request); - requestsComplted = fRequests.isEmpty(); - } - } - if (requestsComplted) { - XMLMemento keyMemento = (XMLMemento) rootDelta.getElement(); - StringWriter writer = new StringWriter(); - try { - keyMemento.save(writer); - fViewerStates.put(writer.toString(), rootDelta); - } catch (IOException e) { - DebugUIPlugin.log(e); - } - if (DEBUG_STATE_SAVE_RESTORE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("STATE SAVE COMPLETED: " + rootDelta); //$NON-NLS-1$ - } - stateSaveComplete(input, this); - } - } else { - cancel(); - } - } - - public void cancel() { - synchronized (this) { - if (fCanceled) { - return; - } - - fCanceled = true; - Iterator iterator = fRequests.iterator(); - while (iterator.hasNext()) { - IElementMementoRequest req = (IElementMementoRequest) iterator.next(); - req.cancel(); - } - fRequests.clear(); - if (DEBUG_STATE_SAVE_RESTORE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("STATE SAVE ABORTED: " + rootDelta.getElement()); //$NON-NLS-1$ - } - } - stateSaveComplete(input, this); - } - - /* - * (non-Javadoc) - * - * @see - * org.eclipse.debug.internal.ui.viewers.model.provisional.viewers - * .IMementoManager#processReqeusts() - */ - public synchronized void processReqeusts() { - Map providers = new HashMap(); - Iterator iterator = fRequests.iterator(); - while (iterator.hasNext()) { - IElementMementoRequest request = (IElementMementoRequest) iterator.next(); - notifyStateUpdate(input, UPDATE_BEGINS, request); - IElementMementoProvider provider = ViewerAdapterService.getMementoProvider(request.getElement()); - if (provider == null) { - provider = defaultProvider; - } - List reqs = (List) providers.get(provider); - if (reqs == null) { - reqs = new ArrayList(); - providers.put(provider, reqs); - } - reqs.add(request); - } - iterator = providers.entrySet().iterator(); - while (iterator.hasNext()) { - Entry entry = (Entry) iterator.next(); - IElementMementoProvider provider = (IElementMementoProvider) entry.getKey(); - List reqs = (List) entry.getValue(); - provider.encodeElements((IElementMementoRequest[]) reqs.toArray(new IElementMementoRequest[reqs - .size()])); - } - } - - /* - * (non-Javadoc) - * - * @see - * org.eclipse.debug.internal.ui.viewers.model.provisional.viewers - * .IMementoManager - * #addRequest(org.eclipse.debug.internal.ui.viewers. - * model.provisional.IElementMementoRequest) - */ - public synchronized void addRequest(IElementMementoRequest request) { - fRequests.add(request); - } - - }; - IModelDeltaVisitor visitor = new IModelDeltaVisitor() { - public boolean visit(IModelDelta delta, int depth) { - // Add the CONTENT flag to all nodes with an EXPAND flag. - // During restoring, this flag is used as a marker indicating - // whether all the content of a given element has been - // retrieved. - if ((delta.getFlags() | IModelDelta.EXPAND) != 0) { - ((ModelDelta)delta).setFlags(delta.getFlags() | IModelDelta.CONTENT); - } - - // This is the root element, save the root element memento in 'inputMemento'. - if (delta.getParentDelta() == null) { - manager.addRequest(new ElementMementoRequest(ModelContentProvider.this, input, manager, - getPresentationContext(), delta.getElement(), getViewerTreePath(delta), inputMemento, - (ModelDelta) delta)); - } else { - // If this is another node element, save the memento to a children memento. - if (!(delta.getElement() instanceof XMLMemento)) { - manager.addRequest(new ElementMementoRequest(ModelContentProvider.this, input, manager, - getPresentationContext(), delta.getElement(), getViewerTreePath(delta), childrenMemento - .createChild("CHILD_ELEMENT"), (ModelDelta) delta)); //$NON-NLS-1$ - } - } - return true; - } - }; - rootDelta.accept(visitor); - stateSaveStarted(input, manager); - manager.processReqeusts(); - } - - /** - * Called when a state save is starting. - * @param input the {@link ModelDelta} input - * @param manager the manager to notify - */ - private synchronized void stateSaveStarted(Object input, IMementoManager manager) { - notifyStateUpdate(input, STATE_SAVE_SEQUENCE_BEGINS, null); - fPendingStateSaves.add(manager); - } - - /** - * Called when a state save is complete. - * @param input the {@link ModelDelta} input - * @param manager the manager to notify - */ - private synchronized void stateSaveComplete(Object input, IMementoManager manager) { - notifyStateUpdate(input, STATE_SAVE_SEQUENCE_COMPLETE, null); - fPendingStateSaves.remove(manager); - if (fQueuedRestore != null) { - Object temp = fQueuedRestore; - fQueuedRestore = null; - restoreViewerState(temp); - } - } - - /** - * Returns whether any state saving is in progress. - * - * @return whether any state saving is in progress - */ - private synchronized boolean isSavingState() { - return !fPendingStateSaves.isEmpty(); - } - - /** - * Builds a delta with the given root delta for expansion/selection state. - * - * @param delta - * root delta - */ - protected abstract void buildViewerState(ModelDelta delta); - - /** - * Uninstalls the model proxy installed for the given element, if any. - * @param path the {@link TreePath} to dispose the model proxy for - */ - protected synchronized void disposeModelProxy(TreePath path) { - IModelProxy proxy = (IModelProxy) fTreeModelProxies.remove(path); - if (proxy != null) { - proxy.dispose(); - } - proxy = (IModelProxy) fModelProxies.remove(path.getLastSegment()); - if (proxy != null) { - proxy.dispose(); - } - } - - /** - * Uninstalls each model proxy - */ - protected synchronized void disposeAllModelProxies() { - Iterator updatePolicies = fModelProxies.values().iterator(); - while (updatePolicies.hasNext()) { - IModelProxy proxy = (IModelProxy) updatePolicies.next(); - proxy.dispose(); - } - fModelProxies.clear(); - - updatePolicies = fTreeModelProxies.values().iterator(); - while (updatePolicies.hasNext()) { - IModelProxy proxy = (IModelProxy) updatePolicies.next(); - proxy.dispose(); - } - fTreeModelProxies.clear(); - } - - protected synchronized IModelProxy[] getModelProxies() { - IModelProxy[] proxies = new IModelProxy[fTreeModelProxies.size() + fModelProxies.size()]; - fTreeModelProxies.values().toArray(proxies); - System.arraycopy(fModelProxies.values().toArray(), 0, proxies, fModelProxies.size(), fModelProxies.size()); - return proxies; - } - - protected synchronized IModelProxy getElementProxy(TreePath path) { - while (path != null) { - IModelProxy proxy = (IModelProxy) fTreeModelProxies.get(path); - if (proxy != null) { - return proxy; - } - - Object element = path.getSegmentCount() == 0 ? getViewer().getInput() : path.getLastSegment(); - proxy = (IModelProxy) fModelProxies.get(element); - if (proxy != null) { - return proxy; - } - - path = path.getParentPath(); - } - return null; - } - - /** - * Installs the model proxy for the given element into this content provider - * if not already installed. - * @param input the input to install the model proxy on - * @param path the {@link TreePath} to install the proxy for - */ - protected synchronized void installModelProxy(Object input, TreePath path) { - if (!fTreeModelProxies.containsKey(path) && !fModelProxies.containsKey(path.getLastSegment())) { - Object element = path.getSegmentCount() != 0 ? path.getLastSegment() : input; - IModelProxy proxy = null; - IModelProxyFactory2 modelProxyFactory2 = ViewerAdapterService.getModelProxyFactory2(element); - if (modelProxyFactory2 != null) { - proxy = modelProxyFactory2.createTreeModelProxy(input, path, getPresentationContext()); - if (proxy != null) { - fTreeModelProxies.put(path, proxy); - } - } - if (proxy == null) { - IModelProxyFactory modelProxyFactory = ViewerAdapterService.getModelProxyFactory(element); - if (modelProxyFactory != null) { - proxy = modelProxyFactory.createModelProxy(element, getPresentationContext()); - if (proxy != null) { - fModelProxies.put(element, proxy); - } - } - } - - if (proxy != null) { - final IModelProxy finalProxy = proxy; - if (proxy != null) { - Job job = new Job("Model Proxy installed notification job") {//$NON-NLS-1$ - protected IStatus run(IProgressMonitor monitor) { - if (!monitor.isCanceled()) { - IPresentationContext context = null; - Viewer viewer = null; - synchronized (ModelContentProvider.this) { - if (!isDisposed()) { - context = getPresentationContext(); - viewer = (Viewer) getViewer(); - } - } - if (context != null && !finalProxy.isDisposed()) { - finalProxy.init(context); - finalProxy.addModelChangedListener(ModelContentProvider.this); - finalProxy.installed(viewer); - } - } - return Status.OK_STATUS; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.core.runtime.jobs.Job#shouldRun() - */ - public boolean shouldRun() { - return !isDisposed(); - } - }; - job.setSystem(true); - job.schedule(); - } - } - } - } - - /** - * Returns the presentation context for this content provider. - * - * @return presentation context - */ - protected abstract IPresentationContext getPresentationContext(); - - /* - * (non-Javadoc) - * - * @see - * org.eclipse.debug.internal.ui.viewers.provisional.IModelChangedListener - * #modelChanged - * (org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta) - */ - public void modelChanged(final IModelDelta delta, final IModelProxy proxy) { - Display display = null; - - // Check if the viewer is still available, i.e. if the content provider - // is not disposed. - synchronized(this) { - if (fViewer != null && !proxy.isDisposed()) { - display = fViewer.getDisplay(); - } - } - if (display != null) { - // If we're in display thread, process the delta immediately to - // avoid "skid" in processing events. - if (Thread.currentThread().equals(display.getThread())) { - doModelChanged(delta, proxy); - } - else { - WorkbenchJob job = new WorkbenchJob(fViewer.getDisplay(), "process model delta") { //$NON-NLS-1$ - public IStatus runInUIThread(IProgressMonitor monitor) { - doModelChanged(delta, proxy); - return Status.OK_STATUS; - } - }; - job.setSystem(true); - job.schedule(); - } - } - } - - private void doModelChanged(IModelDelta delta, IModelProxy proxy) { - if (!proxy.isDisposed()) { - if (DEBUG_DELTAS && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - DebugUIPlugin.debug("RECEIVED DELTA: " + delta.toString()); //$NON-NLS-1$ - } - - updateModel(delta, getModelDeltaMask()); - - // Call model listeners after updating the viewer model. - Object[] listeners = fModelListeners.getListeners(); - for (int i = 0; i < listeners.length; i++) { - ((IModelChangedListener) listeners[i]).modelChanged(delta, proxy); - } - } - } - - /** - * @param mask the new mask to set - * @see ITreeModelContentProvider for a list of masks - */ - public void setModelDeltaMask(int mask) { - fModelDeltaMask = mask; - } - - /** - * @return the current model delta mask - * @see IModelDelta for a list of masks - */ - public int getModelDeltaMask() { - return fModelDeltaMask; - } - - public void updateModel(IModelDelta delta, int mask) { - IModelDelta[] deltaArray = new IModelDelta[] { delta }; - updateNodes(deltaArray, mask & (IModelDelta.REMOVED | IModelDelta.UNINSTALL)); - updateNodes(deltaArray, mask & ITreeModelContentProvider.UPDATE_MODEL_DELTA_FLAGS - & ~(IModelDelta.REMOVED | IModelDelta.UNINSTALL)); - updateNodes(deltaArray, mask & ITreeModelContentProvider.CONTROL_MODEL_DELTA_FLAGS); - - checkIfRestoreComplete(); - } - - /** - * Updates the viewer with the following deltas. - * - * @param nodes Model deltas to be processed. - * @param mask the model delta mask - * @see IModelDelta for a list of masks - */ - protected void updateNodes(IModelDelta[] nodes, int mask) { - for (int i = 0; i < nodes.length; i++) { - IModelDelta node = nodes[i]; - int flags = node.getFlags() & mask; - - if ((flags & IModelDelta.ADDED) != 0) { - handleAdd(node); - } - if ((flags & IModelDelta.REMOVED) != 0) { - handleRemove(node); - } - if ((flags & IModelDelta.CONTENT) != 0) { - handleContent(node); - } - if ((flags & IModelDelta.STATE) != 0) { - handleState(node); - } - if ((flags & IModelDelta.INSERTED) != 0) { - handleInsert(node); - } - if ((flags & IModelDelta.REPLACED) != 0) { - handleReplace(node); - } - if ((flags & IModelDelta.INSTALL) != 0) { - handleInstall(node); - } - if ((flags & IModelDelta.UNINSTALL) != 0) { - handleUninstall(node); - } - if ((flags & IModelDelta.EXPAND) != 0) { - handleExpand(node); - } - if ((flags & IModelDelta.COLLAPSE) != 0) { - handleCollapse(node); - } - if ((flags & IModelDelta.SELECT) != 0) { - handleSelect(node); - } - if ((flags & IModelDelta.REVEAL) != 0) { - handleReveal(node); - } - updateNodes(node.getChildDeltas(), mask); - } - } - - protected abstract void handleState(IModelDelta delta); - - protected abstract void handleSelect(IModelDelta delta); - - protected abstract void handleExpand(IModelDelta delta); - - protected abstract void handleCollapse(IModelDelta delta); - - protected abstract void handleContent(IModelDelta delta); - - protected abstract void handleRemove(IModelDelta delta); - - protected abstract void handleAdd(IModelDelta delta); - - protected abstract void handleInsert(IModelDelta delta); - - protected abstract void handleReplace(IModelDelta delta); - - protected abstract void handleReveal(IModelDelta delta); - - protected void handleInstall(IModelDelta delta) { - installModelProxy(getViewer().getInput(), getFullTreePath(delta)); - } - - protected void handleUninstall(IModelDelta delta) { - disposeModelProxy(getFullTreePath(delta)); - } - - /** - * Returns a tree path for the node including the root element. - * - * @param node - * model delta - * @return corresponding tree path - */ - protected TreePath getFullTreePath(IModelDelta node) { - ArrayList list = new ArrayList(); - while (node.getParentDelta() != null) { - list.add(0, node.getElement()); - node = node.getParentDelta(); - } - return new TreePath(list.toArray()); - } - - /** - * Returns a tree path for the node, *not* including the root element. - * - * @param node - * model delta - * @return corresponding tree path - */ - protected TreePath getViewerTreePath(IModelDelta node) { - ArrayList list = new ArrayList(); - IModelDelta parentDelta = node.getParentDelta(); - while (parentDelta != null) { - list.add(0, node.getElement()); - node = parentDelta; - parentDelta = node.getParentDelta(); - } - return new TreePath(list.toArray()); - } - - /** - * Returns the viewer this content provider is working for. - * - * @return viewer - */ - protected ITreeModelContentProviderTarget getViewer() { - return fViewer; - } - - /** - * Translates and returns the given child index from the viewer coordinate - * space to the model coordinate space. - * - * @param parentPath - * path to parent element - * @param index - * index of child element in viewer (filtered) space - * @return index of child element in model (raw) space - */ - public/* protected */int viewToModelIndex(TreePath parentPath, int index) { - return fTransform.viewToModelIndex(parentPath, index); - } - - /** - * Translates and returns the given child count from the viewer coordinate - * space to the model coordinate space. - * - * @param parentPath - * path to parent element - * @param count - * number of child elements in viewer (filtered) space - * @return number of child elements in model (raw) space - */ - public/* protected */int viewToModelCount(TreePath parentPath, int count) { - return fTransform.viewToModelCount(parentPath, count); - } - - /** - * Translates and returns the given child index from the model coordinate - * space to the viewer coordinate space. - * - * @param parentPath - * path to parent element - * @param index - * index of child element in model (raw) space - * @return index of child element in viewer (filtered) space or -1 if - * filtered - */ - public int modelToViewIndex(TreePath parentPath, int index) { - return fTransform.modelToViewIndex(parentPath, index); - } - - /** - * Translates and returns the given child count from the model coordinate - * space to the viewer coordinate space. - * - * @param parentPath - * path to parent element - * @param count - * child count element in model (raw) space - * @return child count in viewer (filtered) space - */ - public int modelToViewChildCount(TreePath parentPath, int count) { - return fTransform.modelToViewCount(parentPath, count); - } - - /** - * Notes that the child at the specified index of the given parent element - * has been filtered from the viewer. Returns whether the child at the given - * index was already filtered. - * - * @param parentPath - * path to parent element - * @param index - * index of child element to be filtered - * @param element - * the filtered element - * @return whether the child was already filtered - */ - protected boolean addFilteredIndex(TreePath parentPath, int index, Object element) { - return fTransform.addFilteredIndex(parentPath, index, element); - } - - /** - * Notes that the element at the given index has been removed from its - * parent and filtered indexes should be updated accordingly. - * - * @param parentPath - * path to parent element - * @param index - * index of element that was removed - */ - protected void removeElementFromFilters(TreePath parentPath, int index) { - fTransform.removeElementFromFilters(parentPath, index); - } - - /** - * Removes the given element from filtered elements of the given parent - * element. Return true if the element was removed, otherwise false. - * - * @param parentPath - * path to parent element - * @param element - * element to remove - * @return whether the element was removed - */ - protected boolean removeElementFromFilters(TreePath parentPath, Object element) { - return fTransform.removeElementFromFilters(parentPath, element); - } - - /** - * The child count for a parent has been computed. Ensure any filtered items - * above the given count are cleared. - * - * @param parentPath - * path to parent element - * @param childCount - * number of children - */ - protected void setModelChildCount(TreePath parentPath, int childCount) { - fTransform.setModelChildCount(parentPath, childCount); - } - - /** - * Returns whether the given element is filtered. - * - * @param parentElementOrTreePath - * the parent element or path - * @param element - * the child element - * @return whether to filter the element - */ - public boolean shouldFilter(Object parentElementOrTreePath, Object element) { - ViewerFilter[] filters = fViewer.getFilters(); - if (filters.length > 0) { - for (int j = 0; j < filters.length; j++) { - if (!(filters[j].select((Viewer) fViewer, parentElementOrTreePath, element))) { - return true; - } - } - } - return false; - } - - /** - * Returns whether the given index of the specified parent was previously - * filtered. - * - * @param parentPath the parent path - * @param index the index of parent path - * @return whether the element at the given index was filtered - */ - protected boolean isFiltered(TreePath parentPath, int index) { - return fTransform.isFiltered(parentPath, index); - } - - /** - * Notification the given element is being unmapped. - * - * @param path the path to remove from the {@link FilterTransform} and cancel updates from - */ - public void unmapPath(TreePath path) { - // System.out.println("Unmap " + path.getLastSegment()); - fTransform.clear(path); - cancelSubtreeUpdates(path); - } - - /** - * Returns filtered children or <code>null</code> - * - * @param parent the parent path to get children for - * @return filtered children or <code>null</code> - */ - protected int[] getFilteredChildren(TreePath parent) { - return fTransform.getFilteredChildren(parent); - } - - protected void clearFilteredChild(TreePath parent, int modelIndex) { - fTransform.clear(parent, modelIndex); - } - - protected void clearFilters(TreePath parent) { - fTransform.clear(parent); - } - - protected synchronized void checkIfRestoreComplete() { - if (fPendingState == null) { - return; - } - - /** - * Used to determine when restoration delta has been processed - */ - class CheckState implements IModelDeltaVisitor { - private boolean complete = true; - - /* - * (non-Javadoc) - * - * @see - * org.eclipse.debug.internal.ui.viewers.provisional.IModelDeltaVisitor - * #visit(org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta, - * int) - */ - public boolean visit(IModelDelta delta, int depth) { - // Filster out the CONTENT flags from the delta flags, the content - // flag is only used as a marker indicating that all the sub-elements - // of a given delta have been retrieved. - int flags = (delta.getFlags() & ~IModelDelta.CONTENT); - - if (flags != IModelDelta.NO_CHANGE) { - IModelDelta parentDelta = delta.getParentDelta(); - // Remove the delta if : - // - The parent delta has no more flags on it (the content flag is removed as well), - // which means that parent element's children have been completely exposed. - // - There are no more pending updates for the element. - // - If element is a memento, there are no state requests pending. - if (parentDelta != null && parentDelta.getFlags() == IModelDelta.NO_CHANGE) { - TreePath deltaPath = getViewerTreePath(delta); - if ( !areElementUpdatesPending(deltaPath) && - (!(delta.getElement() instanceof IMemento) || !areMementoUpdatesPending(delta)) ) - { - removeDelta(delta); - return false; - } - } - - if (flags != IModelDelta.REVEAL || (delta.getElement() instanceof IMemento)) { - complete = false; - return false; - } - } - return true; - } - - public boolean isComplete() { - return complete; - } - - 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.containsUpdate(path)) { - 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(IModelDelta delta) { - for (Iterator itr = fCompareRequestsInProgress.keySet().iterator(); itr.hasNext();) { - CompareRequestKey key = (CompareRequestKey) itr.next(); - if (delta.getElement().equals(key.fDelta.getElement())) { - return true; - } - } - return false; - } - - private void removeDelta(IModelDelta delta) { - if (DEBUG_STATE_SAVE_RESTORE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("\tRESTORE 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; - } - }); - - } - } - - CheckState state = new CheckState(); - fPendingState.accept(state); - if (state.isComplete()) { - // notify restore complete if REVEAL was restored also, otherwise - // postpone until then. - if (fPendingSetTopItem == null) { - if (DEBUG_STATE_SAVE_RESTORE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("STATE RESTORE COMPELTE: " + fPendingState); //$NON-NLS-1$ - } - - notifyStateUpdate(fPendingState.getElement(), STATE_RESTORE_SEQUENCE_COMPLETE, null); - } - - fPendingState = null; - } - } - - public void addViewerUpdateListener(IViewerUpdateListener listener) { - fUpdateListeners.add(listener); - } - - public void removeViewerUpdateListener(IViewerUpdateListener listener) { - fUpdateListeners.remove(listener); - } - - /** - * Notification an update request has started - * - * @param update the update to notify about - */ - void updateStarted(ViewerUpdateMonitor update) { - boolean begin = false; - synchronized (fRequestsInProgress) { - begin = fRequestsInProgress.isEmpty(); - List requests = (List) fRequestsInProgress.get(update.getSchedulingPath()); - if (requests == null) { - requests = new ArrayList(); - fRequestsInProgress.put(update.getSchedulingPath(), requests); - } - requests.add(update); - } - if (begin) { - if (DEBUG_UPDATE_SEQUENCE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("MODEL SEQUENCE BEGINS"); //$NON-NLS-1$ - } - notifyUpdate(UPDATE_SEQUENCE_BEGINS, null); - } - if (DEBUG_UPDATE_SEQUENCE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("\tBEGIN - " + update); //$NON-NLS-1$ - } - notifyUpdate(UPDATE_BEGINS, update); - } - - /** - * Notification an update request has completed - * - * @param update the update to notify - */ - void updateComplete(final ViewerUpdateMonitor update) { - notifyUpdate(UPDATE_COMPLETE, update); - if (DEBUG_UPDATE_SEQUENCE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("\tEND - " + update); //$NON-NLS-1$ - } - - new UIJob("Update complete") { //$NON-NLS-1$ - { setSystem(true); } - - public IStatus runInUIThread(IProgressMonitor monitor) { - boolean end = false; - synchronized (fRequestsInProgress) { - List requests = (List) fRequestsInProgress.get(update.getSchedulingPath()); - if (requests != null) { - requests.remove(update); - trigger(update); - if (requests.isEmpty()) { - fRequestsInProgress.remove(update.getSchedulingPath()); - } - } - end = fRequestsInProgress.isEmpty(); - } - if (end) { - if (DEBUG_UPDATE_SEQUENCE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("MODEL SEQUENCE ENDS"); //$NON-NLS-1$ - } - notifyUpdate(UPDATE_SEQUENCE_COMPLETE, null); - } - return Status.OK_STATUS; - } - }.schedule(); - } - - protected void notifyUpdate(final int type, final IViewerUpdate update) { - if (!fUpdateListeners.isEmpty()) { - Object[] listeners = fUpdateListeners.getListeners(); - for (int i = 0; i < listeners.length; i++) { - final IViewerUpdateListener listener = (IViewerUpdateListener) listeners[i]; - SafeRunner.run(new ISafeRunnable() { - public void run() throws Exception { - switch (type) { - case UPDATE_SEQUENCE_BEGINS: - listener.viewerUpdatesBegin(); - break; - case UPDATE_SEQUENCE_COMPLETE: - listener.viewerUpdatesComplete(); - break; - case UPDATE_BEGINS: - listener.updateStarted(update); - break; - case UPDATE_COMPLETE: - listener.updateComplete(update); - break; - } - } - - public void handleException(Throwable exception) { - DebugUIPlugin.log(exception); - } - }); - } - } - } - - public void addStateUpdateListener(IStateUpdateListener listener) { - fStateUpdateListeners.add(listener); - } - - public void removeStateUpdateListener(IStateUpdateListener listener) { - fStateUpdateListeners.remove(listener); - } - - protected void notifyStateUpdate(final Object input, final int type, final IViewerUpdate update) { - if (!fStateUpdateListeners.isEmpty()) { - Object[] listeners = fStateUpdateListeners.getListeners(); - for (int i = 0; i < listeners.length; i++) { - final IStateUpdateListener listener = (IStateUpdateListener) listeners[i]; - SafeRunner.run(new ISafeRunnable() { - public void run() throws Exception { - switch (type) { - case STATE_SAVE_SEQUENCE_BEGINS: - listener.stateSaveUpdatesBegin(input); - break; - case STATE_SAVE_SEQUENCE_COMPLETE: - listener.stateSaveUpdatesComplete(input); - break; - case STATE_RESTORE_SEQUENCE_BEGINS: - listener.stateRestoreUpdatesBegin(input); - break; - case STATE_RESTORE_SEQUENCE_COMPLETE: - listener.stateRestoreUpdatesComplete(input); - break; - case UPDATE_BEGINS: - listener.stateUpdateStarted(input, update); - break; - case UPDATE_COMPLETE: - listener.stateUpdateComplete(input, update); - break; - } - } - - public void handleException(Throwable exception) { - DebugUIPlugin.log(exception); - } - }); - } - } - } - - protected void cancelSubtreeUpdates(TreePath path) { - synchronized (fRequestsInProgress) { - Iterator iterator = fRequestsInProgress.entrySet().iterator(); - while (iterator.hasNext()) { - Entry entry = (Entry) iterator.next(); - TreePath entryPath = (TreePath) entry.getKey(); - if (entryPath.startsWith(path, null)) { - List requests = (List) entry.getValue(); - Iterator reqIter = requests.iterator(); - while (reqIter.hasNext()) { - ((IRequest) reqIter.next()).cancel(); - } - } - } - List purge = new ArrayList(); - iterator = fWaitingRequests.keySet().iterator(); - while (iterator.hasNext()) { - TreePath entryPath = (TreePath) iterator.next(); - if (entryPath.startsWith(path, null)) { - purge.add(entryPath); - } - } - iterator = purge.iterator(); - while (iterator.hasNext()) { - fWaitingRequests.remove(iterator.next()); - } - } - for (Iterator itr = fCompareRequestsInProgress.keySet().iterator(); itr.hasNext();) { - CompareRequestKey key = (CompareRequestKey) itr.next(); - if (key.fPath.startsWith(path, null)) { - ElementCompareRequest compareRequest = (ElementCompareRequest) fCompareRequestsInProgress.get(key); - compareRequest.cancel(); - itr.remove(); - } - } - } - - /** - * Returns whether this given request should be run, or should wait for - * parent update to complete. - * - * @param update the update the schedule - */ - void schedule(ViewerUpdateMonitor update) { - synchronized (fRequestsInProgress) { - TreePath schedulingPath = update.getSchedulingPath(); - List requests = (List) fWaitingRequests.get(schedulingPath); - if (requests == null) { - // no waiting requests - TreePath parentPath = schedulingPath; - while (fRequestsInProgress.get(parentPath) == null) { - parentPath = parentPath.getParentPath(); - if (parentPath == null) { - // no running requests: start request - update.start(); - return; - } - } - // request running on parent, add to waiting list - requests = new ArrayList(); - requests.add(update); - fWaitingRequests.put(schedulingPath, requests); - } else { - // there are waiting requests: coalesce with existing request? - Iterator reqIter = requests.iterator(); - while (reqIter.hasNext()) { - ViewerUpdateMonitor waiting = (ViewerUpdateMonitor) reqIter.next(); - if (waiting.coalesce(update)) { - // coalesced with existing request, done - return; - } - } - // add to list of waiting requests - requests.add(update); - return; - } - } - } - - 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. - * - * TODO: should we cancel child updates if a request has been canceled? - * - * @param request the request that just completed - */ - void trigger(ViewerUpdateMonitor request) { - if (fWaitingRequests.isEmpty()) { - return; - } - TreePath schedulingPath = request.getSchedulingPath(); - List waiting = (List) fWaitingRequests.get(schedulingPath); - if (waiting == null) { - // no waiting, update the entry with the shortest path - int length = Integer.MAX_VALUE; - Iterator entries = fWaitingRequests.entrySet().iterator(); - Entry candidate = null; - while (entries.hasNext()) { - Entry entry = (Entry) entries.next(); - TreePath key = (TreePath) entry.getKey(); - if (key.getSegmentCount() < length) { - candidate = entry; - length = key.getSegmentCount(); - } - } - if (candidate != null) { - startHighestPriorityRequest((TreePath) candidate.getKey(), (List) candidate.getValue()); - } - } else { - // start the highest priority request - startHighestPriorityRequest(schedulingPath, waiting); - } - } - - /** - * @param key the {@link TreePath} - * @param waiting the list of waiting requests - */ - private void startHighestPriorityRequest(TreePath key, List waiting) { - int priority = 4; - ViewerUpdateMonitor next = null; - Iterator requests = waiting.iterator(); - while (requests.hasNext()) { - ViewerUpdateMonitor vu = (ViewerUpdateMonitor) requests.next(); - if (vu.getPriority() < priority) { - next = vu; - priority = next.getPriority(); - } - } - if (next != null) { - waiting.remove(next); - if (waiting.isEmpty()) { - fWaitingRequests.remove(key); - } - next.start(); - } - } - - /** - * Registers the given listener for model delta notification. - * - * @param listener - * model delta listener - */ - public void addModelChangedListener(IModelChangedListener listener) { - fModelListeners.add(listener); - } - - /** - * Unregisters the given listener from model delta notification. - * - * @param listener - * model delta listener - */ - public void removeModelChangedListener(IModelChangedListener listener) { - fModelListeners.remove(listener); - } - - /** - * Returns the element corresponding to the given tree path. - * - * @param path - * tree path - * @return model element - */ - protected Object getElement(TreePath path) { - if (path.getSegmentCount() > 0) { - return path.getLastSegment(); - } - return getViewer().getInput(); - } - - /** - * Reschedule any children updates in progress for the given parent that - * have a start index greater than the given index. An element has been - * removed at this index, invalidating updates in progress. - * - * @param parentPath - * view tree path to parent element - * @param modelIndex - * index at which an element was removed - */ - protected void rescheduleUpdates(TreePath parentPath, int modelIndex) { - synchronized (fRequestsInProgress) { - List requests = (List) fRequestsInProgress.get(parentPath); - List reCreate = null; - if (requests != null) { - Iterator iterator = requests.iterator(); - while (iterator.hasNext()) { - IViewerUpdate update = (IViewerUpdate) iterator.next(); - if (update instanceof IChildrenUpdate) { - IChildrenUpdate childrenUpdate = (IChildrenUpdate) update; - if (childrenUpdate.getOffset() > modelIndex) { - childrenUpdate.cancel(); - if (reCreate == null) { - reCreate = new ArrayList(); - } - reCreate.add(childrenUpdate); - if (DEBUG_CONTENT_PROVIDER && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("canceled update in progress handling REMOVE: " + childrenUpdate); //$NON-NLS-1$ - } - } - } - } - } - requests = (List) fWaitingRequests.get(parentPath); - if (requests != null) { - Iterator iterator = requests.iterator(); - while (iterator.hasNext()) { - IViewerUpdate update = (IViewerUpdate) iterator.next(); - if (update instanceof IChildrenUpdate) { - IChildrenUpdate childrenUpdate = (IChildrenUpdate) update; - if (childrenUpdate.getOffset() > modelIndex) { - ((ChildrenUpdate) childrenUpdate).setOffset(childrenUpdate.getOffset() - 1); - if (DEBUG_CONTENT_PROVIDER && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("modified waiting update handling REMOVE: " + childrenUpdate); //$NON-NLS-1$ - } - } - } - } - } - // re-schedule canceled updates at new position. - // have to do this last else the requests would be waiting and - // get modified. - if (reCreate != null) { - Iterator iterator = reCreate.iterator(); - while (iterator.hasNext()) { - IChildrenUpdate childrenUpdate = (IChildrenUpdate) iterator.next(); - int start = childrenUpdate.getOffset() - 1; - int end = start + childrenUpdate.getLength(); - for (int i = start; i < end; i++) { - ((TreeModelContentProvider) this).doUpdateElement(parentPath, i); - } - } - } - } - } -} 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 6d46f29a2..c9cebd5d3 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 @@ -53,7 +53,7 @@ public class SubTreeModelViewer extends TreeModelViewer { private DelegatingTreeModelViewer fDelegatingViewer; /** - * Returns the root element's model tree path. + * @return Returns the root element's model tree path. */ public TreePath getRootPath() { return fRootPath; @@ -80,7 +80,7 @@ public class SubTreeModelViewer extends TreeModelViewer { * full model paths that the providers expect. */ public class DelegatingTreeModelViewer extends Viewer - implements ITreeModelLabelProviderTarget, ITreeModelContentProviderTarget + implements IInternalTreeModelViewer { public void reveal(TreePath path, int index) { if (path.startsWith(fRootPath, null)) { @@ -338,10 +338,19 @@ public class SubTreeModelViewer extends TreeModelViewer { SubTreeModelViewer.this.updateViewer(delta); } + public ViewerFilter[] getFilters() { return SubTreeModelViewer.this.getFilters(); } + public void addFilter(ViewerFilter filter) { + SubTreeModelViewer.this.addFilter(filter); + } + + public void setFilters(ViewerFilter[] filters) { + SubTreeModelViewer.this.setFilters(filters); + } + public boolean overrideSelection(ISelection current, ISelection candidate) { return SubTreeModelViewer.this.overrideSelection(current, candidate); } @@ -357,6 +366,27 @@ public class SubTreeModelViewer extends TreeModelViewer { public void clearSelectionQuiet() { SubTreeModelViewer.this.clearSelectionQuiet(); } + + public TreePath[] getElementPaths(Object element) { + TreePath[] subViewerPaths = SubTreeModelViewer.this.getElementPaths(element); + TreePath[] retVal = new TreePath[subViewerPaths.length]; + for (int i = 0; i < subViewerPaths.length; i++) { + retVal[i] = createFullPath(subViewerPaths[i]); + } + return retVal; + } + + public boolean getElementChecked(TreePath path) { + return SubTreeModelViewer.this.getElementChecked(createSubPath(path)); + } + + public boolean getElementGrayed(TreePath path) { + return SubTreeModelViewer.this.getElementGrayed(createSubPath(path)); + } + + public void setElementChecked(TreePath path, boolean checked, boolean grayed) { + SubTreeModelViewer.this.setElementChecked(createSubPath(path), checked, grayed); + } } @@ -396,6 +426,10 @@ public class SubTreeModelViewer extends TreeModelViewer { fBaseProvider.addModelChangedListener(listener); } + public void preserveState(TreePath subPath) { + fBaseProvider.preserveState(createFullPath(subPath)); + } + public void addStateUpdateListener(IStateUpdateListener listener) { fBaseProvider.addStateUpdateListener(listener); } @@ -432,6 +466,10 @@ public class SubTreeModelViewer extends TreeModelViewer { fBaseProvider.setModelDeltaMask(mask); } + public boolean areTreeModelViewerFiltersApplicable(Object parentElement) { + return fBaseProvider.areTreeModelViewerFiltersApplicable(parentElement); + } + public boolean shouldFilter(Object parentElementOrTreePath, Object element) { if (parentElementOrTreePath instanceof TreePath) { TreePath path = (TreePath)parentElementOrTreePath; @@ -467,9 +505,15 @@ public class SubTreeModelViewer extends TreeModelViewer { fBaseProvider.inputChanged(fDelegatingViewer, oldInput, newInput); } - public void inputAboutToChange(ITreeModelContentProviderTarget viewer, Object oldInput, Object newInput) { - fBaseProvider.inputAboutToChange(viewer, oldInput, newInput); + public void postInputChanged(IInternalTreeModelViewer viewer, + Object oldInput, Object newInput) { + fBaseProvider.postInputChanged(viewer, oldInput, newInput); + } + + public boolean setChecked(TreePath path, boolean checked) { + return fBaseProvider.setChecked(createFullPath(path), checked); } + } /** @@ -482,7 +526,7 @@ public class SubTreeModelViewer extends TreeModelViewer { private TreeModelLabelProvider fBaseProvider; - public SubTreeModelLabelProvider(ITreeModelLabelProviderTarget viewer) { + public SubTreeModelLabelProvider(IInternalTreeModelViewer viewer) { fBaseProvider = new TreeModelLabelProvider(viewer); } @@ -529,6 +573,10 @@ public class SubTreeModelViewer extends TreeModelViewer { } private TreePath createFullPath(TreePath subPath) { + if (fRootPath == null) { + return TreePath.EMPTY; + } + Object[] segments = new Object[fRootPath.getSegmentCount() + subPath.getSegmentCount()]; for (int i = 0; i < fRootPath.getSegmentCount(); i++) { segments[i] = fRootPath.getSegment(i); @@ -540,6 +588,10 @@ public class SubTreeModelViewer extends TreeModelViewer { } private TreePath createSubPath(TreePath fullPath) { + if (fRootPath == null) { + return TreePath.EMPTY; + } + if (fullPath.getSegmentCount() <= fRootPath.getSegmentCount()) { return TreePath.EMPTY; } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/TreeCursor.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/TreeCursor.java deleted file mode 100644 index 2b420b284..000000000 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/TreeCursor.java +++ /dev/null @@ -1,724 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2000, 2010 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * IBM Corporation - initial API and implementation - *******************************************************************************/ -package org.eclipse.debug.internal.ui.viewers.model; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.SWTException; -import org.eclipse.swt.events.SelectionListener; -import org.eclipse.swt.graphics.Color; -import org.eclipse.swt.graphics.GC; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.widgets.Canvas; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Event; -import org.eclipse.swt.widgets.Listener; -import org.eclipse.swt.widgets.ScrollBar; -import org.eclipse.swt.widgets.Tree; -import org.eclipse.swt.widgets.TreeColumn; -import org.eclipse.swt.widgets.TreeItem; -import org.eclipse.swt.widgets.TypedListener; -import org.eclipse.swt.widgets.Widget; - -import org.eclipse.debug.internal.core.IInternalDebugCoreConstants; - -/** - * A TreeCursor provides a way for the user to navigate around a Tree with columns using the - * keyboard. It also provides a mechanism for selecting an individual cell in a tree. - * - * <p> - * Here is an example of using a TreeCursor to navigate to a cell and then edit it. - * - * <code><pre> - * public static void main(String[] args) { - * Display display = new Display(); - * Shell shell = new Shell(display); - * shell.setLayout(new GridLayout()); - * - * // create a a tree with 3 columns and fill with data - * final Tree tree = new Tree(shell, SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION); - * tree.setLayoutData(new GridData(GridData.FILL_BOTH)); - * TreeColumn column1 = new TreeColumn(tree, SWT.NONE); - * TreeColumn column2 = new TreeColumn(tree, SWT.NONE); - * TreeColumn column3 = new TreeColumn(tree, SWT.NONE); - * for (int i = 0; i < 100; i++) { - * TreeItem item = new TreeItem(tree, SWT.NONE); - * item.setText(new String[] { "cell "+i+" 0", "cell "+i+" 1", "cell "+i+" 2"}); - * } - * column1.pack(); - * column2.pack(); - * column3.pack(); - * - * // create a TreeCursor to navigate around the tree - * final TreeCursor cursor = new TreeCursor(tree, SWT.NONE); - * // create an editor to edit the cell when the user hits "ENTER" - * // while over a cell in the tree - * final ControlEditor editor = new ControlEditor(cursor); - * editor.grabHorizontal = true; - * editor.grabVertical = true; - * - * cursor.addSelectionListener(new SelectionAdapter() { - * // when the TreeEditor is over a cell, select the corresponding row in - * // the tree - * public void widgetSelected(SelectionEvent e) { - * tree.setSelection(new TreeItem[] {cursor.getRow()}); - * } - * // when the user hits "ENTER" in the TreeCursor, pop up a text editor so that - * // they can change the text of the cell - * public void widgetDefaultSelected(SelectionEvent e){ - * final Text text = new Text(cursor, SWT.NONE); - * TreeItem row = cursor.getRow(); - * int column = cursor.getColumn(); - * text.setText(row.getText(column)); - * text.addKeyListener(new KeyAdapter() { - * public void keyPressed(KeyEvent e) { - * // close the text editor and copy the data over - * // when the user hits "ENTER" - * if (e.character == SWT.CR) { - * TreeItem row = cursor.getRow(); - * int column = cursor.getColumn(); - * row.setText(column, text.getText()); - * text.dispose(); - * } - * // close the text editor when the user hits "ESC" - * if (e.character == SWT.ESC) { - * text.dispose(); - * } - * } - * }); - * editor.setEditor(text); - * text.setFocus(); - * } - * }); - * // Hide the TreeCursor when the user hits the "MOD1" or "MOD2" key. - * // This alows the user to select multiple items in the tree. - * cursor.addKeyListener(new KeyAdapter() { - * public void keyPressed(KeyEvent e) { - * if (e.keyCode == SWT.MOD1 || - * e.keyCode == SWT.MOD2 || - * (e.stateMask & SWT.MOD1) != 0 || - * (e.stateMask & SWT.MOD2) != 0) { - * cursor.setVisible(false); - * } - * } - * }); - * // Show the TreeCursor when the user releases the "MOD2" or "MOD1" key. - * // This signals the end of the multiple selection task. - * tree.addKeyListener(new KeyAdapter() { - * public void keyReleased(KeyEvent e) { - * if (e.keyCode == SWT.MOD1 && (e.stateMask & SWT.MOD2) != 0) return; - * if (e.keyCode == SWT.MOD2 && (e.stateMask & SWT.MOD1) != 0) return; - * if (e.keyCode != SWT.MOD1 && (e.stateMask & SWT.MOD1) != 0) return; - * if (e.keyCode != SWT.MOD2 && (e.stateMask & SWT.MOD2) != 0) return; - * - * TreeItem[] selection = tree.getSelection(); - * TreeItem row = (selection.length == 0) ? tree.getItem(tree.indexOf(tree.getTopItem())) : selection[0]; - * tree.showItem(row); - * cursor.setSelection(row, 0); - * cursor.setVisible(true); - * cursor.setFocus(); - * } - * }); - * - * shell.open(); - * while (!shell.isDisposed()) { - * if (!display.readAndDispatch()) - * display.sleep(); - * } - * display.dispose(); - * } - * </pre></code> - * - * <dl> - * <dt><b>Styles:</b></dt> - * <dd>BORDER</dd> - * <dt><b>Events:</b></dt> - * <dd>Selection, DefaultSelection</dd> - * </dl> - * - * @since 3.3 - * - */ -public class TreeCursor extends Canvas { - Tree tree; - - TreeItem row= null; - - TreeColumn column= null; - - Listener treeListener, resizeListener, disposeItemListener, disposeColumnListener; - - // By default, invert the list selection colors - static final int BACKGROUND= SWT.COLOR_LIST_SELECTION_TEXT; - - static final int FOREGROUND= SWT.COLOR_LIST_SELECTION; - - /** - * Constructs a new instance of this class given its parent table and a style value describing - * its behavior and appearance. - * <p> - * The style value is either one of the style constants defined in class <code>SWT</code> which - * is applicable to instances of this class, or must be built by <em>bitwise OR</em>'ing - * together (that is, using the <code>int</code> "|" operator) two or more of those - * <code>SWT</code> style constants. The class description lists the style constants that are - * applicable to the class. Style bits are also inherited from superclasses. - * </p> - * - * @param parent a Tree control which will be the parent of the new instance (cannot be null) - * @param style the style of control to construct - * - * @exception IllegalArgumentException <ul> - * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> - * </ul> - * @exception SWTException <ul> - * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created - * the parent</li> - * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> - * </ul> - * - * @see SWT#BORDER - * @see Widget#checkSubclass() - * @see Widget#getStyle() - */ - public TreeCursor(Tree parent, int style) { - super(parent, style); - tree= parent; - setBackground(null); - setForeground(null); - - Listener listener= new Listener() { - public void handleEvent(Event event) { - switch (event.type) { - case SWT.Dispose: - treeCursorDispose(event); - break; - case SWT.FocusIn: - case SWT.FocusOut: - redraw(); - break; - case SWT.KeyDown: - treeCursorKeyDown(event); - break; - case SWT.Paint: - treeCursorPaint(event); - break; - case SWT.Traverse: - treeCursorTraverse(event); - break; - } - } - }; - int[] events= new int[] { SWT.Dispose, SWT.FocusIn, SWT.FocusOut, SWT.KeyDown, SWT.Paint, SWT.Traverse }; - for (int i= 0; i < events.length; i++) { - addListener(events[i], listener); - } - - treeListener= new Listener() { - public void handleEvent(Event event) { - switch (event.type) { - case SWT.MouseDown: - tableMouseDown(event); - break; - case SWT.FocusIn: - tableFocusIn(event); - break; - } - } - }; - tree.addListener(SWT.FocusIn, treeListener); - tree.addListener(SWT.MouseDown, treeListener); - - disposeItemListener= new Listener() { - public void handleEvent(Event event) { - row= null; - column= null; - _resize(); - } - }; - disposeColumnListener= new Listener() { - public void handleEvent(Event event) { - row= null; - column= null; - _resize(); - } - }; - resizeListener= new Listener() { - public void handleEvent(Event event) { - _resize(); - } - }; - ScrollBar hBar= tree.getHorizontalBar(); - if (hBar != null) { - hBar.addListener(SWT.Selection, resizeListener); - } - ScrollBar vBar= tree.getVerticalBar(); - if (vBar != null) { - vBar.addListener(SWT.Selection, resizeListener); - } - } - - /** - * Adds the listener to the collection of listeners who will be notified when the receiver's - * selection changes, by sending it one of the messages defined in the - * <code>SelectionListener</code> interface. - * <p> - * When <code>widgetSelected</code> is called, the item field of the event object is valid. If - * the receiver has <code>SWT.CHECK</code> style set and the check selection changes, the event - * object detail field contains the value <code>SWT.CHECK</code>. - * <code>widgetDefaultSelected</code> is typically called when an item is double-clicked. - * </p> - * - * @param listener the listener which should be notified - * - * @exception IllegalArgumentException <ul> - * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> - * </ul> - * @exception SWTException <ul> - * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> - * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created - * the receiver</li> - * </ul> - * - * @see SelectionListener - * @see SelectionEvent - * @see #removeSelectionListener(SelectionListener) - * - */ - public void addSelectionListener(SelectionListener listener) { - checkWidget(); - if (listener == null) - SWT.error(SWT.ERROR_NULL_ARGUMENT); - TypedListener typedListener= new TypedListener(listener); - addListener(SWT.Selection, typedListener); - addListener(SWT.DefaultSelection, typedListener); - } - - /** - * @param event - */ - void treeCursorDispose(Event event) { - tree.removeListener(SWT.FocusIn, treeListener); - tree.removeListener(SWT.MouseDown, treeListener); - if (column != null) { - column.removeListener(SWT.Dispose, disposeColumnListener); - column.removeListener(SWT.Move, resizeListener); - column.removeListener(SWT.Resize, resizeListener); - column= null; - } - if (row != null) { - row.removeListener(SWT.Dispose, disposeItemListener); - row= null; - } - ScrollBar hBar= tree.getHorizontalBar(); - if (hBar != null) { - hBar.removeListener(SWT.Selection, resizeListener); - } - ScrollBar vBar= tree.getVerticalBar(); - if (vBar != null) { - vBar.removeListener(SWT.Selection, resizeListener); - } - } - - void treeCursorKeyDown(Event event) { - if (row == null) - return; - switch (event.character) { - case SWT.CR: - notifyListeners(SWT.DefaultSelection, new Event()); - return; - } - int rowIndex= tree.indexOf(row); - int columnIndex= column == null ? 0 : tree.indexOf(column); - switch (event.keyCode) { - case SWT.ARROW_UP: - setRowColumn(Math.max(0, rowIndex - 1), columnIndex, true); - break; - case SWT.ARROW_DOWN: - setRowColumn(Math.min(rowIndex + 1, tree.getItemCount() - 1), columnIndex, true); - break; - case SWT.ARROW_LEFT: - case SWT.ARROW_RIGHT: { - int columnCount= tree.getColumnCount(); - if (columnCount == 0) - break; - int[] order= tree.getColumnOrder(); - int index= 0; - while (index < order.length) { - if (order[index] == columnIndex) - break; - index++; - } - if (index == order.length) - index= 0; - int leadKey= (getStyle() & SWT.RIGHT_TO_LEFT) != 0 ? SWT.ARROW_RIGHT : SWT.ARROW_LEFT; - if (event.keyCode == leadKey) { - setRowColumn(rowIndex, order[Math.max(0, index - 1)], true); - } else { - setRowColumn(rowIndex, order[Math.min(columnCount - 1, index + 1)], true); - } - break; - } - case SWT.HOME: - setRowColumn(0, columnIndex, true); - break; - case SWT.END: { - int i= tree.getItemCount() - 1; - setRowColumn(i, columnIndex, true); - break; - } - case SWT.PAGE_UP: { - int index= tree.indexOf(tree.getTopItem()); - if (index == rowIndex) { - Rectangle rect= tree.getClientArea(); - TreeItem item= tree.getItem(index); - Rectangle itemRect= item.getBounds(0); - rect.height-= itemRect.y; - int height= tree.getItemHeight(); - int page= Math.max(1, rect.height / height); - index= Math.max(0, index - page + 1); - } - setRowColumn(index, columnIndex, true); - break; - } - case SWT.PAGE_DOWN: { - int index= tree.indexOf(tree.getTopItem()); - Rectangle rect= tree.getClientArea(); - TreeItem item= tree.getItem(index); - Rectangle itemRect= item.getBounds(0); - rect.height-= itemRect.y; - int height= tree.getItemHeight(); - int page= Math.max(1, rect.height / height); - int end= tree.getItemCount() - 1; - index= Math.min(end, index + page - 1); - if (index == rowIndex) { - index= Math.min(end, index + page - 1); - } - setRowColumn(index, columnIndex, true); - break; - } - } - } - - void treeCursorPaint(Event event) { - if (row == null) - return; - int columnIndex= column == null ? 0 : tree.indexOf(column); - GC gc= event.gc; - Display display= getDisplay(); - gc.setBackground(getBackground()); - gc.setForeground(getForeground()); - gc.fillRectangle(event.x, event.y, event.width, event.height); - int x= 0; - Point size= getSize(); - Image image= row.getImage(columnIndex); - if (image != null) { - Rectangle imageSize= image.getBounds(); - int imageY= (size.y - imageSize.height) / 2; - gc.drawImage(image, x, imageY); - x+= imageSize.width; - } - String text= row.getText(columnIndex); - if (text != IInternalDebugCoreConstants.EMPTY_STRING) { - Rectangle bounds= row.getBounds(columnIndex); - Point extent= gc.stringExtent(text); - // Temporary code - need a better way to determine table trim - String platform= SWT.getPlatform(); - if ("win32".equals(platform)) { //$NON-NLS-1$ - if (tree.getColumnCount() == 0 || columnIndex == 0) { - x+= 2; - } else { - int alignmnent= column.getAlignment(); - switch (alignmnent) { - case SWT.LEFT: - x+= 6; - break; - case SWT.RIGHT: - x= bounds.width - extent.x - 6; - break; - case SWT.CENTER: - x+= (bounds.width - x - extent.x) / 2; - break; - } - } - } else { - if (tree.getColumnCount() == 0) { - x+= 5; - } else { - int alignmnent= column.getAlignment(); - switch (alignmnent) { - case SWT.LEFT: - x+= 5; - break; - case SWT.RIGHT: - x= bounds.width - extent.x - 2; - break; - case SWT.CENTER: - x+= (bounds.width - x - extent.x) / 2 + 2; - break; - } - } - } - int textY= (size.y - extent.y) / 2; - gc.drawString(text, x, textY); - } - if (isFocusControl()) { - gc.setBackground(display.getSystemColor(SWT.COLOR_BLACK)); - gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE)); - gc.drawFocus(0, 0, size.x, size.y); - } - } - - /** - * @param event - */ - void tableFocusIn(Event event) { - if (isDisposed()) - return; - if (isVisible()) - setFocus(); - } - - void tableMouseDown(Event event) { - if (isDisposed() || !isVisible()) - return; - Point pt= new Point(event.x, event.y); - int lineWidth= tree.getLinesVisible() ? tree.getGridLineWidth() : 0; - TreeItem item= tree.getItem(pt); - if ((tree.getStyle() & SWT.FULL_SELECTION) != 0) { - if (item == null) - return; - } else { - int start= item != null ? tree.indexOf(item) : tree.indexOf(tree.getTopItem()); - int end= tree.getItemCount(); - Rectangle clientRect= tree.getClientArea(); - for (int i= start; i < end; i++) { - TreeItem nextItem= tree.getItem(i); - Rectangle rect= nextItem.getBounds(0); - if (pt.y >= rect.y && pt.y < rect.y + rect.height + lineWidth) { - item= nextItem; - break; - } - if (rect.y > clientRect.y + clientRect.height) - return; - } - if (item == null) - return; - } - TreeColumn newColumn= null; - int columnCount= tree.getColumnCount(); - if (columnCount > 0) { - for (int i= 0; i < columnCount; i++) { - Rectangle rect= item.getBounds(i); - rect.width+= lineWidth; - rect.height+= lineWidth; - if (rect.contains(pt)) { - newColumn= tree.getColumn(i); - break; - } - } - if (newColumn == null) { - newColumn= tree.getColumn(0); - } - } - setRowColumn(item, newColumn, true); - setFocus(); - return; - } - - void treeCursorTraverse(Event event) { - switch (event.detail) { - case SWT.TRAVERSE_ARROW_NEXT: - case SWT.TRAVERSE_ARROW_PREVIOUS: - case SWT.TRAVERSE_RETURN: - event.doit= false; - return; - } - event.doit= true; - } - - void setRowColumn(int row, int column, boolean notify) { - TreeItem item= row == -1 ? null : tree.getItem(row); - TreeColumn col= column == -1 || tree.getColumnCount() == 0 ? null : tree.getColumn(column); - setRowColumn(item, col, notify); - } - - void setRowColumn(TreeItem row, TreeColumn column, boolean notify) { - if (this.row == row && this.column == column) { - return; - } - if (this.row != null && this.row != row) { - this.row.removeListener(SWT.Dispose, disposeItemListener); - this.row= null; - } - if (this.column != null && this.column != column) { - this.column.removeListener(SWT.Dispose, disposeColumnListener); - this.column.removeListener(SWT.Move, resizeListener); - this.column.removeListener(SWT.Resize, resizeListener); - this.column= null; - } - if (row != null) { - if (this.row != row) { - this.row= row; - row.addListener(SWT.Dispose, disposeItemListener); - tree.showItem(row); - } - if (this.column != column && column != null) { - this.column= column; - column.addListener(SWT.Dispose, disposeColumnListener); - column.addListener(SWT.Move, resizeListener); - column.addListener(SWT.Resize, resizeListener); - tree.showColumn(column); - } - int columnIndex= column == null ? 0 : tree.indexOf(column); - setBounds(row.getBounds(columnIndex)); - redraw(); - if (notify) { - notifyListeners(SWT.Selection, new Event()); - } - } - } - - public void setVisible(boolean visible) { - checkWidget(); - if (visible) - _resize(); - super.setVisible(visible); - } - - /** - * Removes the listener from the collection of listeners who will be notified when the - * receiver's selection changes. - * - * @param listener the listener which should no longer be notified - * - * @exception IllegalArgumentException <ul> - * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> - * </ul> - * @exception SWTException <ul> - * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> - * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created - * the receiver</li> - * </ul> - * - * @see SelectionListener - * @see #addSelectionListener(SelectionListener) - * - * @since 3.0 - */ - public void removeSelectionListener(SelectionListener listener) { - checkWidget(); - if (listener == null) { - SWT.error(SWT.ERROR_NULL_ARGUMENT); - } - removeListener(SWT.Selection, listener); - removeListener(SWT.DefaultSelection, listener); - } - - void _resize() { - if (row == null) { - setBounds(-200, -200, 0, 0); - } else { - int columnIndex= column == null ? 0 : tree.indexOf(column); - setBounds(row.getBounds(columnIndex)); - } - } - - /** - * Returns the column over which the TreeCursor is positioned. - * - * @return the column for the current position - * - * @exception SWTException <ul> - * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> - * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created - * the receiver</li> - * </ul> - */ - public int getColumn() { - checkWidget(); - return column == null ? 0 : tree.indexOf(column); - } - - /** - * Returns the row over which the TreeCursor is positioned. - * - * @return the item for the current position - * - * @exception SWTException <ul> - * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> - * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created - * the receiver</li> - * </ul> - */ - public TreeItem getRow() { - checkWidget(); - return row; - } - - public void setBackground(Color color) { - if (color == null) - color= getDisplay().getSystemColor(BACKGROUND); - super.setBackground(color); - redraw(); - } - - public void setForeground(Color color) { - if (color == null) - color= getDisplay().getSystemColor(FOREGROUND); - super.setForeground(color); - redraw(); - } - - /** - * Positions the TreeCursor over the cell at the given row and column in the parent table. - * - * @param row the index of the row for the cell to select - * @param column the index of column for the cell to select - * - * @exception SWTException <ul> - * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> - * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created - * the receiver</li> - * </ul> - * - */ - public void setSelection(int row, int column) { - checkWidget(); - int columnCount= tree.getColumnCount(); - int maxColumnIndex= columnCount == 0 ? 0 : columnCount - 1; - if (row < 0 || row >= tree.getItemCount() || column < 0 || column > maxColumnIndex) - SWT.error(SWT.ERROR_INVALID_ARGUMENT); - setRowColumn(row, column, false); - } - - /** - * Positions the TreeCursor over the cell at the given row and column in the parent table. - * - * @param row the TreeItem of the row for the cell to select - * @param column the index of column for the cell to select - * - * @exception SWTException <ul> - * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> - * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created - * the receiver</li> - * </ul> - * - */ - public void setSelection(TreeItem row, int column) { - checkWidget(); - int columnCount= tree.getColumnCount(); - int maxColumnIndex= columnCount == 0 ? 0 : columnCount - 1; - if (row == null || row.isDisposed() || column < 0 || column > maxColumnIndex) - SWT.error(SWT.ERROR_INVALID_ARGUMENT); - setRowColumn(tree.indexOf(row), column, false); - } -} 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 aa1e21dac..18b37ec53 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 @@ -13,17 +13,46 @@ *******************************************************************************/ package org.eclipse.debug.internal.ui.viewers.model; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.ISafeRunnable; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.ListenerList; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.SafeRunner; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.debug.core.IRequest; +import org.eclipse.debug.internal.ui.DebugUIPlugin; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ICheckboxModelProxy; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelChangedListener; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDeltaVisitor; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy2; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxyFactory; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxyFactory2; import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IStateUpdateListener; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer; import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; -import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdateListener; +import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewerFilter; +import org.eclipse.jface.viewers.IContentProvider; import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.ITreeSelection; import org.eclipse.jface.viewers.TreePath; import org.eclipse.jface.viewers.TreeSelection; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.swt.widgets.Display; /** @@ -31,54 +60,1072 @@ import org.eclipse.swt.widgets.Display; * * @since 3.3 */ -public class TreeModelContentProvider extends ModelContentProvider implements ITreeModelContentProvider { - - /** - * Re-filters any filtered children of the given parent element. - * - * @param path parent element - */ - protected void refilterChildren(TreePath path) { - if (getViewer() != null) { - int[] filteredChildren = getFilteredChildren(path); - if (filteredChildren != null) { - for (int i = 0; i < filteredChildren.length; i++) { - doUpdateElement(path, filteredChildren[i]); - } - } - } - } - - protected synchronized void doUpdateChildCount(TreePath path) { +public class TreeModelContentProvider implements ITreeModelContentProvider, IContentProvider, IModelChangedListener { + + /** + * Tree model viewer that this content provider is used with. + */ + private IInternalTreeModelViewer fViewer; + + /** + * Mask used to filter delta updates coming from the model. + */ + private int fModelDeltaMask = ~0; + + /** + * Map tree paths to model proxy responsible for element + * + * Used to install different model proxy instances for one element depending + * on the tree path. + */ + private Map fTreeModelProxies = new HashMap(); // tree model proxy by + // element tree path + + /** + * Map element to model proxy responsible for it. + * + * Used to install a single model proxy which is responsible for all + * instances of an element in the model tree. + */ + private Map fModelProxies = new HashMap(); // model proxy by element + + /** + * Map of nodes that have been filtered from the viewer. + */ + private FilterTransform fTransform = new FilterTransform(); + + /** + * Model listeners + */ + private ListenerList fModelListeners = new ListenerList(); + + /** + * Viewer update listeners + */ + private ListenerList fUpdateListeners = new ListenerList(); + + /** + * Map of updates in progress: element path -> list of requests + */ + private Map fRequestsInProgress = new HashMap(); + + /** + * Map of dependent requests waiting for parent requests to complete: + * element path -> list of requests + */ + private Map fWaitingRequests = new HashMap(); + + private List fCompletedUpdates = new ArrayList(); + + private Runnable fCompletedUpdatesJob; + + private ViewerStateTracker fStateTracker = new ViewerStateTracker(this); + + /** + * Update type constants + */ + static final int UPDATE_SEQUENCE_BEGINS = 0; + + static final int UPDATE_SEQUENCE_COMPLETE = 1; + + static final int UPDATE_BEGINS = 2; + + static final int UPDATE_COMPLETE = 3; + + + /** + * Constant for an empty tree path. + */ + static final TreePath EMPTY_TREE_PATH = new TreePath(new Object[] {}); + + // debug flags + public static String DEBUG_PRESENTATION_ID = null; + public static boolean DEBUG_CONTENT_PROVIDER = false; + public static boolean DEBUG_UPDATE_SEQUENCE = false; + public static boolean DEBUG_DELTAS = false; + public static boolean DEBUG_TEST_PRESENTATION_ID(IPresentationContext context) { + if (context == null) { + return true; + } + return DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(context.getId()); + } + + static { + DEBUG_PRESENTATION_ID = Platform.getDebugOption("org.eclipse.debug.ui/debug/viewers/presentationId"); //$NON-NLS-1$ + if (!DebugUIPlugin.DEBUG || "".equals(DEBUG_PRESENTATION_ID)) { //$NON-NLS-1$ + DEBUG_PRESENTATION_ID = null; + } + DEBUG_CONTENT_PROVIDER = DebugUIPlugin.DEBUG && "true".equals( //$NON-NLS-1$ + Platform.getDebugOption("org.eclipse.debug.ui/debug/viewers/contentProvider")); //$NON-NLS-1$ + DEBUG_UPDATE_SEQUENCE = DebugUIPlugin.DEBUG && "true".equals( //$NON-NLS-1$ + Platform.getDebugOption("org.eclipse.debug.ui/debug/viewers/updateSequence")); //$NON-NLS-1$ + ViewerStateTracker.DEBUG_STATE_SAVE_RESTORE = DebugUIPlugin.DEBUG && "true".equals( //$NON-NLS-1$ + Platform.getDebugOption("org.eclipse.debug.ui/debug/viewers/stateSaveRestore")); //$NON-NLS-1$ + DEBUG_DELTAS = DebugUIPlugin.DEBUG && "true".equals( //$NON-NLS-1$ + Platform.getDebugOption("org.eclipse.debug.ui/debug/viewers/deltas")); //$NON-NLS-1$ + } + + public void dispose() { + Assert.isTrue( getViewer().getDisplay().getThread() == Thread.currentThread() ); + + // cancel pending updates + Iterator iterator = fRequestsInProgress.values().iterator(); + while (iterator.hasNext()) { + List requests = (List) iterator.next(); + Iterator reqIter = requests.iterator(); + while (reqIter.hasNext()) { + ((IRequest) reqIter.next()).cancel(); + } + } + fWaitingRequests.clear(); + + fStateTracker.dispose(); + fModelListeners.clear(); + fUpdateListeners.clear(); + disposeAllModelProxies(); + + synchronized(this) { + fViewer = null; + } + } + + /** + * @return Returns whether the content provider is disposed. + */ + boolean isDisposed() { + synchronized(this) { + return fViewer == null; + } + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + synchronized(this) { + fViewer = (IInternalTreeModelViewer) viewer; + } + + Assert.isTrue( fViewer.getDisplay().getThread() == Thread.currentThread() ); + + if (oldInput != null) { + fStateTracker.saveViewerState(oldInput); + } + } + + public void postInputChanged(IInternalTreeModelViewer viewer, Object oldInput, Object newInput) { + Assert.isTrue( getViewer().getDisplay().getThread() == Thread.currentThread() ); + + cancelSubtreeUpdates(TreePath.EMPTY); + disposeAllModelProxies(); + cancelSubtreeUpdates(TreePath.EMPTY); + fTransform.clear(); + if (newInput != null) { + installModelProxy(newInput, TreePath.EMPTY); + fStateTracker.restoreViewerState(newInput); + } + } + + public void addViewerUpdateListener(IViewerUpdateListener listener) { + fUpdateListeners.add(listener); + } + + public void removeViewerUpdateListener(IViewerUpdateListener listener) { + fUpdateListeners.remove(listener); + } + + public void addStateUpdateListener(IStateUpdateListener listener) { + fStateTracker.addStateUpdateListener(listener); + } + + public void preserveState(TreePath path) { + fStateTracker.appendToPendingStateDelta(path); + } + + public void removeStateUpdateListener(IStateUpdateListener listener) { + fStateTracker.removeStateUpdateListener(listener); + } + + public void addModelChangedListener(IModelChangedListener listener) { + fModelListeners.add(listener); + } + + public void removeModelChangedListener(IModelChangedListener listener) { + fModelListeners.remove(listener); + } + + public void cancelRestore(final TreePath path, final int flags) { + fStateTracker.cancelRestore(path, flags); + } + + public boolean setChecked(TreePath path, boolean checked) { + IModelProxy elementProxy = getElementProxy(path); + if (elementProxy instanceof ICheckboxModelProxy) { + return ((ICheckboxModelProxy) elementProxy).setChecked(getPresentationContext(), getViewer().getInput(), path, checked); + } + return false; + } + + /** + * Installs the model proxy for the given element into this content provider + * if not already installed. + * @param input the input to install the model proxy on + * @param path the {@link TreePath} to install the proxy for + */ + private void installModelProxy(Object input, TreePath path) { + + if (!fTreeModelProxies.containsKey(path) && !fModelProxies.containsKey(path.getLastSegment())) { + Object element = path.getSegmentCount() != 0 ? path.getLastSegment() : input; + IModelProxy proxy = null; + IModelProxyFactory2 modelProxyFactory2 = ViewerAdapterService.getModelProxyFactory2(element); + if (modelProxyFactory2 != null) { + proxy = modelProxyFactory2.createTreeModelProxy(input, path, getPresentationContext()); + if (proxy != null) { + fTreeModelProxies.put(path, proxy); + } + } + if (proxy == null) { + IModelProxyFactory modelProxyFactory = ViewerAdapterService.getModelProxyFactory(element); + if (modelProxyFactory != null) { + proxy = modelProxyFactory.createModelProxy(element, getPresentationContext()); + if (proxy != null) { + fModelProxies.put(element, proxy); + } + } + } + + if (proxy instanceof IModelProxy2) { + proxy.addModelChangedListener(this); + ((IModelProxy2)proxy).initialize(getViewer()); + } else if (proxy != null) { + final IModelProxy finalProxy = proxy; + Job job = new Job("Model Proxy installed notification job") {//$NON-NLS-1$ + protected IStatus run(IProgressMonitor monitor) { + if (!monitor.isCanceled()) { + IPresentationContext context = null; + Viewer viewer = null; + synchronized (TreeModelContentProvider.this) { + if (!isDisposed()) { + context = getPresentationContext(); + viewer = (Viewer) getViewer(); + } + } + if (viewer != null && context != null && !finalProxy.isDisposed()) { + finalProxy.init(context); + finalProxy.addModelChangedListener(TreeModelContentProvider.this); + finalProxy.installed(viewer); + } + } + return Status.OK_STATUS; + } + + public boolean shouldRun() { + return !isDisposed(); + } + }; + job.setSystem(true); + job.schedule(); + } + } + } + + /** + * Finds the model proxy that an element with a given path is associated with. + * @param path Path of the elemnt. + * @return Element's model proxy. + */ + private IModelProxy getElementProxy(TreePath path) { + while (path != null) { + IModelProxy proxy = (IModelProxy) fTreeModelProxies.get(path); + if (proxy != null) { + return proxy; + } + + Object element = path.getSegmentCount() == 0 ? getViewer().getInput() : path.getLastSegment(); + proxy = (IModelProxy) fModelProxies.get(element); + if (proxy != null) { + return proxy; + } + + path = path.getParentPath(); + } + return null; + } + + /** + * Uninstalls each model proxy + */ + private void disposeAllModelProxies() { + Assert.isTrue( getViewer().getDisplay().getThread() == Thread.currentThread() ); + + Iterator updatePolicies = fModelProxies.values().iterator(); + while (updatePolicies.hasNext()) { + IModelProxy proxy = (IModelProxy) updatePolicies.next(); + proxy.dispose(); + } + fModelProxies.clear(); + + updatePolicies = fTreeModelProxies.values().iterator(); + while (updatePolicies.hasNext()) { + IModelProxy proxy = (IModelProxy) updatePolicies.next(); + proxy.dispose(); + } + fTreeModelProxies.clear(); + } + + /** + * Uninstalls the model proxy installed for the given element, if any. + * @param path the {@link TreePath} to dispose the model proxy for + */ + private void disposeModelProxy(TreePath path) { + IModelProxy proxy = (IModelProxy) fTreeModelProxies.remove(path); + if (proxy != null) { + proxy.dispose(); + } + proxy = (IModelProxy) fModelProxies.remove(path.getLastSegment()); + if (proxy != null) { + proxy.dispose(); + } + } + + public void modelChanged(final IModelDelta delta, final IModelProxy proxy) { + Display display = null; + + // Check if the viewer is still available, i.e. if the content provider + // is not disposed. + synchronized(this) { + if (fViewer != null && !proxy.isDisposed()) { + display = fViewer.getDisplay(); + } + } + if (display != null) { + // If we're in display thread, process the delta immediately to + // avoid "skid" in processing events. + if (Thread.currentThread().equals(display.getThread())) { + doModelChanged(delta, proxy); + } + else { + fViewer.getDisplay().asyncExec(new Runnable() { + public void run() { + doModelChanged(delta, proxy); + } + }); + } + } + } + + /** + * Executes the mdoel proxy in UI thread. + * @param delta Delta to process + * @param proxy Proxy that fired the delta. + */ + private void doModelChanged(IModelDelta delta, IModelProxy proxy) { + if (!proxy.isDisposed()) { + if (DEBUG_DELTAS && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { + DebugUIPlugin.debug("RECEIVED DELTA: " + delta.toString()); //$NON-NLS-1$ + } + + updateModel(delta, getModelDeltaMask()); + + // Initiate model update sequence before notifying of the model changed. + trigger(null); + + // Call model listeners after updating the viewer model. + Object[] listeners = fModelListeners.getListeners(); + for (int i = 0; i < listeners.length; i++) { + ((IModelChangedListener) listeners[i]).modelChanged(delta, proxy); + } + } + } + + public void setModelDeltaMask(int mask) { + fModelDeltaMask = mask; + } + + public int getModelDeltaMask() { + return fModelDeltaMask; + } + + public void updateModel(IModelDelta delta, int mask) { + IModelDelta[] deltaArray = new IModelDelta[] { delta }; + updateNodes(deltaArray, mask & (IModelDelta.REMOVED | IModelDelta.UNINSTALL)); + updateNodes(deltaArray, mask & ITreeModelContentProvider.UPDATE_MODEL_DELTA_FLAGS + & ~(IModelDelta.REMOVED | IModelDelta.UNINSTALL)); + updateNodes(deltaArray, mask & ITreeModelContentProvider.CONTROL_MODEL_DELTA_FLAGS); + + fStateTracker.checkIfRestoreComplete(); + } + + /** + * Returns a tree path for the node including the root element. + * + * @param node + * model delta + * @return corresponding tree path + */ + TreePath getFullTreePath(IModelDelta node) { + ArrayList list = new ArrayList(); + while (node.getParentDelta() != null) { + list.add(0, node.getElement()); + node = node.getParentDelta(); + } + return new TreePath(list.toArray()); + } + + /** + * Returns a tree path for the node, *not* including the root element. + * + * @param node + * model delta + * @return corresponding tree path + */ + TreePath getViewerTreePath(IModelDelta node) { + ArrayList list = new ArrayList(); + IModelDelta parentDelta = node.getParentDelta(); + while (parentDelta != null) { + list.add(0, node.getElement()); + node = parentDelta; + parentDelta = node.getParentDelta(); + } + return new TreePath(list.toArray()); + } + + /** + * Returns the viewer this content provider is working for. + * + * @return viewer + */ + protected IInternalTreeModelViewer getViewer() { + synchronized(this) { + return fViewer; + } + } + + public int viewToModelIndex(TreePath parentPath, int index) { + return fTransform.viewToModelIndex(parentPath, index); + } + + public int viewToModelCount(TreePath parentPath, int count) { + return fTransform.viewToModelCount(parentPath, count); + } + + public int modelToViewIndex(TreePath parentPath, int index) { + return fTransform.modelToViewIndex(parentPath, index); + } + + public int modelToViewChildCount(TreePath parentPath, int count) { + return fTransform.modelToViewCount(parentPath, count); + } + + public boolean areTreeModelViewerFiltersApplicable(Object parentElement) { + ViewerFilter[] filters = fViewer.getFilters(); + if (filters.length > 0) { + for (int j = 0; j < filters.length; j++) { + if (filters[j] instanceof TreeModelViewerFilter && + ((TreeModelViewerFilter)filters[j]).isApplicable(fViewer, parentElement)) + { + return true; + } + } + } + return false; + } + + public boolean shouldFilter(Object parentElementOrTreePath, Object element) { + ViewerFilter[] filters = fViewer.getFilters(); + if (filters.length > 0) { + for (int j = 0; j < filters.length; j++) { + if (filters[j] instanceof TreeModelViewerFilter) { + // Skip the filter if not applicable to parent element + Object parentElement = parentElementOrTreePath instanceof TreePath + ? ((TreePath)parentElementOrTreePath).getLastSegment() : parentElementOrTreePath; + if (parentElement == null) parentElement = fViewer.getInput(); + if (!((TreeModelViewerFilter)filters[j]).isApplicable(fViewer, parentElement)) { + continue; + } + } + + if (!(filters[j].select((Viewer) fViewer, parentElementOrTreePath, element))) { + return true; + } + } + } + return false; + } + + public void unmapPath(TreePath path) { + Assert.isTrue( getViewer().getDisplay().getThread() == Thread.currentThread() ); + fTransform.clear(path); + cancelSubtreeUpdates(path); + } + + + boolean addFilteredIndex(TreePath parentPath, int index, Object element) { + Assert.isTrue( getViewer().getDisplay().getThread() == Thread.currentThread() ); + return fTransform.addFilteredIndex(parentPath, index, element); + } + + void removeElementFromFilters(TreePath parentPath, int index) { + Assert.isTrue( getViewer().getDisplay().getThread() == Thread.currentThread() ); + fTransform.removeElementFromFilters(parentPath, index); + } + + boolean removeElementFromFilters(TreePath parentPath, Object element) { + Assert.isTrue( getViewer().getDisplay().getThread() == Thread.currentThread() ); + return fTransform.removeElementFromFilters(parentPath, element); + } + + void setModelChildCount(TreePath parentPath, int childCount) { + Assert.isTrue( getViewer().getDisplay().getThread() == Thread.currentThread() ); + fTransform.setModelChildCount(parentPath, childCount); + } + + boolean isFiltered(TreePath parentPath, int index) { + Assert.isTrue( getViewer().getDisplay().getThread() == Thread.currentThread() ); + return fTransform.isFiltered(parentPath, index); + } + + int[] getFilteredChildren(TreePath parent) { + Assert.isTrue( getViewer().getDisplay().getThread() == Thread.currentThread() ); + return fTransform.getFilteredChildren(parent); + } + + void clearFilteredChild(TreePath parent, int modelIndex) { + Assert.isTrue( getViewer().getDisplay().getThread() == Thread.currentThread() ); + fTransform.clear(parent, modelIndex); + } + + void clearFilters(TreePath parent) { + Assert.isTrue( getViewer().getDisplay().getThread() == Thread.currentThread() ); + fTransform.clear(parent); + } + + /** + * Notification an update request has started + * + * @param update the update to notify about + */ + void updateStarted(ViewerUpdateMonitor update) { + Assert.isTrue( getViewer().getDisplay().getThread() == Thread.currentThread() ); + + boolean begin = fRequestsInProgress.isEmpty(); + List requests = (List) fRequestsInProgress.get(update.getSchedulingPath()); + if (requests == null) { + requests = new ArrayList(); + fRequestsInProgress.put(update.getSchedulingPath(), requests); + } + requests.add(update); + if (begin) { + if (DEBUG_UPDATE_SEQUENCE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { + System.out.println("MODEL SEQUENCE BEGINS"); //$NON-NLS-1$ + } + notifyUpdate(UPDATE_SEQUENCE_BEGINS, null); + } + if (DEBUG_UPDATE_SEQUENCE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { + System.out.println("\tBEGIN - " + update); //$NON-NLS-1$ + } + notifyUpdate(UPDATE_BEGINS, update); + } + + /** + * Notification an update request has completed + * + * @param updates the updates to notify + */ + void updatesComplete(final List updates) { + for (int i = 0; i < updates.size(); i++) { + ViewerUpdateMonitor update = (ViewerUpdateMonitor)updates.get(i); + notifyUpdate(UPDATE_COMPLETE, update); + if (DEBUG_UPDATE_SEQUENCE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { + System.out.println("\tEND - " + update); //$NON-NLS-1$ + } + } + + // Wait a single cycle to allow viewer to queue requests triggered by completed updates. + getViewer().getDisplay().asyncExec(new Runnable() { + public void run() { + if (isDisposed()) return; + + for (int i = 0; i < updates.size(); i++) { + ViewerUpdateMonitor update = (ViewerUpdateMonitor)updates.get(i); + + // Search for update in list using identity test. Otherwise a completed canceled + // update may trigger removal of up-to-date running update on the same element. + List requests = (List) fRequestsInProgress.get(update.getSchedulingPath()); + boolean found = false; + if (requests != null) { + for (int j = 0; j < requests.size(); j++) { + if (requests.get(j) == update) { + found = true; + requests.remove(j); + break; + } + } + } + + if (found) { + // Trigger may initiate new updates, so wait to remove requests array from + // fRequestsInProgress map. This way updateStarted() will not send a + // redundant "UPDATE SEQUENCE STARTED" notification. + trigger(update.getSchedulingPath()); + if (requests.isEmpty()) { + fRequestsInProgress.remove(update.getSchedulingPath()); + } + } else { + // Update may be removed from in progress list if it was canceled by schedule(). + Assert.isTrue( update.isCanceled() ); + } + } + if (fRequestsInProgress.isEmpty() && fWaitingRequests.isEmpty()) { + if (DEBUG_UPDATE_SEQUENCE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { + System.out.println("MODEL SEQUENCE ENDS"); //$NON-NLS-1$ + } + notifyUpdate(UPDATE_SEQUENCE_COMPLETE, null); + } + } + }); + + } + + /** + * @return Returns true if there are outstanding updates in the viewer. + */ + boolean areRequestsPending() { + return !fRequestsInProgress.isEmpty() || !fWaitingRequests.isEmpty(); + } + + /** + * @return Returns the state tracker for the content provider. + */ + ViewerStateTracker getStateTracker() { + return fStateTracker; + } + + /** + * Notifies listeners about given update. + * @param type Type of update to call listeners with. + * @param update Update to notify about. + */ + private void notifyUpdate(final int type, final IViewerUpdate update) { + if (!fUpdateListeners.isEmpty()) { + Object[] listeners = fUpdateListeners.getListeners(); + for (int i = 0; i < listeners.length; i++) { + final IViewerUpdateListener listener = (IViewerUpdateListener) listeners[i]; + SafeRunner.run(new ISafeRunnable() { + public void run() throws Exception { + switch (type) { + case UPDATE_SEQUENCE_BEGINS: + listener.viewerUpdatesBegin(); + break; + case UPDATE_SEQUENCE_COMPLETE: + listener.viewerUpdatesComplete(); + break; + case UPDATE_BEGINS: + listener.updateStarted(update); + break; + case UPDATE_COMPLETE: + listener.updateComplete(update); + break; + } + } + + public void handleException(Throwable exception) { + DebugUIPlugin.log(exception); + } + }); + } + } + } + + /** + * Cancels outstanding updates for the element at given path and its + * children. + * @param path Path of element. + */ + private void cancelSubtreeUpdates(TreePath path) { + Assert.isTrue( getViewer().getDisplay().getThread() == Thread.currentThread() ); + + Iterator iterator = fRequestsInProgress.entrySet().iterator(); + while (iterator.hasNext()) { + Entry entry = (Entry) iterator.next(); + TreePath entryPath = (TreePath) entry.getKey(); + if (entryPath.startsWith(path, null)) { + List requests = (List) entry.getValue(); + Iterator reqIter = requests.iterator(); + while (reqIter.hasNext()) { + // Cancel update and remove from requests list. Removing from + // fRequestsInProgress ensures that isRequestBlocked() won't be triggered + // by a canceled update. + ((IRequest) reqIter.next()).cancel(); + reqIter.remove(); + } + } + } + List purge = new ArrayList(); + iterator = fWaitingRequests.keySet().iterator(); + while (iterator.hasNext()) { + TreePath entryPath = (TreePath) iterator.next(); + if (entryPath.startsWith(path, null)) { + purge.add(entryPath); + } + } + iterator = purge.iterator(); + while (iterator.hasNext()) { + fWaitingRequests.remove(iterator.next()); + } + + fStateTracker.cancelStateSubtreeUpdates(path); + } + + /** + * Returns whether this given request should be run, or should wait for + * parent update to complete. + * + * @param update the update the schedule + */ + private void schedule(final ViewerUpdateMonitor update) { + TreePath schedulingPath = update.getSchedulingPath(); + List requests = (List) fWaitingRequests.get(schedulingPath); + if (requests == null) { + requests = new LinkedList(); + requests.add(update); + fWaitingRequests.put(schedulingPath, requests); + + List inProgressList = (List)fRequestsInProgress.get(schedulingPath); + if (inProgressList != null) { + int staleUpdateIndex = inProgressList.indexOf(update); + if (staleUpdateIndex >= 0) { + // Cancel update and remove from requests list. Removing from + // fRequestsInProgress ensures that isRequestBlocked() won't be triggered + // by a canceled update. + ViewerUpdateMonitor staleUpdate = (ViewerUpdateMonitor)inProgressList.remove(staleUpdateIndex); + staleUpdate.cancel(); + // Note: Do not reset the inProgressList to null. This would cause the + // updateStarted() method to think that a new update sequence is + // being started. Since there are waiting requests for this scheduling + // path, the list will be cleaned up later. + } + } + if (inProgressList == null || inProgressList.isEmpty()) { + getViewer().getDisplay().asyncExec(new Runnable() { + public void run() { + trigger(update.getSchedulingPath()); + } + }); + } + } else { + // there are waiting requests: coalesce with existing request and add to list + requests.add(coalesce(requests, update)); + } + } + + /** + * Tries to coalesce the given request with any request in the list. If a match is found, + * the resulting request is then coalesced again with candidates in list. + * @param requests List of waiting requests to coalesce with + * @param toCoalesce request to coalesce + * @return Returns either the coalesced request. If no match was found it returns the + * toCoalesce parameter request. Either way the returned request needs to be added to the + * waiting requests list. + */ + private ViewerUpdateMonitor coalesce(List requests, ViewerUpdateMonitor toCoalesce) { + Iterator reqIter = requests.iterator(); + while (reqIter.hasNext()) { + ViewerUpdateMonitor waiting = (ViewerUpdateMonitor) reqIter.next(); + if (waiting.coalesce(toCoalesce)) { + requests.remove(waiting); + // coalesced with existing request, done + // try to coalesce the combined requests with other waiting requests + return coalesce(requests, waiting); + } + } + return toCoalesce; + } + + /** + * Returns whether there are outstanding ChildrenUpdate updates for the given path. + * This method is expected to be called during processing of a ChildrenRequest, + * therefore one running children request is ignored. + * @param path Path of element to check. + * @return True if there are outstanding children updates for given element. + */ + boolean areChildrenUpdatesPending(TreePath path) { + Assert.isTrue( getViewer().getDisplay().getThread() == Thread.currentThread() ); + + List requests = (List) fWaitingRequests.get(path); + if (requests != null) { + for (int i = 0; i < requests.size(); i++) { + if (requests.get(i) instanceof ChildrenUpdate) { + return true; + } + } + } + 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 true; + } + } + } + } + return false; + } + + /** + * Triggers waiting requests based on the given request that just + * completed. + * <p> + * Requests are processed in order such that updates to + * children are delayed until updates for parent elements are completed. + * This allows the expansion/selection state of the elements to be + * properly restored as new elements are retreived from model. + * </p> + * + * @param The schedulingPath path or requests to start processing. May + * be <code>null</code> to start the shortest path request. + */ + private void trigger(TreePath schedulingPath) { + if (fWaitingRequests.isEmpty()) { + return; + } + List waiting = (List) fWaitingRequests.get(schedulingPath); + if (waiting == null) { + // no waiting, update the entry with the shortest path + int length = Integer.MAX_VALUE; + Iterator entries = fWaitingRequests.entrySet().iterator(); + Entry candidate = null; + while (entries.hasNext()) { + Entry entry = (Entry) entries.next(); + TreePath key = (TreePath) entry.getKey(); + if (key.getSegmentCount() < length && !isRequestBlocked(key)) { + candidate = entry; + length = key.getSegmentCount(); + } + } + if (candidate != null) { + startHighestPriorityRequest((TreePath) candidate.getKey(), (List) candidate.getValue()); + } + } else if (!isRequestBlocked(schedulingPath)) { + // start the highest priority request + startHighestPriorityRequest(schedulingPath, waiting); + } + } + + /** + * Returns true if there are running requests for any parent element of + * the given tree path. + * @param requestPath Path of element to check. + * @return Returns true if requests are running. + */ + private boolean isRequestBlocked(TreePath requestPath) { + TreePath parentPath = requestPath; + List parentRequests = (List)fRequestsInProgress.get(parentPath); + while (parentRequests == null || parentRequests.isEmpty()) { + parentPath = parentPath.getParentPath(); + if (parentPath == null) { + // no running requests: start request + return false; + } + parentRequests = (List)fRequestsInProgress.get(parentPath); + } + return true; + } + + /** + * @param key the {@link TreePath} + * @param waiting the list of waiting requests + */ + private void startHighestPriorityRequest(TreePath key, List waiting) { + int priority = 4; + ViewerUpdateMonitor next = null; + Iterator requests = waiting.iterator(); + while (requests.hasNext()) { + ViewerUpdateMonitor vu = (ViewerUpdateMonitor) requests.next(); + if (vu.getPriority() < priority) { + next = vu; + priority = next.getPriority(); + } + } + if (next != null) { + waiting.remove(next); + if (waiting.isEmpty()) { + fWaitingRequests.remove(key); + } + next.start(); + } + } + + /** + * Returns the element corresponding to the given tree path. + * + * @param path + * tree path + * @return model element + */ + protected Object getElement(TreePath path) { + if (path.getSegmentCount() > 0) { + return path.getLastSegment(); + } + return getViewer().getInput(); + } + + /** + * Reschedule any children updates in progress for the given parent that + * have a start index greater than the given index. An element has been + * removed at this index, invalidating updates in progress. + * + * @param parentPath + * view tree path to parent element + * @param modelIndex + * index at which an element was removed + */ + private void rescheduleUpdates(TreePath parentPath, int modelIndex) { + Assert.isTrue( getViewer().getDisplay().getThread() == Thread.currentThread() ); + + List requests = (List) fRequestsInProgress.get(parentPath); + List reCreate = null; + if (requests != null) { + Iterator iterator = requests.iterator(); + while (iterator.hasNext()) { + IViewerUpdate update = (IViewerUpdate) iterator.next(); + if (update instanceof IChildrenUpdate) { + IChildrenUpdate childrenUpdate = (IChildrenUpdate) update; + if (childrenUpdate.getOffset() > modelIndex) { + // Cancel update and remove from requests list. Removing from + // fRequestsInProgress ensures that isRequestBlocked() won't be triggered + // by a canceled update. + childrenUpdate.cancel(); + iterator.remove(); + if (reCreate == null) { + reCreate = new ArrayList(); + } + reCreate.add(childrenUpdate); + if (DEBUG_CONTENT_PROVIDER && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { + System.out.println("canceled update in progress handling REMOVE: " + childrenUpdate); //$NON-NLS-1$ + } + } + } + } + } + requests = (List) fWaitingRequests.get(parentPath); + if (requests != null) { + Iterator iterator = requests.iterator(); + while (iterator.hasNext()) { + IViewerUpdate update = (IViewerUpdate) iterator.next(); + if (update instanceof IChildrenUpdate) { + IChildrenUpdate childrenUpdate = (IChildrenUpdate) update; + if (childrenUpdate.getOffset() > modelIndex) { + ((ChildrenUpdate) childrenUpdate).setOffset(childrenUpdate.getOffset() - 1); + if (DEBUG_CONTENT_PROVIDER && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { + System.out.println("modified waiting update handling REMOVE: " + childrenUpdate); //$NON-NLS-1$ + } + } + } + } + } + // re-schedule canceled updates at new position. + // have to do this last else the requests would be waiting and + // get modified. + if (reCreate != null) { + Iterator iterator = reCreate.iterator(); + while (iterator.hasNext()) { + IChildrenUpdate childrenUpdate = (IChildrenUpdate) iterator.next(); + int start = childrenUpdate.getOffset() - 1; + int end = start + childrenUpdate.getLength(); + for (int i = start; i < end; i++) { + doUpdateElement(parentPath, i); + } + } + } + } + + private void doUpdateChildCount(TreePath path) { + Assert.isTrue( getViewer().getDisplay().getThread() == Thread.currentThread() ); + Object element = getElement(path); IElementContentProvider contentAdapter = ViewerAdapterService.getContentProvider(element); if (contentAdapter != null) { - ChildrenCountUpdate request = new ChildrenCountUpdate(this, getViewer().getInput(), path, element, contentAdapter, getPresentationContext()); + ChildrenCountUpdate request = new ChildrenCountUpdate(this, getViewer().getInput(), path, element, contentAdapter); schedule(request); } } - protected synchronized void doUpdateElement(TreePath parentPath, int modelIndex) { + void doUpdateElement(TreePath parentPath, int modelIndex) { + Assert.isTrue( getViewer().getDisplay().getThread() == Thread.currentThread() ); + Object parent = getElement(parentPath); IElementContentProvider contentAdapter = ViewerAdapterService.getContentProvider(parent); if (contentAdapter != null) { - ChildrenUpdate request = new ChildrenUpdate(this, getViewer().getInput(), parentPath, parent, modelIndex, contentAdapter, getPresentationContext()); + ChildrenUpdate request = new ChildrenUpdate(this, getViewer().getInput(), parentPath, parent, modelIndex, contentAdapter); schedule(request); } } - protected synchronized void doUpdateHasChildren(TreePath path) { + private void doUpdateHasChildren(TreePath path) { + Assert.isTrue( getViewer().getDisplay().getThread() == Thread.currentThread() ); + Object element = getElement(path); IElementContentProvider contentAdapter = ViewerAdapterService.getContentProvider(element); if (contentAdapter != null) { - HasChildrenUpdate request = new HasChildrenUpdate(this, getViewer().getInput(), path, element, contentAdapter, getPresentationContext()); + HasChildrenUpdate request = new HasChildrenUpdate(this, getViewer().getInput(), path, element, contentAdapter); schedule(request); } } - /* (non-Javadoc) - * @see org.eclipse.debug.internal.ui.viewers.model.provisional.viewers.ModelContentProvider#getPresentationContext() + /** + * Checks if there are outstanding updates that may replace the element + * at given path. + * @param path Path of element to check. + * @return Returns true if there are outsanding updates. */ + boolean areElementUpdatesPending(TreePath path) { + Assert.isTrue( getViewer().getDisplay().getThread() == Thread.currentThread() ); + + 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.containsUpdate(path)) { + 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; + } + + /** + * Returns the presentation context for this content provider. + * + * @return presentation context + */ protected IPresentationContext getPresentationContext() { ITreeModelViewer viewer = getViewer(); if (viewer != null) { @@ -87,10 +1134,67 @@ public class TreeModelContentProvider extends ModelContentProvider implements IT return null; } - /* (non-Javadoc) - * @see org.eclipse.debug.internal.ui.viewers.model.provisional.viewers.ModelContentProvider#handleAdd(org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta) - */ - protected void handleAdd(IModelDelta delta) { + /** + * Updates the viewer with the following deltas. + * + * @param nodes Model deltas to be processed. + * @param mask the model delta mask + * @see IModelDelta for a list of masks + */ + private void updateNodes(IModelDelta[] nodes, int mask) { + for (int i = 0; i < nodes.length; i++) { + IModelDelta node = nodes[i]; + int flags = node.getFlags() & mask; + + if ((flags & IModelDelta.ADDED) != 0) { + handleAdd(node); + } + if ((flags & IModelDelta.REMOVED) != 0) { + handleRemove(node); + } + if ((flags & IModelDelta.CONTENT) != 0) { + handleContent(node); + } + if ((flags & IModelDelta.STATE) != 0) { + handleState(node); + } + if ((flags & IModelDelta.INSERTED) != 0) { + handleInsert(node); + } + if ((flags & IModelDelta.REPLACED) != 0) { + handleReplace(node); + } + if ((flags & IModelDelta.INSTALL) != 0) { + handleInstall(node); + } + if ((flags & IModelDelta.UNINSTALL) != 0) { + handleUninstall(node); + } + if ((flags & IModelDelta.EXPAND) != 0) { + handleExpand(node); + } + if ((flags & IModelDelta.COLLAPSE) != 0) { + handleCollapse(node); + } + if ((flags & IModelDelta.SELECT) != 0) { + handleSelect(node); + } + if ((flags & IModelDelta.REVEAL) != 0) { + handleReveal(node); + } + updateNodes(node.getChildDeltas(), mask); + } + } + + protected void handleInstall(IModelDelta delta) { + installModelProxy(getViewer().getInput(), getFullTreePath(delta)); + } + + protected void handleUninstall(IModelDelta delta) { + disposeModelProxy(getFullTreePath(delta)); + } + + protected void handleAdd(IModelDelta delta) { IModelDelta parentDelta = delta.getParentDelta(); TreePath parentPath = getViewerTreePath(parentDelta); Object element = delta.getElement(); @@ -122,7 +1226,7 @@ public class TreeModelContentProvider extends ModelContentProvider implements IT getViewer().replace(parentPath, viewIndex, element); TreePath childPath = parentPath.createChildPath(element); updateHasChildren(childPath); - restorePendingStateOnUpdate(childPath, modelIndex, false, false, false); + fStateTracker.restorePendingStateOnUpdate(childPath, modelIndex, false, false, false); } } else { if (DEBUG_CONTENT_PROVIDER && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { @@ -135,7 +1239,7 @@ public class TreeModelContentProvider extends ModelContentProvider implements IT /* (non-Javadoc) * @see org.eclipse.debug.internal.ui.viewers.model.provisional.viewers.ModelContentProvider#handleContent(org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta) */ - protected void handleContent(IModelDelta delta) { + protected void handleContent(IModelDelta delta) { if (delta.getParentDelta() == null && delta.getChildCount() == 0) { // if the delta is for the root, ensure the root still matches viewer input if (!delta.getElement().equals(getViewer().getInput())) { @@ -144,14 +1248,13 @@ public class TreeModelContentProvider extends ModelContentProvider implements IT } TreePath treePath = getViewerTreePath(delta); cancelSubtreeUpdates(treePath); - appendToPendingStateDelta(treePath); getViewer().refresh(getElement(treePath)); } /* (non-Javadoc) * @see org.eclipse.debug.internal.ui.viewers.model.ModelContentProvider#handleCollapse(org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta) */ - protected void handleCollapse(IModelDelta delta) { + protected void handleCollapse(IModelDelta delta) { TreePath elementPath = getViewerTreePath(delta); getViewer().setExpandedState(elementPath, false); cancelRestore(elementPath, IModelDelta.EXPAND); @@ -160,7 +1263,7 @@ public class TreeModelContentProvider extends ModelContentProvider implements IT /* (non-Javadoc) * @see org.eclipse.debug.internal.ui.viewers.model.provisional.viewers.ModelContentProvider#handleExpand(org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta) */ - protected void handleExpand(IModelDelta delta) { + protected void handleExpand(IModelDelta delta) { // expand each parent, then this node IModelDelta parentDelta = delta.getParentDelta(); if (parentDelta != null) { @@ -181,10 +1284,14 @@ public class TreeModelContentProvider extends ModelContentProvider implements IT } } - protected void expand(IModelDelta delta) { + /** + * Expands the element pointed to by given delta. + * @param delta Delta that points to the element to expand. + */ + private void expand(IModelDelta delta) { int childCount = delta.getChildCount(); int modelIndex = delta.getIndex(); - ITreeModelContentProviderTarget treeViewer = getViewer(); + IInternalTreeModelViewer treeViewer = getViewer(); TreePath elementPath = getViewerTreePath(delta); if (modelIndex >= 0) { TreePath parentPath = elementPath.getParentPath(); @@ -227,9 +1334,10 @@ public class TreeModelContentProvider extends ModelContentProvider implements IT * @param parentPath viewer tree path to parent element * @param element element to insert * @param modelIndex index of the element in the model - * @return + * @return Returns the view index of the newly inserted element + * or -1 if not inserted. */ - protected int unfilterElement(TreePath parentPath, Object element, int modelIndex) { + private int unfilterElement(TreePath parentPath, Object element, int modelIndex) { // Element is filtered - if no longer filtered, insert the element if (shouldFilter(parentPath, element)) { if (DEBUG_CONTENT_PROVIDER && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { @@ -253,23 +1361,17 @@ public class TreeModelContentProvider extends ModelContentProvider implements IT } } - /* (non-Javadoc) - * @see org.eclipse.debug.internal.ui.viewers.model.provisional.viewers.ModelContentProvider#handleInsert(org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta) - */ protected void handleInsert(IModelDelta delta) { // TODO: filters getViewer().insert(getViewerTreePath(delta.getParentDelta()), delta.getElement(), delta.getIndex()); } - /* (non-Javadoc) - * @see org.eclipse.debug.internal.ui.viewers.model.provisional.viewers.ModelContentProvider#handleRemove(org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta) - */ protected void handleRemove(IModelDelta delta) { if (DEBUG_CONTENT_PROVIDER && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { System.out.println("handleRemove(" + delta.getElement() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ } IModelDelta parentDelta = delta.getParentDelta(); - ITreeModelContentProviderTarget treeViewer = getViewer(); + IInternalTreeModelViewer treeViewer = getViewer(); TreePath parentPath = getViewerTreePath(parentDelta); Object element = delta.getElement(); if (removeElementFromFilters(parentPath, element)) { @@ -333,20 +1435,43 @@ public class TreeModelContentProvider extends ModelContentProvider implements IT getViewer().refresh(parentDelta.getElement()); } - /* (non-Javadoc) - * @see org.eclipse.debug.internal.ui.viewers.model.provisional.viewers.ModelContentProvider#handleReplace(org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta) - */ protected void handleReplace(IModelDelta delta) { TreePath parentPath = getViewerTreePath(delta.getParentDelta()); - getViewer().replace(parentPath, delta.getIndex(), delta.getElement()); + int index = delta.getIndex(); + if (index < 0) { + index = fTransform.indexOfFilteredElement(parentPath, delta.getElement()); + } + if (index >= 0) { + boolean filtered = isFiltered(parentPath, index); + boolean shouldFilter = shouldFilter(parentPath, delta.getReplacementElement()); + + // Update the filter transform + if (filtered) { + clearFilteredChild(parentPath, index); + } + if (shouldFilter) { + addFilteredIndex(parentPath, index, delta.getElement()); + } + + // Update the viewer + if (filtered) { + if (!shouldFilter) { + getViewer().insert(parentPath, delta.getReplacementElement(), modelToViewIndex(parentPath, index)); + } + //else do nothing + } else { + if (shouldFilter) { + getViewer().remove(parentPath, modelToViewIndex(parentPath, index)); + } else { + getViewer().replace(parentPath, delta.getIndex(), delta.getReplacementElement()); + } + } + } } - /* (non-Javadoc) - * @see org.eclipse.debug.internal.ui.viewers.model.provisional.viewers.ModelContentProvider#handleSelect(org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta) - */ protected void handleSelect(IModelDelta delta) { int modelIndex = delta.getIndex(); - ITreeModelContentProviderTarget treeViewer = getViewer(); + IInternalTreeModelViewer treeViewer = getViewer(); // check if selection is allowed IStructuredSelection candidate = new TreeSelection(getViewerTreePath(delta)); if ((delta.getFlags() & IModelDelta.FORCE) == 0 && @@ -382,16 +1507,10 @@ public class TreeModelContentProvider extends ModelContentProvider implements IT } } - /* (non-Javadoc) - * @see org.eclipse.debug.internal.ui.viewers.model.provisional.viewers.ModelContentProvider#handleState(org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta) - */ protected void handleState(IModelDelta delta) { getViewer().update(delta.getElement()); } - /* (non-Javadoc) - * @see org.eclipse.debug.internal.ui.viewers.model.ModelContentProvider#handleReveal(org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta) - */ protected void handleReveal(IModelDelta delta) { IModelDelta parentDelta = delta.getParentDelta(); if (parentDelta != null) { @@ -401,9 +1520,13 @@ public class TreeModelContentProvider extends ModelContentProvider implements IT } } - protected void reveal(IModelDelta delta) { + /** + * Reveals the element pointed to by given delta. + * @param delta Delta pointing to the element to reveal. + */ + private void reveal(IModelDelta delta) { int modelIndex = delta.getIndex(); - ITreeModelContentProviderTarget treeViewer = getViewer(); + IInternalTreeModelViewer treeViewer = getViewer(); TreePath elementPath = getViewerTreePath(delta); if (modelIndex >= 0) { TreePath parentPath = elementPath.getParentPath(); @@ -433,90 +1556,7 @@ public class TreeModelContentProvider extends ModelContentProvider implements IT } } } - - /* (non-Javadoc) - * @see org.eclipse.debug.internal.ui.viewers.model.provisional.viewers.ModelContentProvider#buildViewerState(org.eclipse.debug.internal.ui.viewers.provisional.ModelDelta) - */ - protected void buildViewerState(ModelDelta delta) { - ITreeModelContentProviderTarget viewer = getViewer(); - 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. - TreePath topElementPath = viewer.getTopElementPath(); - if (topElementPath != null) { - ModelDelta parentDelta = delta; - TreePath parentPath = EMPTY_TREE_PATH; - for (int i = 0; i < topElementPath.getSegmentCount(); i++) { - Object element = topElementPath.getSegment(i); - int index = viewer.findElementIndex(parentPath, element); - ModelDelta childDelta = parentDelta.getChildDelta(element); - if (childDelta == null) { - parentDelta = parentDelta.addNode(element, index, IModelDelta.NO_CHANGE); - } else { - parentDelta = childDelta; - } - parentPath = parentPath.createChildPath(element); - } - parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.REVEAL); - } - } - - /* (non-Javadoc) - * @see org.eclipse.debug.internal.ui.viewers.model.provisional.viewers.ModelContentProvider#doInitialRestore() - */ - protected void doInitialRestore(ModelDelta delta) { - // Find the reveal delta and mark nodes on its path - // to reveal as elements are updated. - markRevealDelta(delta); - - // Restore visible items. - // Note (Pawel Piech): the initial list of items is normally - // empty, so in most cases the code below does not do anything. - // Instead doRestore() is called when various updates complete. - int count = getViewer().getChildCount(TreePath.EMPTY); - for (int i = 0; i < count; i++) { - Object data = getViewer().getChildElement(TreePath.EMPTY, i); - if (data != null) { - restorePendingStateOnUpdate(new TreePath(new Object[]{data}), i, false, false, false); - } - } - - } - - /** - * Finds the delta with the reveal flag, then it walks up this - * delta and marks all the parents of it with the reveal flag. - * These flags are then used by the restore logic to restore - * and reveal all the nodes leading up to the element that should - * be ultimately at the top. - * @return The node just under the rootDelta which contains - * the reveal flag. <code>null</code> if no reveal flag was found. - */ - private ModelDelta markRevealDelta(ModelDelta rootDelta) { - final ModelDelta[] revealDelta = new ModelDelta[1]; - IModelDeltaVisitor visitor = new IModelDeltaVisitor() { - public boolean visit(IModelDelta delta, int depth) { - if ( (delta.getFlags() & IModelDelta.REVEAL) != 0) { - revealDelta[0] = (ModelDelta)delta; - } - // Keep recursing only if we haven't found our delta yet. - return revealDelta[0] == null; - } - }; - - rootDelta.accept(visitor); - if (revealDelta[0] != null) { - ModelDelta parentDelta = (ModelDelta)revealDelta[0].getParentDelta(); - while(parentDelta.getParentDelta() != null) { - revealDelta[0] = parentDelta; - revealDelta[0].setFlags(revealDelta[0].getFlags() | IModelDelta.REVEAL); - parentDelta = (ModelDelta)parentDelta.getParentDelta(); - } - } - return revealDelta[0]; - } /* (non-Javadoc) * @see org.eclipse.jface.viewers.ILazyTreePathContentProvider#getParents(java.lang.Object) @@ -528,20 +1568,21 @@ public class TreeModelContentProvider extends ModelContentProvider implements IT /* (non-Javadoc) * @see org.eclipse.jface.viewers.ILazyTreePathContentProvider#updateChildCount(org.eclipse.jface.viewers.TreePath, int) */ - public synchronized void updateChildCount(TreePath treePath, int currentChildCount) { + public void updateChildCount(TreePath treePath, int currentChildCount) { + Assert.isTrue( getViewer().getDisplay().getThread() == Thread.currentThread() ); + if (DEBUG_CONTENT_PROVIDER && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { System.out.println("updateChildCount(" + getElement(treePath) + ", " + currentChildCount + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } - refilterChildren(treePath); - //re-filter children when asked to update the child count for an element (i.e. - // when refreshing, see if filtered children are still filtered) doUpdateChildCount(treePath); } /* (non-Javadoc) * @see org.eclipse.jface.viewers.ILazyTreePathContentProvider#updateElement(org.eclipse.jface.viewers.TreePath, int) */ - public synchronized void updateElement(TreePath parentPath, int viewIndex) { + public void updateElement(TreePath parentPath, int viewIndex) { + Assert.isTrue( getViewer().getDisplay().getThread() == Thread.currentThread() ); + int modelIndex = viewToModelIndex(parentPath, viewIndex); if (DEBUG_CONTENT_PROVIDER && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { System.out.println("updateElement("+ getElement(parentPath) + ", " + viewIndex + ") > modelIndex = " + modelIndex); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ @@ -552,7 +1593,9 @@ public class TreeModelContentProvider extends ModelContentProvider implements IT /* (non-Javadoc) * @see org.eclipse.jface.viewers.ILazyTreePathContentProvider#updateHasChildren(org.eclipse.jface.viewers.TreePath) */ - public synchronized void updateHasChildren(TreePath path) { + public void updateHasChildren(TreePath path) { + Assert.isTrue( getViewer().getDisplay().getThread() == Thread.currentThread() ); + if (DEBUG_CONTENT_PROVIDER && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { System.out.println("updateHasChildren(" + getElement(path)); //$NON-NLS-1$ } @@ -560,198 +1603,58 @@ public class TreeModelContentProvider extends ModelContentProvider implements IT } /** - * @param delta + * Schedules given update to be performed on the viewer. + * Updates are queued up if they are completed in the same + * UI cycle. + * @param update Update to perform. */ - void restorePendingStateNode(final ModelDelta delta, boolean knowsHasChildren, boolean knowsChildCount, boolean checkChildrenRealized) { - final TreePath treePath = getViewerTreePath(delta); - final ITreeModelContentProviderTarget viewer = getViewer(); - - // 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_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("\tRESTORE 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_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("\tRESTORE COLLAPSE: " + treePath.getLastSegment()); //$NON-NLS-1$ - } - // Check auto-expand before collapsing an element (bug 335734) - int autoexpand = getViewer().getAutoExpandLevel(); - if (autoexpand != ITreeModelViewer.ALL_LEVELS && autoexpand < (treePath.getSegmentCount() + 1)) { - getViewer().setExpandedState(treePath, false); - } - delta.setFlags(delta.getFlags() & ~IModelDelta.COLLAPSE); - } - } - - if ((delta.getFlags() & IModelDelta.SELECT) != 0) { - delta.setFlags(delta.getFlags() & ~IModelDelta.SELECT); - if (DEBUG_STATE_SAVE_RESTORE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("\tRESTORE SELECT: " + treePath.getLastSegment()); //$NON-NLS-1$ - } - ITreeSelection currentSelection = (ITreeSelection)viewer.getSelection(); - if (currentSelection == null || currentSelection.isEmpty()) { - viewer.setSelection(new TreeSelection(treePath), false, false); - } else { - TreePath[] currentPaths = currentSelection.getPaths(); - boolean pathInSelection = false; - for (int i = 0; i < currentPaths.length; i++) { - if (currentPaths[i].equals(treePath)) { - pathInSelection = true; - break; - } - } - // Only set the selection if the element is not yet in - // selection. Otherwise the setSelection() call will - // update selection listeners needlessly. - if (!pathInSelection) { - TreePath[] newPaths = new TreePath[currentPaths.length + 1]; - System.arraycopy(currentPaths, 0, newPaths, 0, currentPaths.length); - newPaths[newPaths.length - 1] = treePath; - viewer.setSelection(new TreeSelection(newPaths), false, false); - } - } - } - - if ((delta.getFlags() & IModelDelta.REVEAL) != 0) { - delta.setFlags(delta.getFlags() & ~IModelDelta.REVEAL); - // Look for the reveal flag in the child deltas. If - // A child delta has the reveal flag, do not set the - // top element yet. - boolean setTopItem = true; - IModelDelta[] childDeltas = delta.getChildDeltas(); - for (int i = 0; i < childDeltas.length; i++) { - IModelDelta childDelta = childDeltas[i]; - int modelIndex = childDelta.getIndex(); - if (modelIndex >= 0 && (childDelta.getFlags() & IModelDelta.REVEAL) != 0) { - setTopItem = false; - } - } - - if (setTopItem) { - Assert.isTrue(fPendingSetTopItem == null); - final TreePath parentPath = treePath.getParentPath(); - - // listen when current updates are complete and only - // then do the REVEAL - fPendingSetTopItem = new IPendingRevealDelta() { - // Revealing some elements can trigger expanding some of elements - // that have been just revealed. Therefore, we have to check one - // more time after the new triggered updates are completed if we - // have to set again the top index - private int counter = 0; - private Object modelInput = fPendingState.getElement(); - - public void viewerUpdatesBegin() { - } - - public void viewerUpdatesComplete() { - // assume that fRequestsInProgress is empty if we got here - Assert.isTrue(fRequestsInProgress.isEmpty()); - - final Display viewerDisplay = viewer.getDisplay(); - if (fWaitingRequests.isEmpty() && !viewerDisplay.isDisposed()) { - viewerDisplay.asyncExec(new Runnable() { - public void run() { - if (TreeModelContentProvider.this.isDisposed()) { - return; - } - - TreePath topPath = viewer.getTopElementPath(); - if (!treePath.equals(topPath)) { - int index = viewer.findElementIndex(parentPath, treePath.getLastSegment()); - if (index >= 0) { - if (DEBUG_STATE_SAVE_RESTORE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("\tRESTORE REVEAL: " + treePath.getLastSegment()); //$NON-NLS-1$ - } - viewer.reveal(parentPath, index); - - } - } - } - }); - - counter++; - // in case the pending state was already set to null, we assume that - // all others elements are restored, so we don't expect that REVEAL will - // trigger other updates - if (counter > 1 || fPendingState == null) { - dispose(); - } - } - - } - - public void updateStarted(IViewerUpdate update) { - } - - public void updateComplete(IViewerUpdate update) { - } - - public ModelDelta getDelta() { - return delta; - } - - public void dispose() { - // remove myself as viewer update listener - viewer.removeViewerUpdateListener(this); - - // top item is set - fPendingSetTopItem = null; - - if (fPendingState == null) { - if (DEBUG_STATE_SAVE_RESTORE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("STATE RESTORE COMPELTE: " + fPendingState); //$NON-NLS-1$ - } - notifyStateUpdate(modelInput, STATE_RESTORE_SEQUENCE_COMPLETE, null); - } else { - checkIfRestoreComplete(); - } - } - - }; - viewer.addViewerUpdateListener(fPendingSetTopItem); - } - } + void scheduleViewerUpdate(ViewerUpdateMonitor update) { + Display display; + synchronized(this) { + if (isDisposed()) return; + display = getViewer().getDisplay(); + fCompletedUpdates.add(update); + if (fCompletedUpdatesJob == null) { + fCompletedUpdatesJob = new Runnable() { + public void run() { + if (!isDisposed()) { + performUpdates(); + } + } + }; + display.asyncExec(fCompletedUpdatesJob); + } + } + } - // If we know the child count of the element, look for the reveal - // flag in the child deltas. For the children with reveal flag start - // a new update. - // If the child delta's index is out of range, strip the reveal flag - // since it is no longer applicable. - if (knowsChildCount) { - int childCount = viewer.getChildCount(treePath); - if (childCount >= 0) { - ModelDelta[] childDeltas = (ModelDelta[])delta.getChildDeltas(); - for (int i = 0; i < childDeltas.length; i++) { - ModelDelta childDelta = childDeltas[i]; - int modelIndex = childDelta.getIndex(); - if (modelIndex >= 0 && (childDelta.getFlags() & IModelDelta.REVEAL) != 0) { - if (modelIndex < childCount) { - doUpdateElement(treePath, modelIndex); - } else { - childDelta.setFlags(childDelta.getFlags() & ~IModelDelta.REVEAL); - } - } - } - } - } - - // 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_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("\tRESTORE CONTENT: " + treePath.getLastSegment()); //$NON-NLS-1$ + /** + * Perform the updates pointed to by given array on the viewer. + */ + private void performUpdates() { + Assert.isTrue( getViewer().getDisplay().getThread() == Thread.currentThread() ); + + List jobCompletedUpdates; + synchronized(TreeModelContentProvider.this) { + if (isDisposed()) { + return; } - delta.setFlags(delta.getFlags() & ~IModelDelta.CONTENT); + jobCompletedUpdates = fCompletedUpdates; + fCompletedUpdatesJob = null; + fCompletedUpdates = new ArrayList(); } + // necessary to check if viewer is disposed + try { + for (int i = 0; i < jobCompletedUpdates.size(); i++) { + ViewerUpdateMonitor completedUpdate = (ViewerUpdateMonitor)jobCompletedUpdates.get(i); + if (!completedUpdate.isCanceled() && !isDisposed()) { + IStatus status = completedUpdate.getStatus(); + if (status == null || status.isOK()) { + completedUpdate.performUpdate(); + } + } + } + } finally { + updatesComplete(jobCompletedUpdates); + } } - } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/TreeModelLabelProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/TreeModelLabelProvider.java index 833124807..513c2dc07 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/TreeModelLabelProvider.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/TreeModelLabelProvider.java @@ -19,12 +19,10 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; -import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.ISafeRunnable; -import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.ListenerList; import org.eclipse.core.runtime.SafeRunner; -import org.eclipse.core.runtime.Status; import org.eclipse.debug.internal.ui.DebugUIPlugin; import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider; import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; @@ -43,7 +41,6 @@ import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.widgets.Display; -import org.eclipse.ui.progress.UIJob; /** * @since 3.3 @@ -52,7 +49,11 @@ public class TreeModelLabelProvider extends ColumnLabelProvider implements ITreeModelLabelProvider, IModelChangedListener { - private ITreeModelLabelProviderTarget fViewer; + private IInternalTreeModelViewer fViewer; + + /** + * Note: access this variable should be synchronized with <code>this</code>. + */ private List fComplete; /** @@ -84,23 +85,15 @@ public class TreeModelLabelProvider extends ColumnLabelProvider /** * Updates waiting to be sent to the label provider. The map contains * lists of updates, keyed using the provider. - * <p> - * Note: this variable should only be accessed inside a synchronized section - * using the enclosing label provider instance. - * </p> */ private Map fPendingUpdates = new HashMap(); /** - * A job that will send the label update requests. + * A runnable that will send the label update requests. * This variable allows the job to be canceled and re-scheduled if * new updates are requested. - * <p> - * Note: this variable should only be accessed inside a synchronized section - * using the enclosing label provider instance. - * </p> */ - private UIJob fPendingUpdatesJob; + private Runnable fPendingUpdatesRunnable; /** * List of updates in progress @@ -111,7 +104,7 @@ public class TreeModelLabelProvider extends ColumnLabelProvider * Delta visitor actively cancels the outstanding label updates for * elements that are changed and are about to be updated. */ - class Visitor implements IModelDeltaVisitor { + class CancelPendingUpdatesVisitor implements IModelDeltaVisitor { /* (non-Javadoc) * @see org.eclipse.debug.internal.ui.viewers.provisional.IModelDeltaVisitor#visit(org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta, int) */ @@ -130,12 +123,13 @@ public class TreeModelLabelProvider extends ColumnLabelProvider /** * Delta visitor */ - private Visitor fVisitor = new Visitor(); + private CancelPendingUpdatesVisitor fCancelPendingUpdatesVisitor = new CancelPendingUpdatesVisitor(); /** * Constructs a new label provider on the given display + * @param viewer Viewer that this label provieer is used with. */ - public TreeModelLabelProvider(ITreeModelLabelProviderTarget viewer) { + public TreeModelLabelProvider(IInternalTreeModelViewer viewer) { fViewer = viewer; fViewer.addModelChangedListener(this); } @@ -210,20 +204,38 @@ public class TreeModelLabelProvider extends ColumnLabelProvider * @see org.eclipse.jface.viewers.BaseLabelProvider#dispose() */ public void dispose() { + Assert.isTrue(fViewer.getDisplay().getThread() == Thread.currentThread()); + fViewer.removeModelChangedListener(this); - synchronized (fUpdatesInProgress) { - Iterator updatesInProgress = fUpdatesInProgress.iterator(); - while (updatesInProgress.hasNext()) { - ILabelUpdate currentUpdate = (ILabelUpdate) updatesInProgress.next(); - currentUpdate.cancel(); - } + fViewer = null; + + List complete = null; + synchronized(this) { + complete = fComplete; + fComplete = null; + } + if (complete != null) { + for (Iterator itr = complete.iterator(); itr.hasNext();) { + ((ILabelUpdate)itr.next()).cancel(); + } + } + + Iterator updatesInProgress = fUpdatesInProgress.iterator(); + while (updatesInProgress.hasNext()) { + ILabelUpdate currentUpdate = (ILabelUpdate) updatesInProgress.next(); + currentUpdate.cancel(); } - synchronized (this) { - if (fPendingUpdatesJob != null) { - fPendingUpdatesJob.cancel(); - fPendingUpdatesJob = null; - } + + if (fPendingUpdatesRunnable != null) { + fPendingUpdatesRunnable = null; + } + for (Iterator itr = fPendingUpdates.values().iterator(); itr.hasNext();) { + List updateList = (List)itr.next(); + for (Iterator listItr = updateList.iterator(); listItr.hasNext();) { + ((LabelUpdate)listItr.next()).cancel(); + } } + fPendingUpdates.clear(); Iterator images = fImageCache.values().iterator(); while (images.hasNext()) { Image image = (Image) images.next(); @@ -248,11 +260,17 @@ public class TreeModelLabelProvider extends ColumnLabelProvider super.dispose(); } - public synchronized void update(ViewerCell cell) { + private boolean isDisposed() { + return fViewer == null; + } + + public void update(ViewerCell cell) { // NOT USED - the viewer updates each row instead } - public synchronized boolean update(TreePath elementPath) { + public boolean update(TreePath elementPath) { + Assert.isTrue(fViewer.getDisplay().getThread() == Thread.currentThread()); + cancelPathUpdates(elementPath); String[] visibleColumns = fViewer.getVisibleColumns(); @@ -265,17 +283,13 @@ public class TreeModelLabelProvider extends ColumnLabelProvider fPendingUpdates.put(presentation, updates); } updates.add(new LabelUpdate(fViewer.getInput(), elementPath, this, visibleColumns, fViewer.getPresentationContext())); - if (fPendingUpdatesJob != null) { - fPendingUpdatesJob.cancel(); - } - fPendingUpdatesJob = new UIJob(fViewer.getDisplay(), "Schedule Pending Label Updates") { //$NON-NLS-1$ - public IStatus runInUIThread(IProgressMonitor monitor) { - startRequests(this); - return Status.OK_STATUS; - } - }; - fPendingUpdatesJob.setSystem(true); - fPendingUpdatesJob.schedule(); + fPendingUpdatesRunnable = new Runnable() { + public void run() { + if (isDisposed()) return; + startRequests(this); + } + }; + fViewer.getDisplay().asyncExec(fPendingUpdatesRunnable); return true; } else { return false; @@ -284,15 +298,16 @@ public class TreeModelLabelProvider extends ColumnLabelProvider /** * Cancel any outstanding updates that are running for this element. + * @param elementPath Element to cancel updates for. */ - protected void cancelPathUpdates(TreePath elementPath) { - synchronized (fUpdatesInProgress) { - Iterator updatesInProgress = fUpdatesInProgress.iterator(); - while (updatesInProgress.hasNext()) { - ILabelUpdate currentUpdate = (ILabelUpdate) updatesInProgress.next(); - if (elementPath.equals(currentUpdate.getElementPath())) { - currentUpdate.cancel(); - } + private void cancelPathUpdates(TreePath elementPath) { + Assert.isTrue(fViewer.getDisplay().getThread() == Thread.currentThread()); + + Iterator updatesInProgress = fUpdatesInProgress.iterator(); + while (updatesInProgress.hasNext()) { + ILabelUpdate currentUpdate = (ILabelUpdate) updatesInProgress.next(); + if (elementPath.equals(currentUpdate.getElementPath())) { + currentUpdate.cancel(); } } } @@ -300,41 +315,46 @@ public class TreeModelLabelProvider extends ColumnLabelProvider /** * Sets the element's display information in the viewer. * + * @param path Element path. + * @param numColumns Number of columns in the data. + * @param labels Array of labels. The array cannot to be + * <code>null</code>, but values within the array may be. + * @param images Array of image descriptors, may be <code>null</code>. + * @param fontDatas Array of fond data objects, may be <code>null</code>. + * @param foregrounds Array of RGB values for foreground colors, may be + * <code>null</code>. + * @param backgrounds Array of RGB values for background colors, may be + * <code>null</code>. + * @param checked Whether given item should be checked. + * @param grayed Whether given item's checkbox shoudl be grayed. * @see ITreeModelLabelProviderTarget#setElementData(TreePath, int, String[], ImageDescriptor[], FontData[], RGB[], RGB[]) * @see ITreeModelCheckProviderTarget#setElementChecked(TreePath, boolean, boolean) */ - protected void setElementData(TreePath path, int numColumns, String[] labels, ImageDescriptor[] images, + void setElementData(TreePath path, int numColumns, String[] labels, ImageDescriptor[] images, FontData[] fontDatas, RGB[] foregrounds, RGB[] backgrounds, boolean checked, boolean grayed) { fViewer.setElementData(path, numColumns, labels, images, fontDatas, foregrounds, backgrounds); - - if (fViewer instanceof ITreeModelCheckProviderTarget) - ((ITreeModelCheckProviderTarget) fViewer).setElementChecked(path, checked, grayed); + fViewer.setElementChecked(path, checked, grayed); } - private void startRequests(UIJob updateJob) { - // Avoid calling providers inside a synchronized section. Instead - // copy the updates map into a new variable. - Map updates = null; - synchronized(this) { - if (updateJob == fPendingUpdatesJob) { - updates = fPendingUpdates; - fPendingUpdates = new HashMap(); - fPendingUpdatesJob = null; - } - } + private void startRequests(Runnable runnable) { + if (runnable != fPendingUpdatesRunnable) { + return; + } - if (updates != null) { - for (Iterator itr = updates.keySet().iterator(); itr.hasNext();) { - IElementLabelProvider presentation = (IElementLabelProvider)itr.next(); - List list = (List)updates.get(presentation); + if (!fPendingUpdates.isEmpty()) { + for (Iterator itr = fPendingUpdates.keySet().iterator(); itr.hasNext();) { + IElementLabelProvider provider = (IElementLabelProvider)itr.next(); + List list = (List)fPendingUpdates.get(provider); for (Iterator listItr = list.iterator(); listItr.hasNext();) { updateStarted((ILabelUpdate)listItr.next()); } - presentation.update( (ILabelUpdate[])list.toArray(new ILabelUpdate[list.size()]) ); + provider.update( (ILabelUpdate[])list.toArray(new ILabelUpdate[list.size()]) ); } } + fPendingUpdates.clear(); + fPendingUpdatesRunnable = null; } /** @@ -345,29 +365,27 @@ public class TreeModelLabelProvider extends ColumnLabelProvider * @param searchFullPath flag whether to look for the element in the full path * of the update */ - protected void cancelElementUpdates(Object element, boolean searchFullPath) { - synchronized (fUpdatesInProgress) { - Iterator updatesInProgress = fUpdatesInProgress.iterator(); - while (updatesInProgress.hasNext()) { - ILabelUpdate currentUpdate = (ILabelUpdate) updatesInProgress.next(); - - if (searchFullPath) { - if (element.equals(fViewer.getInput())) { - currentUpdate.cancel(); - } else { - TreePath updatePath = currentUpdate.getElementPath(); - for (int i = 0; i < updatePath.getSegmentCount(); i++) { - if (element.equals(updatePath.getSegment(i))) { - currentUpdate.cancel(); - break; // Exit the for loop, stay in the while loop - } - } - } + private void cancelElementUpdates(Object element, boolean searchFullPath) { + Iterator updatesInProgress = fUpdatesInProgress.iterator(); + while (updatesInProgress.hasNext()) { + ILabelUpdate currentUpdate = (ILabelUpdate) updatesInProgress.next(); + + if (searchFullPath) { + if (element.equals(fViewer.getInput())) { + currentUpdate.cancel(); } else { - if (element.equals(currentUpdate.getElement())) { - currentUpdate.cancel(); + TreePath updatePath = currentUpdate.getElementPath(); + for (int i = 0; i < updatePath.getSegmentCount(); i++) { + if (element.equals(updatePath.getSegment(i))) { + currentUpdate.cancel(); + break; // Exit the for loop, stay in the while loop + } } } + } else { + if (element.equals(currentUpdate.getElement())) { + currentUpdate.cancel(); + } } } } @@ -377,39 +395,40 @@ public class TreeModelLabelProvider extends ColumnLabelProvider * * @return presentation context */ - protected IPresentationContext getPresentationContext() { + private IPresentationContext getPresentationContext() { return fViewer.getPresentationContext(); } /** * A label update is complete. * - * @param update + * @param update Update that is to be completed. */ - protected synchronized void complete(ILabelUpdate update) { - if (update.isCanceled()){ - updateComplete(update); - } else { - if (fComplete == null) { - fComplete = new ArrayList(); - UIJob job = new UIJob(getDisplay(), "Label Updates") { //$NON-NLS-1$ - public IStatus runInUIThread(IProgressMonitor monitor) { - LabelUpdate[] updates = null; - synchronized (TreeModelLabelProvider.this) { - updates = (LabelUpdate[]) fComplete.toArray(new LabelUpdate[fComplete.size()]); - fComplete = null; - } - for (int i = 0; i < updates.length; i++) { - updates[i].update(); - } - return Status.OK_STATUS; - } - }; - job.setSystem(true); - job.schedule(10L); - } - fComplete.add(update); + synchronized void complete(ILabelUpdate update) { + if (fViewer == null) return; + + if (fComplete == null) { + fComplete = new LinkedList(); + fViewer.getDisplay().asyncExec(new Runnable() { + public void run() { + if (isDisposed()) return; + List updates = null; + synchronized (TreeModelLabelProvider.this) { + updates = fComplete; + fComplete = null; + } + for (Iterator itr = updates.iterator(); itr.hasNext();) { + LabelUpdate itrUpdate = (LabelUpdate)itr.next(); + if (itrUpdate.isCanceled()) { + updateComplete(itrUpdate); + } else { + itrUpdate.performUpdate(); + } + } + } + }); } + fComplete.add(update); } public void addLabelUpdateListener(ILabelUpdateListener listener) { @@ -423,50 +442,47 @@ public class TreeModelLabelProvider extends ColumnLabelProvider /** * Notification an update request has started * - * @param update + * @param update Update that was started */ void updateStarted(ILabelUpdate update) { - boolean begin = false; - synchronized (fUpdatesInProgress) { - begin = fUpdatesInProgress.isEmpty(); - fUpdatesInProgress.add(update); - } + Assert.isTrue(fViewer.getDisplay().getThread() == Thread.currentThread()); + + boolean begin = fUpdatesInProgress.isEmpty(); + fUpdatesInProgress.add(update); + if (begin) { - if (ModelContentProvider.DEBUG_UPDATE_SEQUENCE && ModelContentProvider.DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { + if (TreeModelContentProvider.DEBUG_UPDATE_SEQUENCE && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { System.out.println("LABEL SEQUENCE BEGINS"); //$NON-NLS-1$ } - notifyUpdate(ModelContentProvider.UPDATE_SEQUENCE_BEGINS, null); + notifyUpdate(TreeModelContentProvider.UPDATE_SEQUENCE_BEGINS, null); } - if (ModelContentProvider.DEBUG_UPDATE_SEQUENCE && ModelContentProvider.DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { + if (TreeModelContentProvider.DEBUG_UPDATE_SEQUENCE && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { System.out.println("\tBEGIN - " + update); //$NON-NLS-1$ } - notifyUpdate(ModelContentProvider.UPDATE_BEGINS, update); + notifyUpdate(TreeModelContentProvider.UPDATE_BEGINS, update); } /** * Notification an update request has completed * - * @param update + * @param update Update that completed. */ void updateComplete(ILabelUpdate update) { - boolean end = false; - synchronized (fUpdatesInProgress) { - fUpdatesInProgress.remove(update); - end = fUpdatesInProgress.isEmpty(); - } - if (ModelContentProvider.DEBUG_UPDATE_SEQUENCE && ModelContentProvider.DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { + fUpdatesInProgress.remove(update); + + if (TreeModelContentProvider.DEBUG_UPDATE_SEQUENCE && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { System.out.println("\tEND - " + update); //$NON-NLS-1$ } - notifyUpdate(ModelContentProvider.UPDATE_COMPLETE, update); - if (end) { - if (ModelContentProvider.DEBUG_UPDATE_SEQUENCE && ModelContentProvider.DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { + notifyUpdate(TreeModelContentProvider.UPDATE_COMPLETE, update); + if (fUpdatesInProgress.isEmpty()) { + if (TreeModelContentProvider.DEBUG_UPDATE_SEQUENCE && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { System.out.println("LABEL SEQUENCE ENDS"); //$NON-NLS-1$ } - notifyUpdate(ModelContentProvider.UPDATE_SEQUENCE_COMPLETE, null); + notifyUpdate(TreeModelContentProvider.UPDATE_SEQUENCE_COMPLETE, null); } } - protected void notifyUpdate(final int type, final ILabelUpdate update) { + private void notifyUpdate(final int type, final ILabelUpdate update) { if (!fLabelListeners.isEmpty()) { Object[] listeners = fLabelListeners.getListeners(); for (int i = 0; i < listeners.length; i++) { @@ -474,16 +490,16 @@ public class TreeModelLabelProvider extends ColumnLabelProvider SafeRunner.run(new ISafeRunnable() { public void run() throws Exception { switch (type) { - case ModelContentProvider.UPDATE_SEQUENCE_BEGINS: + case TreeModelContentProvider.UPDATE_SEQUENCE_BEGINS: listener.labelUpdatesBegin(); break; - case ModelContentProvider.UPDATE_SEQUENCE_COMPLETE: + case TreeModelContentProvider.UPDATE_SEQUENCE_COMPLETE: listener.labelUpdatesComplete(); break; - case ModelContentProvider.UPDATE_BEGINS: + case TreeModelContentProvider.UPDATE_BEGINS: listener.labelUpdateStarted(update); break; - case ModelContentProvider.UPDATE_COMPLETE: + case TreeModelContentProvider.UPDATE_COMPLETE: listener.labelUpdateComplete(update); break; } @@ -496,9 +512,8 @@ public class TreeModelLabelProvider extends ColumnLabelProvider } } - public void modelChanged(IModelDelta delta, IModelProxy proxy) { - delta.accept(fVisitor); + delta.accept(fCancelPendingUpdatesVisitor); } } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ViewerStateTracker.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ViewerStateTracker.java new file mode 100644 index 000000000..a116ca77a --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ViewerStateTracker.java @@ -0,0 +1,1585 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.viewers.model; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.ISafeRunnable; +import org.eclipse.core.runtime.ListenerList; +import org.eclipse.core.runtime.SafeRunner; +import org.eclipse.debug.internal.ui.DebugUIPlugin; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementCompareRequest; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoRequest; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDeltaVisitor; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IStateUpdateListener; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdateListener; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; +import org.eclipse.jface.viewers.ITreeSelection; +import org.eclipse.jface.viewers.TreePath; +import org.eclipse.jface.viewers.TreeSelection; +import org.eclipse.ui.IMemento; +import org.eclipse.ui.XMLMemento; + +/** + * Class containing logic to save and restore expanded state of the tree model + * viewer. + * <p> + * When the input to the viewer is changes, the tree model viewer attempts to + * save the expansion state of elements as well as currently selected element and + * scroll position. Each expanded element is queried for its memento and all the + * collected mementos are saved into a delta tree then serialized by the viewer.<br> + * When a new input is set to the viewer, the viewer compares the input's memento + * with the stored mementos and if a match is found, it attempts to restore the + * previous expansion state to the viewer. As elements are revealed and realized + * in the viewer, the element's memento is compared against the memento stored in + * the saved state delta. If matching elements are found in the delta, the expansion + * and selection state is then restored to those elements. + * </p><p> + * Additionally to saving restoring state on input change, the viewer also + * saves/restores elements' state when the model requests viewer to refresh model + * structure. Since the viewer items are matched to the model elements using items' + * indexes, inserting or removing elements in model can cause the expansion state + * of elements to shift after a refresh. To compensate for this, the viewer saves + * the elements before a refresh is performed into a delta, but without encoding + * elements using mementos. As the refresh of the tree progresses, the save state + * is restored to the tree and elements are expanded or collapsed as needed to + * compensate for changes in model structure. + * </p> + * @see TreeModelContentProvider + */ +class ViewerStateTracker { + + // Debugging flag. + static boolean DEBUG_STATE_SAVE_RESTORE = false; + + // State update type constants used in notifying listeners + static final int STATE_SAVE_SEQUENCE_BEGINS = 4; + static final int STATE_SAVE_SEQUENCE_COMPLETE = 5; + static final int STATE_RESTORE_SEQUENCE_BEGINS = 6; + static final int STATE_RESTORE_SEQUENCE_COMPLETE = 7; + + /** + * 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$ + + /** + * Collector of memento encoding requests. + */ + interface IElementMementoCollector { + + /** + * Adds the request to this manager. + * + * @param request to add + */ + public void addRequest(ElementMementoRequest request); + + /** + * Notification the request is complete. + * + * @param request that was completed + */ + public void requestComplete(ElementMementoRequest request); + + /** + * Process the queued requests. Accepts no more new requests. + */ + public void processReqeusts(); + + /** + * Cancels the requests in progress. + */ + public void cancel(); + } + + /** + * LRU cache for viewer states + */ + class LRUMap extends LinkedHashMap { + private static final long serialVersionUID = 1L; + + private int fMaxSize; + + LRUMap(int maxSize) { + super(); + fMaxSize = maxSize; + } + + protected boolean removeEldestEntry(Entry eldest) { + return size() > fMaxSize; + } + } + + /** + * Content provider that is using this state tracker. + */ + private TreeModelContentProvider fContentProvider; + + ViewerStateTracker(TreeModelContentProvider contentProvider) { + fContentProvider = contentProvider; + } + + /** + * Map of viewer states keyed by viewer input mementos + */ + private Map fViewerStates = new LRUMap(20); + + /** + * Pending viewer state to be restored + */ + private ModelDelta fPendingState = null; + + /** + * Flag indicating that the content provider is performing + * state restore operations. + */ + private boolean fInStateRestore = false; + + /** + * State update listeners + */ + private ListenerList fStateUpdateListeners = new ListenerList(); + + /** + * Postpone restoring REVEAL element until the current updates are complete. + * See bug 324100 + */ + protected PendingRevealDelta fPendingSetTopItem = null; + + /** + * Set of IMementoManager's that are currently saving state + */ + private Set fPendingStateSaves = new HashSet(); + + /** + * Used to queue a viewer input for state restore + */ + private Object fQueuedRestore = null; + + /** + * Object used to key compare requests in map. + */ + private static class CompareRequestKey { + + CompareRequestKey(TreePath path, IModelDelta delta) { + fPath = path; + fDelta = delta; + } + + TreePath fPath; + IModelDelta fDelta; + + public boolean equals(Object obj) { + if (obj instanceof CompareRequestKey) { + CompareRequestKey key = (CompareRequestKey) obj; + return key.fDelta.equals(fDelta) && key.fPath.equals(fPath); + } + return false; + } + + public int hashCode() { + return fDelta.hashCode() + fPath.hashCode(); + } + } + + /** + * Compare requests that are currently running. + */ + private Map fCompareRequestsInProgress = new LinkedHashMap(); + + + /** + * Cancels pending updates. + */ + void dispose() { + Assert.isTrue( fContentProvider.getViewer().getDisplay().getThread() == Thread.currentThread() ); + + for (Iterator itr = fPendingStateSaves.iterator(); itr.hasNext(); ) { + ((IElementMementoCollector)itr.next()).cancel(); + } + fStateUpdateListeners.clear(); + + for (Iterator itr = fCompareRequestsInProgress.values().iterator(); itr.hasNext();) { + ((ElementCompareRequest)itr.next()).cancel(); + } + fCompareRequestsInProgress.clear(); + + if (fPendingSetTopItem != null) { + fPendingSetTopItem.dispose(); + } + } + + /** + * Restores viewer state for the given input + * + * @param input + * viewer input + */ + private void startRestoreViewerState(final Object input) { + Assert.isTrue( fContentProvider.getViewer().getDisplay().getThread() == Thread.currentThread() ); + + fPendingState = null; + final IElementMementoProvider defaultProvider = ViewerAdapterService.getMementoProvider(input); + if (defaultProvider != null) { + // build a model delta representing expansion and selection state + final ModelDelta delta = new ModelDelta(input, IModelDelta.NO_CHANGE); + final XMLMemento inputMemento = XMLMemento.createWriteRoot("VIEWER_INPUT_MEMENTO"); //$NON-NLS-1$ + final IElementMementoCollector manager = new IElementMementoCollector() { + + private IElementMementoRequest fRequest; + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.debug.internal.ui.viewers.model.provisional.viewers + * . + * IMementoManager#requestComplete(org.eclipse.debug.internal.ui + * .viewers.model.provisional.IElementMementoRequest) + */ + public void requestComplete(ElementMementoRequest request) { + if (fContentProvider.isDisposed()) return; + + notifyStateUpdate(input, TreeModelContentProvider.UPDATE_COMPLETE, request); + + if (!request.isCanceled() && (request.getStatus() == null || request.getStatus().isOK())) { + XMLMemento keyMemento = (XMLMemento) delta.getElement(); + StringWriter writer = new StringWriter(); + try { + keyMemento.save(writer); + final String keyMementoString = writer.toString(); + ModelDelta stateDelta = (ModelDelta) fViewerStates.get(keyMementoString); + if (stateDelta != null) { + if (DEBUG_STATE_SAVE_RESTORE && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) { + System.out.println("STATE RESTORE INPUT COMARE ENDED : " + fRequest + " - MATCHING STATE FOUND"); //$NON-NLS-1$ //$NON-NLS-2$ + } + + // Process start of restore in an async cycle because we may still be inside inputChanged() + // call. I.e. the "input.equals(fContentProvider.getViewer().getInput())" test may fail. + fContentProvider.getViewer().getDisplay().asyncExec(new Runnable() { + public void run() { + if (!fContentProvider.isDisposed() && input.equals(fContentProvider.getViewer().getInput())) { + ModelDelta stateDelta2 = (ModelDelta) fViewerStates.remove(keyMementoString); + if (stateDelta2 != null) { + if (DEBUG_STATE_SAVE_RESTORE && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) { + System.out.println("STATE RESTORE BEGINS"); //$NON-NLS-1$ + System.out.println("\tRESTORE: " + stateDelta2.toString()); //$NON-NLS-1$ + notifyStateUpdate(input, STATE_RESTORE_SEQUENCE_BEGINS, null); + } + stateDelta2.setElement(input); + fPendingState = stateDelta2; + doInitialRestore(fPendingState); + } + } else { + if (DEBUG_STATE_SAVE_RESTORE && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) { + System.out.println("STATE RESTORE CANCELED."); //$NON-NLS-1$ + } + } + } + }); + } else { + if (DEBUG_STATE_SAVE_RESTORE && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) { + System.out.println("STATE RESTORE INPUT COMARE ENDED : " + fRequest + " - NO MATCHING STATE"); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + } catch (IOException e) { + DebugUIPlugin.log(e); + } + } else { + notifyStateUpdate(input, STATE_RESTORE_SEQUENCE_BEGINS, null); + } + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.debug.internal.ui.viewers.model.provisional.viewers + * .IMementoManager#processReqeusts() + */ + public void processReqeusts() { + notifyStateUpdate(input, STATE_RESTORE_SEQUENCE_BEGINS, null); + if (DEBUG_STATE_SAVE_RESTORE && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) { + System.out.println("STATE RESTORE INPUT COMARE BEGIN : " + fRequest); //$NON-NLS-1$ + } + notifyStateUpdate(input, TreeModelContentProvider.UPDATE_BEGINS, fRequest); + defaultProvider.encodeElements(new IElementMementoRequest[] { fRequest }); + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.debug.internal.ui.viewers.model.provisional.viewers + * . + * IMementoManager#addRequest(org.eclipse.debug.internal.ui.viewers + * .model.provisional.IElementMementoRequest) + */ + public void addRequest(ElementMementoRequest req) { + fRequest = req; + } + + public void cancel() { + // not used + } + + }; + manager.addRequest( + new ElementMementoRequest(fContentProvider, fContentProvider.getViewer().getInput(), manager, + delta.getElement(), fContentProvider.getViewerTreePath(delta), inputMemento, delta)); + manager.processReqeusts(); + } else { + if (DEBUG_STATE_SAVE_RESTORE && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) { + System.out.println("STATE RESTORE: No input memento provider"); //$NON-NLS-1$ + } + } + } + + /** + * Appends the state of the given subtree to the current pending delta. + * @param path Path to subtree to restore. + */ + void appendToPendingStateDelta(final TreePath path) { + if (fContentProvider.getViewer() == null) return; // Not initialized yet. + + if (DEBUG_STATE_SAVE_RESTORE && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) { + System.out.println("STATE APPEND BEGIN: " + path.getLastSegment()); //$NON-NLS-1$ + } + + // build a model delta representing expansion and selection state + final ModelDelta appendDeltaRoot = new ModelDelta(fContentProvider.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); + } + + if (!fContentProvider.getViewer().saveElementState(path, delta, IModelDelta.COLLAPSE | IModelDelta.EXPAND | IModelDelta.SELECT)) { + // Path to save the state was not found or there was no + // (expansion) state to save! Abort. + if (DEBUG_STATE_SAVE_RESTORE && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) { + System.out.println("STATE APPEND CANCEL: Element " + path.getLastSegment() + " not found."); //$NON-NLS-1$ //$NON-NLS-2$ + } + return; + } + + // 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 d, int depth) { + if ((d.getFlags() & IModelDelta.EXPAND) != 0) { + ((ModelDelta) d).setFlags(d.getFlags() | IModelDelta.CONTENT); + } + return true; + } + }); + + if (DEBUG_STATE_SAVE_RESTORE && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) { + System.out.println("\tAPPEND DELTA: " + 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 && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) { + System.out.println("\tAPPEND OUTSTANDING RESTORE: " + fPendingState); //$NON-NLS-1$ + } + + // If the append delta is generated for a sub-tree, copy the pending dela + // attributes into the pending delta. + if (path.getSegmentCount() > 0) { + fPendingState.accept( new IModelDeltaVisitor() { + public boolean visit(IModelDelta pendingDeltaNode, int depth) { + TreePath pendingDeltaPath = fContentProvider.getViewerTreePath(pendingDeltaNode); + if (path.startsWith(pendingDeltaPath, null)) + { + ModelDelta appendDelta = findDeltaForPath(appendDeltaRoot, pendingDeltaPath); + appendDelta.setFlags(pendingDeltaNode.getFlags()); + appendDelta.setChildCount(pendingDeltaNode.getChildCount()); + appendDelta.setIndex(pendingDeltaNode.getIndex()); + return true; + } + return false; + } + }); + } + + // Copy the pending state into the new appended state. + fPendingState.accept( new IModelDeltaVisitor() { + public boolean visit(IModelDelta pendingDeltaNode, int depth) { + // Skip 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 = findSubDeltaParent(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 && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) { + System.out.println("\tSKIPPED: " + 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; + } + } + + }); + } + + if (appendDeltaRoot.getChildDeltas().length > 0) { + // Set the new delta root as the pending state delta. + if (fPendingState == null) { + notifyStateUpdate(appendDeltaRoot.getElement(), STATE_RESTORE_SEQUENCE_BEGINS, null); + } + fPendingState = appendDeltaRoot; + if (DEBUG_STATE_SAVE_RESTORE && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) { + System.out.println("STATE APPEND COMPLETE " + fPendingState); //$NON-NLS-1$ + } + } else { + if (DEBUG_STATE_SAVE_RESTORE && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) { + System.out.println("STATE APPEND CANCELED: No Data"); //$NON-NLS-1$ + } + } + } + + /** + * Saves the viewer's state for the previous input. * @param oldInput + * @param input the {@link ModelDelta} input + */ + protected void saveViewerState(Object input) { + for (Iterator itr = fCompareRequestsInProgress.values().iterator(); itr.hasNext();) { + ((ElementCompareRequest) itr.next()).cancel(); + itr.remove(); + } + + IElementMementoProvider stateProvider = ViewerAdapterService.getMementoProvider(input); + if (stateProvider != null) { + if (DEBUG_STATE_SAVE_RESTORE && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) { + 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 && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) { + System.out.println("\tSAVE DELTA FROM VIEW:\n" + saveDeltaRoot); //$NON-NLS-1$ + } + + // check if pending restore reveal + if (fPendingSetTopItem != null) { + // set back the pending reveal flag + ModelDelta revealDelta = fPendingSetTopItem.getDelta(); + revealDelta.setFlags(revealDelta.getFlags() | IModelDelta.REVEAL); + + fPendingSetTopItem.dispose(); + + ModelDelta saveDeltaNode = findSubDeltaParent(saveDeltaRoot, revealDelta); + if (saveDeltaNode != null) { + clearRevealFlag(saveDeltaRoot); + boolean childFounded = false; + for (int i = 0; i < saveDeltaNode.getChildDeltas().length; i++) { + ModelDelta child = (ModelDelta)saveDeltaNode.getChildDeltas()[i]; + if (deltasEqual(child, revealDelta)) { + child.setFlags(child.getFlags() | IModelDelta.REVEAL); + childFounded = true; + break; + } + } + + // the node should be added if not found + if (!childFounded) { + saveDeltaNode.setChildCount(revealDelta.getParentDelta().getChildCount()); + copyIntoDelta(revealDelta, saveDeltaNode); + } + } else { + if (DEBUG_STATE_SAVE_RESTORE && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) { + System.out.println("\tSKIPPED: " + revealDelta.getElement()); //$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 && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) { + System.out.println("\tSAVE 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 = findSubDeltaParent(saveDeltaRoot, pendingDeltaNode); + if (saveDeltaNode != null && !isDeltaInParent(pendingDeltaNode, saveDeltaNode) + && pendingDeltaNode.getFlags() != IModelDelta.NO_CHANGE) { + // There should be only one delta element with + // the REVEAL flag in the entire save delta. The + // reveal flag in the pending delta trumps the one + // in the save delta because most likely the restore + // operation did not yet complete the reveal + // operation. + if ((pendingDeltaNode.getFlags() & IModelDelta.REVEAL) != 0) { + clearRevealFlag(saveDeltaRoot); + } + saveDeltaNode.setChildCount(pendingDeltaNode.getParentDelta().getChildCount()); + copyIntoDelta(pendingDeltaNode, saveDeltaNode); + } else { + if (DEBUG_STATE_SAVE_RESTORE && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) { + System.out.println("\tSKIPPED: " + 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 (saveDeltaRoot.getChildDeltas().length > 0) { + // encode delta with mementos in place of elements, in non-UI + // thread + encodeDelta(saveDeltaRoot, stateProvider); + } else { + if (DEBUG_STATE_SAVE_RESTORE && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) { + System.out.println("STATE SAVE CANCELED, NO DATA"); //$NON-NLS-1$ + } + } + } + } + + private void clearRevealFlag(ModelDelta saveRootDelta) { + IModelDeltaVisitor clearDeltaVisitor = new IModelDeltaVisitor() { + public boolean visit(IModelDelta delta, int depth) { + if ((delta.getFlags() & IModelDelta.REVEAL) != 0) { + ((ModelDelta) delta).setFlags(delta.getFlags() & ~IModelDelta.REVEAL); + } + return true; + } + }; + saveRootDelta.accept(clearDeltaVisitor); + } + + private ModelDelta findSubDeltaParent(ModelDelta destinationDeltaRoot, IModelDelta subDelta) { + // Create a path of elements to the sub-delta. + LinkedList deltaPath = new LinkedList(); + IModelDelta delta = subDelta; + while (delta.getParentDelta() != null) { + delta = delta.getParentDelta(); + deltaPath.addFirst(delta); + } + + // For each element in the path of the sub-delta, find the corresponding + // element in the destination delta + Iterator itr = deltaPath.iterator(); + // Skip the root element + itr.next(); + ModelDelta saveDelta = destinationDeltaRoot; + outer: while (itr.hasNext()) { + IModelDelta itrDelta = (IModelDelta) itr.next(); + for (int i = 0; i < saveDelta.getChildDeltas().length; i++) { + if (deltasEqual(saveDelta.getChildDeltas()[i], itrDelta)) { + saveDelta = (ModelDelta) saveDelta.getChildDeltas()[i]; + continue outer; + } + } + return null; + } + return saveDelta; + } + + private ModelDelta findDeltaForPath(ModelDelta root, TreePath path) { + ModelDelta delta = root; + for (int i = 0; i < path.getSegmentCount(); i++) { + delta = delta.getChildDelta(path.getSegment(i)); + if (delta == null) { + return null; + } + } + return delta; + } + + private boolean deltasEqual(IModelDelta d1, IModelDelta d2) { + // Note: don't compare the child count, because it is + // incorrect for nodes which have not been expanded yet. + return d1.getElement().equals(d2.getElement()) && d1.getIndex() == d2.getIndex(); + } + + private boolean isDeltaInParent(IModelDelta delta, ModelDelta destParent) { + for (int i = 0; i < destParent.getChildDeltas().length; i++) { + if (deltasEqual(destParent.getChildDeltas()[i], delta)) { + return true; + } + } + return false; + } + + private void copyIntoDelta(IModelDelta delta, ModelDelta destParent) { + ModelDelta newDelta = destParent.addNode(delta.getElement(), delta.getIndex(), delta.getFlags(), delta + .getChildCount()); + for (int i = 0; i < delta.getChildDeltas().length; i++) { + copyIntoDelta(delta.getChildDeltas()[i], newDelta); + } + } + + /** + * Encodes delta elements into mementos using the given provider. + * @param rootDelta the {@link ModelDelta} to encode + * @param defaultProvider the default provider to use when processing the given delta + * + */ + protected void encodeDelta(final ModelDelta rootDelta, final IElementMementoProvider defaultProvider) { + final Object input = rootDelta.getElement(); + final XMLMemento inputMemento = XMLMemento.createWriteRoot("VIEWER_INPUT_MEMENTO"); //$NON-NLS-1$ + final XMLMemento childrenMemento = XMLMemento.createWriteRoot("CHILDREN_MEMENTO"); //$NON-NLS-1$ + final IElementMementoCollector manager = new IElementMementoCollector() { + + /** + * list of memento fRequests + */ + private List fRequests = new ArrayList(); + + /** + * Flag indicating whether the encoding of delta has been canceled. + */ + private boolean fCanceled = false; + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.debug.internal.ui.viewers.model.provisional.viewers + * .IMementoManager + * #requestComplete(org.eclipse.debug.internal.ui.viewers + * .model.provisional.IElementMementoRequest) + */ + public void requestComplete(ElementMementoRequest request) { + Assert.isTrue( fContentProvider.getViewer().getDisplay().getThread() == Thread.currentThread() ); + + notifyStateUpdate(input, TreeModelContentProvider.UPDATE_COMPLETE, request); + if (DEBUG_STATE_SAVE_RESTORE && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) { + System.out.println("\tSTATE END: " + request); //$NON-NLS-1$ + } + + if (!request.isCanceled() && (request.getStatus() == null || request.getStatus().isOK())) { + boolean requestsComplted = false; + if (!fCanceled) { + fRequests.remove(request); + requestsComplted = fRequests.isEmpty(); + } + if (requestsComplted) { + XMLMemento keyMemento = (XMLMemento) rootDelta.getElement(); + StringWriter writer = new StringWriter(); + try { + keyMemento.save(writer); + fViewerStates.put(writer.toString(), rootDelta); + } catch (IOException e) { + DebugUIPlugin.log(e); + } + if (DEBUG_STATE_SAVE_RESTORE && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) { + System.out.println("STATE SAVE COMPLETED: " + rootDelta); //$NON-NLS-1$ + } + stateSaveComplete(input, this); + } + } else { + cancel(); + } + } + + public void cancel() { + Assert.isTrue( fContentProvider.getViewer().getDisplay().getThread() == Thread.currentThread() ); + + if (fCanceled) { + return; + } + + fCanceled = true; + Iterator iterator = fRequests.iterator(); + while (iterator.hasNext()) { + IElementMementoRequest req = (IElementMementoRequest) iterator.next(); + req.cancel(); + } + fRequests.clear(); + if (DEBUG_STATE_SAVE_RESTORE && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) { + System.out.println("STATE SAVE ABORTED: " + rootDelta.getElement()); //$NON-NLS-1$ + } + stateSaveComplete(input, this); + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.debug.internal.ui.viewers.model.provisional.viewers + * .IMementoManager#processReqeusts() + */ + public void processReqeusts() { + Assert.isTrue( fContentProvider.getViewer().getDisplay().getThread() == Thread.currentThread() ); + + Map providers = new HashMap(); + Iterator iterator = fRequests.iterator(); + while (iterator.hasNext()) { + IElementMementoRequest request = (IElementMementoRequest) iterator.next(); + notifyStateUpdate(input, TreeModelContentProvider.UPDATE_BEGINS, request); + IElementMementoProvider provider = ViewerAdapterService.getMementoProvider(request.getElement()); + if (provider == null) { + provider = defaultProvider; + } + List reqs = (List) providers.get(provider); + if (reqs == null) { + reqs = new ArrayList(); + providers.put(provider, reqs); + } + reqs.add(request); + } + iterator = providers.entrySet().iterator(); + while (iterator.hasNext()) { + Entry entry = (Entry) iterator.next(); + IElementMementoProvider provider = (IElementMementoProvider) entry.getKey(); + List reqs = (List) entry.getValue(); + provider.encodeElements((IElementMementoRequest[]) reqs.toArray(new IElementMementoRequest[reqs + .size()])); + } + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.debug.internal.ui.viewers.model.provisional.viewers + * .IMementoManager + * #addRequest(org.eclipse.debug.internal.ui.viewers. + * model.provisional.IElementMementoRequest) + */ + public void addRequest(ElementMementoRequest request) { + Assert.isTrue( fContentProvider.getViewer().getDisplay().getThread() == Thread.currentThread() ); + + fRequests.add(request); + } + + }; + IModelDeltaVisitor visitor = new IModelDeltaVisitor() { + public boolean visit(IModelDelta delta, int depth) { + // Add the CONTENT flag to all nodes with an EXPAND flag. + // During restoring, this flag is used as a marker indicating + // whether all the content of a given element has been + // retrieved. + if ((delta.getFlags() | IModelDelta.EXPAND) != 0) { + ((ModelDelta)delta).setFlags(delta.getFlags() | IModelDelta.CONTENT); + } + + // This is the root element, save the root element memento in 'inputMemento'. + if (delta.getParentDelta() == null) { + manager.addRequest(new ElementMementoRequest(fContentProvider, input, manager, + delta.getElement(), fContentProvider.getViewerTreePath(delta), inputMemento, + (ModelDelta) delta)); + } else { + // If this is another node element, save the memento to a children memento. + if (!(delta.getElement() instanceof XMLMemento)) { + manager.addRequest(new ElementMementoRequest(fContentProvider, input, manager, + delta.getElement(), fContentProvider.getViewerTreePath(delta), childrenMemento + .createChild("CHILD_ELEMENT"), (ModelDelta) delta)); //$NON-NLS-1$ + } + } + return true; + } + }; + rootDelta.accept(visitor); + stateSaveStarted(input, manager); + manager.processReqeusts(); + } + + /** + * Called when a state save is starting. + * @param input the {@link ModelDelta} input + * @param manager the manager to notify + */ + private void stateSaveStarted(Object input, IElementMementoCollector manager) { + Assert.isTrue( fContentProvider.getViewer().getDisplay().getThread() == Thread.currentThread() ); + + notifyStateUpdate(input, STATE_SAVE_SEQUENCE_BEGINS, null); + fPendingStateSaves.add(manager); + } + + /** + * Called when a state save is complete. + * @param input the {@link ModelDelta} input + * @param manager the manager to notify + */ + private void stateSaveComplete(Object input, IElementMementoCollector manager) { + Assert.isTrue( fContentProvider.getViewer().getDisplay().getThread() == Thread.currentThread() ); + + notifyStateUpdate(input, STATE_SAVE_SEQUENCE_COMPLETE, null); + fPendingStateSaves.remove(manager); + if (fQueuedRestore != null) { + Object temp = fQueuedRestore; + fQueuedRestore = null; + restoreViewerState(temp); + } + } + + /** + * Returns whether any state saving is in progress. + * + * @return whether any state saving is in progress + */ + private boolean isSavingState() { + Assert.isTrue( fContentProvider.getViewer().getDisplay().getThread() == Thread.currentThread() ); + + return !fPendingStateSaves.isEmpty(); + } + + /** + * Restores the viewer state unless a save is taking place. If a save is + * taking place, the restore is queued. + * + * @param input + * viewer input + */ + protected void restoreViewerState(final Object input) { + Assert.isTrue( fContentProvider.getViewer().getDisplay().getThread() == Thread.currentThread() ); + + fPendingState = null; + if (isSavingState()) { + fQueuedRestore = input; + } else { + startRestoreViewerState(input); + } + } + + + public void cancelRestore(final TreePath path, final int flags) { + if (fInStateRestore) { + // If we are currently processing pending state already, ignore + // cancelRestore requests. These requests may be triggered in the viewer + // by changes to the tree state (Bug 295585). + return; + } + + if ((flags & IModelDelta.REVEAL) != 0 && fPendingSetTopItem != null) { + fPendingSetTopItem.dispose(); + return; + } + + // Nothing else to do + if (fPendingState == null) { + return; + } + + if ((flags & (IModelDelta.SELECT | IModelDelta.REVEAL)) != 0) { + // If we're canceling reveal and this is waiting for updates to complete + // then just cancel it and return + + // If we're canceling select or reveal, cancel it for all of pending deltas + final int mask = flags & (IModelDelta.SELECT | IModelDelta.REVEAL); + fPendingState.accept(new IModelDeltaVisitor() { + public boolean visit(IModelDelta delta, int depth) { + int deltaFlags = delta.getFlags(); + int newFlags = deltaFlags & ~mask; + if (DEBUG_STATE_SAVE_RESTORE && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) { + if (deltaFlags != newFlags) { + System.out.println("\tCANCEL: " + delta.getElement() + "(" + Integer.toHexString(deltaFlags & mask) + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + } + ((ModelDelta)delta).setFlags(newFlags); + return true; + } + }); + } + if ((flags & ~(IModelDelta.SELECT | IModelDelta.REVEAL)) != 0) { + final int mask = flags & ~(IModelDelta.SELECT | IModelDelta.REVEAL); + // For other flags (EXPAND/COLLAPSE), cancel only from the matching path. + fPendingState.accept(new IModelDeltaVisitor() { + public boolean visit(IModelDelta delta, int depth) { + if (depth < path.getSegmentCount()) { + // Descend until we reach a matching depth. + TreePath deltaPath = fContentProvider.getViewerTreePath(delta); + if (path.startsWith(deltaPath, null)) { + return true; + } else { + return false; + } + } + else if (depth == path.getSegmentCount()) { + TreePath deltaPath = fContentProvider.getViewerTreePath(delta); + if (deltaPath.equals(path)) { + int deltaFlags = delta.getFlags(); + int newFlags = deltaFlags & ~mask; + if (DEBUG_STATE_SAVE_RESTORE && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) { + if (deltaFlags != newFlags) { + System.out.println("\tCANCEL: " + delta.getElement() + "(" + Integer.toHexString(deltaFlags & mask) + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + } + ((ModelDelta)delta).setFlags(newFlags); + if ((flags & IModelDelta.EXPAND) != 0) { + // Descend delta to clear the EXPAND flags of a canceled expand + return true; + } + } + return false; + } else { + // We're clearing out flags of a matching sub-tree + // assert (flags & IModelDelta.EXPAND) != 0; + + if (DEBUG_STATE_SAVE_RESTORE && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) { + if (delta.getFlags() != IModelDelta.NO_CHANGE) { + System.out.println("\tCANCEL: " + delta.getElement() + "(" + Integer.toHexString(delta.getFlags()) + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + } + ((ModelDelta)delta).setFlags(IModelDelta.NO_CHANGE); + return true; + } + } + }); + } + } + + + /** + * Perform any restoration required for the given tree path. + * <p> + * This method is called after every viewer update completion to continue + * restoring the expansion state that was previously saved. + * + * @param path the tree path to update + * @param modelIndex the index in the current model + * @param knowsHasChildren if the content provider knows it has children already + * @param knowsChildCount if the content provider knows the current child count already + * @param checkChildrenRealized if any realized children should be checked or not + */ + void restorePendingStateOnUpdate(final TreePath path, final int modelIndex, final boolean knowsHasChildren, + final boolean knowsChildCount, final boolean checkChildrenRealized) + { + Assert.isTrue( fContentProvider.getViewer().getDisplay().getThread() == Thread.currentThread() ); + + if (fPendingState == null) { + return; + } + + IModelDeltaVisitor visitor = new IModelDeltaVisitor() { + public boolean visit(final IModelDelta delta, int depth) { + + Object element = delta.getElement(); + Object potentialMatch = depth != 0 ? path.getSegment(depth - 1) : fContentProvider.getViewer().getInput(); + // Only process if the depth in the delta matches the tree path. + if (depth == path.getSegmentCount()) { + if (element instanceof IMemento) { + IElementMementoProvider provider = ViewerAdapterService.getMementoProvider(potentialMatch); + if (provider == null) { + provider = ViewerAdapterService.getMementoProvider(fContentProvider.getViewer().getInput()); + } + if (provider != null) { + CompareRequestKey key = new CompareRequestKey(path, delta); + ElementCompareRequest existingRequest = (ElementCompareRequest) fCompareRequestsInProgress + .get(key); + if (existingRequest != null) { + // Check all the running compare updates for a + // matching tree path. + // 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( + fContentProvider, fContentProvider.getViewer().getInput(), potentialMatch, path, + (IMemento) element, (ModelDelta) delta, modelIndex, knowsHasChildren, + knowsChildCount, checkChildrenRealized); + fCompareRequestsInProgress.put(key, compareRequest); + if (DEBUG_STATE_SAVE_RESTORE && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) { + System.out.println("\tSTATE BEGIN: " + compareRequest); //$NON-NLS-1$ + } + notifyStateUpdate(element, TreeModelContentProvider.UPDATE_BEGINS, compareRequest); + provider.compareElements(new IElementCompareRequest[] { compareRequest }); + } + } + } else if (element.equals(potentialMatch)) { + // Element comparison already succeeded, and it matches + // our element. + // Call restore with delta to process the delta flags. + restorePendingStateNode((ModelDelta) delta, knowsHasChildren, knowsChildCount, checkChildrenRealized); + } + return false; + } + // Only follow the paths that match the delta. + return element.equals(potentialMatch); + } + }; + + try { + fInStateRestore = true; + fPendingState.accept(visitor); + } + finally { + fInStateRestore = false; + } + checkIfRestoreComplete(); + } + + /** + * Checks whether restoring pending state is already complete. + */ + void checkIfRestoreComplete() { + Assert.isTrue( fContentProvider.getViewer().getDisplay().getThread() == Thread.currentThread() ); + + if (fPendingState == null) { + return; + } + + /** + * Used to determine when restoration delta has been processed + */ + class CheckState implements IModelDeltaVisitor { + private boolean complete = true; + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.debug.internal.ui.viewers.provisional.IModelDeltaVisitor + * #visit(org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta, + * int) + */ + public boolean visit(IModelDelta delta, int depth) { + // Filster out the CONTENT flags from the delta flags, the content + // flag is only used as a marker indicating that all the sub-elements + // of a given delta have been retrieved. + int flags = (delta.getFlags() & ~IModelDelta.CONTENT); + + if (flags != IModelDelta.NO_CHANGE) { + IModelDelta parentDelta = delta.getParentDelta(); + // Remove the delta if : + // - The parent delta has no more flags on it (the content flag is removed as well), + // which means that parent element's children have been completely exposed. + // - There are no more pending updates for the element. + // - If element is a memento, there are no state requests pending. + if (parentDelta != null && parentDelta.getFlags() == IModelDelta.NO_CHANGE) { + TreePath deltaPath = fContentProvider.getViewerTreePath(delta); + if ( !fContentProvider.areElementUpdatesPending(deltaPath) && + (!(delta.getElement() instanceof IMemento) || !areMementoUpdatesPending(delta)) ) + { + removeDelta(delta); + return false; + } + } + + if (flags != IModelDelta.REVEAL || (delta.getElement() instanceof IMemento)) { + complete = false; + return false; + } + } + return true; + } + + public boolean isComplete() { + return complete; + } + + private boolean areMementoUpdatesPending(IModelDelta delta) { + for (Iterator itr = fCompareRequestsInProgress.keySet().iterator(); itr.hasNext();) { + CompareRequestKey key = (CompareRequestKey) itr.next(); + if (delta.getElement().equals(key.fDelta.getElement())) { + return true; + } + } + return false; + } + + private void removeDelta(IModelDelta delta) { + if (DEBUG_STATE_SAVE_RESTORE && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) { + System.out.println("\tRESTORE 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; + } + }); + + } + } + + CheckState state = new CheckState(); + fPendingState.accept(state); + if (state.isComplete()) { + // notify restore complete if REVEAL was restored also, otherwise + // postpone until then. + if (fPendingSetTopItem == null) { + if (DEBUG_STATE_SAVE_RESTORE && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) { + System.out.println("STATE RESTORE COMPELTE: " + fPendingState); //$NON-NLS-1$ + } + + notifyStateUpdate(fPendingState.getElement(), STATE_RESTORE_SEQUENCE_COMPLETE, null); + } + + fPendingState = null; + } + } + + /** + * Restores the pending state in the given delta node. This method is called + * once the state tracker has found the element which matches the element in + * the given delta node. + * @param delta the {@link ModelDelta} to restore from + * @param knowsHasChildren if the content provider has computed its children + * @param knowsChildCount if the content provider has already computed the child count + * @param checkChildrenRealized if any realized children should be checked + */ + void restorePendingStateNode(final ModelDelta delta, boolean knowsHasChildren, boolean knowsChildCount, boolean checkChildrenRealized) { + final TreePath treePath = fContentProvider.getViewerTreePath(delta); + final IInternalTreeModelViewer viewer = fContentProvider.getViewer(); + + // Attempt to expand the node only if the children are known. + if (knowsHasChildren) { + if ((delta.getFlags() & IModelDelta.EXPAND) != 0) { + if (DEBUG_STATE_SAVE_RESTORE && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) { + System.out.println("\tRESTORE 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 && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) { + System.out.println("\tRESTORE COLLAPSE: " + treePath.getLastSegment()); //$NON-NLS-1$ + } + // Check auto-expand before collapsing an element (bug 335734) + int autoexpand = fContentProvider.getViewer().getAutoExpandLevel(); + if (autoexpand != ITreeModelViewer.ALL_LEVELS && autoexpand < (treePath.getSegmentCount() + 1)) { + fContentProvider.getViewer().setExpandedState(treePath, false); + } + delta.setFlags(delta.getFlags() & ~IModelDelta.COLLAPSE); + } + } + + if ((delta.getFlags() & IModelDelta.SELECT) != 0) { + delta.setFlags(delta.getFlags() & ~IModelDelta.SELECT); + if (DEBUG_STATE_SAVE_RESTORE && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) { + System.out.println("\tRESTORE SELECT: " + treePath.getLastSegment()); //$NON-NLS-1$ + } + ITreeSelection currentSelection = (ITreeSelection)viewer.getSelection(); + if (currentSelection == null || currentSelection.isEmpty()) { + viewer.setSelection(new TreeSelection(treePath), false, false); + } else { + TreePath[] currentPaths = currentSelection.getPaths(); + boolean pathInSelection = false; + for (int i = 0; i < currentPaths.length; i++) { + if (currentPaths[i].equals(treePath)) { + pathInSelection = true; + break; + } + } + // Only set the selection if the element is not yet in + // selection. Otherwise the setSelection() call will + // update selection listeners needlessly. + if (!pathInSelection) { + TreePath[] newPaths = new TreePath[currentPaths.length + 1]; + System.arraycopy(currentPaths, 0, newPaths, 0, currentPaths.length); + newPaths[newPaths.length - 1] = treePath; + viewer.setSelection(new TreeSelection(newPaths), false, false); + } + } + } + + if ((delta.getFlags() & IModelDelta.REVEAL) != 0) { + delta.setFlags(delta.getFlags() & ~IModelDelta.REVEAL); + // Look for the reveal flag in the child deltas. If + // A child delta has the reveal flag, do not set the + // top element yet. + boolean setTopItem = true; + IModelDelta[] childDeltas = delta.getChildDeltas(); + for (int i = 0; i < childDeltas.length; i++) { + IModelDelta childDelta = childDeltas[i]; + int modelIndex = childDelta.getIndex(); + if (modelIndex >= 0 && (childDelta.getFlags() & IModelDelta.REVEAL) != 0) { + setTopItem = false; + } + } + + if (setTopItem) { + Assert.isTrue(fPendingSetTopItem == null); + + fPendingSetTopItem = new PendingRevealDelta(treePath, delta); + viewer.addViewerUpdateListener(fPendingSetTopItem); + } + } + + // If we know the child count of the element, look for the reveal + // flag in the child deltas. For the children with reveal flag start + // a new update. + // If the child delta's index is out of range, strip the reveal flag + // since it is no longer applicable. + if (knowsChildCount) { + int childCount = viewer.getChildCount(treePath); + if (childCount >= 0) { + ModelDelta[] childDeltas = (ModelDelta[])delta.getChildDeltas(); + for (int i = 0; i < childDeltas.length; i++) { + ModelDelta childDelta = childDeltas[i]; + int modelIndex = childDelta.getIndex(); + if (modelIndex >= 0 && (childDelta.getFlags() & IModelDelta.REVEAL) != 0) { + if (modelIndex < childCount) { + fContentProvider.doUpdateElement(treePath, modelIndex); + } else { + childDelta.setFlags(childDelta.getFlags() & ~IModelDelta.REVEAL); + } + } + } + } + } + + // 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 && + !fContentProvider.areChildrenUpdatesPending(treePath) && + fContentProvider.getViewer().getElementChildrenRealized(treePath)) || + (knowsHasChildren && !viewer.getHasChildren(treePath)) ) + { + if (DEBUG_STATE_SAVE_RESTORE && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) { + System.out.println("\tRESTORE CONTENT: " + treePath.getLastSegment()); //$NON-NLS-1$ + } + delta.setFlags(delta.getFlags() & ~IModelDelta.CONTENT); + } + } + + /** + * Utility that reveals the saved top item in the viewer. It listens for + * all content updates to complete in order to avoid having the desired top item + * scroll out as view content is filled in. + * <br> + * Revealing some elements can trigger expanding some of elements + * that have been just revealed. Therefore, we have to check one + * more time after the new triggered updates are completed if we + * have to set again the top index + */ + private class PendingRevealDelta implements IViewerUpdateListener { + + private final TreePath fPathToReveal; + private final ModelDelta fRevealDelta; + + PendingRevealDelta(TreePath pathToReveal, ModelDelta revealDelta) { + fPathToReveal = pathToReveal; + fRevealDelta = revealDelta; + } + + /** + * Counter that tracks how many time the viewer updates were completed. + */ + private int fCounter = 0; + private Object fModelInput = fPendingState.getElement(); + + public void viewerUpdatesComplete() { + Assert.isTrue( fContentProvider.getViewer().getDisplay().getThread() == Thread.currentThread() ); + + IInternalTreeModelViewer viewer = fContentProvider.getViewer(); + if (viewer == null || fPendingSetTopItem != this) { + return; + } + + TreePath topPath = viewer.getTopElementPath(); + if (!fPathToReveal.equals(topPath)) { + TreePath parentPath = fPathToReveal.getParentPath(); + int index = viewer.findElementIndex(parentPath, fPathToReveal.getLastSegment()); + if (index >= 0) { + if (DEBUG_STATE_SAVE_RESTORE && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) { + System.out.println("\tRESTORE REVEAL: " + fPathToReveal.getLastSegment()); //$NON-NLS-1$ + } + viewer.reveal(parentPath, index); + + } + } + + fCounter++; + // in case the pending state was already set to null, we assume that + // all others elements are restored, so we don't expect that REVEAL will + // trigger other updates + if (fCounter > 1 || fPendingState == null) { + dispose(); + } + } + + public void viewerUpdatesBegin() {} + public void updateStarted(IViewerUpdate update) {} + public void updateComplete(IViewerUpdate update) {} + + /** + * Returns delta that is used to reveal the item. + * @return delta to be revealed. + */ + public ModelDelta getDelta() { + return fRevealDelta; + } + + /** + * Resets the item + */ + public void dispose() { + // top item is set + fPendingSetTopItem = null; + + IInternalTreeModelViewer viewer = fContentProvider.getViewer(); + if (viewer == null) return; + + // remove myself as viewer update listener + viewer.removeViewerUpdateListener(this); + + if (fPendingState == null) { + if (DEBUG_STATE_SAVE_RESTORE && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) { + System.out.println("STATE RESTORE COMPELTE: " + fPendingState); //$NON-NLS-1$ + } + notifyStateUpdate(fModelInput, STATE_RESTORE_SEQUENCE_COMPLETE, null); + } else { + checkIfRestoreComplete(); + } + } + + } + + /** + * Restore selection/expansion based on items already in the viewer + * @param delta the {@link ModelDelta} to restore from + */ + protected void doInitialRestore(ModelDelta delta) { + // Find the reveal delta and mark nodes on its path + // to reveal as elements are updated. + markRevealDelta(delta); + + // Restore visible items. + // Note (Pawel Piech): the initial list of items is normally + // empty, so in most cases the code below does not do anything. + // Instead doRestore() is called when various updates complete. + int count = fContentProvider.getViewer().getChildCount(TreePath.EMPTY); + for (int i = 0; i < count; i++) { + Object data = fContentProvider.getViewer().getChildElement(TreePath.EMPTY, i); + if (data != null) { + restorePendingStateOnUpdate(new TreePath(new Object[]{data}), i, false, false, false); + } + } + + } + + /** + * Finds the delta with the reveal flag, then it walks up this + * delta and marks all the parents of it with the reveal flag. + * These flags are then used by the restore logic to restore + * and reveal all the nodes leading up to the element that should + * be ultimately at the top. + * @param rootDelta Delta to search + * @return The node just under the rootDelta which contains + * the reveal flag. <code>null</code> if no reveal flag was found. + */ + private ModelDelta markRevealDelta(ModelDelta rootDelta) { + final ModelDelta[] revealDelta = new ModelDelta[1]; + IModelDeltaVisitor visitor = new IModelDeltaVisitor() { + public boolean visit(IModelDelta delta, int depth) { + if ( (delta.getFlags() & IModelDelta.REVEAL) != 0) { + revealDelta[0] = (ModelDelta)delta; + } + // Keep recursing only if we haven't found our delta yet. + return revealDelta[0] == null; + } + }; + + rootDelta.accept(visitor); + if (revealDelta[0] != null) { + ModelDelta parentDelta = (ModelDelta)revealDelta[0].getParentDelta(); + while(parentDelta.getParentDelta() != null) { + revealDelta[0] = parentDelta; + revealDelta[0].setFlags(revealDelta[0].getFlags() | IModelDelta.REVEAL); + parentDelta = (ModelDelta)parentDelta.getParentDelta(); + } + } + return revealDelta[0]; + } + + /** + * Builds a delta with the given root delta for expansion/selection state. + * + * @param delta + * root delta + */ + private void buildViewerState(ModelDelta delta) { + IInternalTreeModelViewer viewer = fContentProvider.getViewer(); + viewer.saveElementState(TreeModelContentProvider.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. + TreePath topElementPath = viewer.getTopElementPath(); + if (topElementPath != null) { + ModelDelta parentDelta = delta; + TreePath parentPath = TreeModelContentProvider.EMPTY_TREE_PATH; + for (int i = 0; i < topElementPath.getSegmentCount(); i++) { + Object element = topElementPath.getSegment(i); + int index = viewer.findElementIndex(parentPath, element); + ModelDelta childDelta = parentDelta.getChildDelta(element); + if (childDelta == null) { + parentDelta = parentDelta.addNode(element, index, IModelDelta.NO_CHANGE); + } else { + parentDelta = childDelta; + } + parentPath = parentPath.createChildPath(element); + } + parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.REVEAL); + } + } + + /** + * Cancels any outstanding compare requests for given element and its children. + * @param path Path of element to cancel updates for. + */ + void cancelStateSubtreeUpdates(TreePath path) { + for (Iterator itr = fCompareRequestsInProgress.keySet().iterator(); itr.hasNext();) { + CompareRequestKey key = (CompareRequestKey) itr.next(); + if (key.fPath.startsWith(path, null)) { + ElementCompareRequest compareRequest = (ElementCompareRequest) fCompareRequestsInProgress.get(key); + compareRequest.cancel(); + itr.remove(); + } + } + } + + void compareFinished(ElementCompareRequest request, ModelDelta delta) { + notifyStateUpdate(request.getViewerInput(), TreeModelContentProvider.UPDATE_COMPLETE, request); + if (DEBUG_STATE_SAVE_RESTORE && TreeModelContentProvider.DEBUG_TEST_PRESENTATION_ID(fContentProvider.getPresentationContext())) { + System.out.println("\tSTATE END: " + request + " = " + false); //$NON-NLS-1$ //$NON-NLS-2$ + } + + fCompareRequestsInProgress.remove(new CompareRequestKey(request.getElementPath(), delta)); + if (!request.isCanceled()) { + if (request.isEqual()) { + delta.setElement(request.getElement()); + restorePendingStateNode(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 of the element that it was compared + // against. If this is the case, strip the reveal flag from + // the delta as it is most likely not applicable anymore. + if ((delta.getFlags() & IModelDelta.REVEAL) != 0 && delta.getIndex() == request.getModelIndex()) { + delta.setFlags(delta.getFlags() & ~IModelDelta.REVEAL); + } + } + } + checkIfRestoreComplete(); + } + + + void addStateUpdateListener(IStateUpdateListener listener) { + fStateUpdateListeners.add(listener); + } + + void removeStateUpdateListener(IStateUpdateListener listener) { + fStateUpdateListeners.remove(listener); + } + + void notifyStateUpdate(final Object input, final int type, final IViewerUpdate update) { + if (!fStateUpdateListeners.isEmpty()) { + Object[] listeners = fStateUpdateListeners.getListeners(); + for (int i = 0; i < listeners.length; i++) { + final IStateUpdateListener listener = (IStateUpdateListener) listeners[i]; + SafeRunner.run(new ISafeRunnable() { + public void run() throws Exception { + switch (type) { + case STATE_SAVE_SEQUENCE_BEGINS: + listener.stateSaveUpdatesBegin(input); + break; + case STATE_SAVE_SEQUENCE_COMPLETE: + listener.stateSaveUpdatesComplete(input); + break; + case STATE_RESTORE_SEQUENCE_BEGINS: + listener.stateRestoreUpdatesBegin(input); + break; + case STATE_RESTORE_SEQUENCE_COMPLETE: + listener.stateRestoreUpdatesComplete(input); + break; + case TreeModelContentProvider.UPDATE_BEGINS: + listener.stateUpdateStarted(input, update); + break; + case TreeModelContentProvider.UPDATE_COMPLETE: + listener.stateUpdateComplete(input, update); + break; + } + } + + public void handleException(Throwable exception) { + DebugUIPlugin.log(exception); + } + }); + } + } + } +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ViewerUpdateMonitor.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ViewerUpdateMonitor.java index 9ba15c551..267cba74b 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ViewerUpdateMonitor.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ViewerUpdateMonitor.java @@ -11,24 +11,22 @@ *******************************************************************************/ package org.eclipse.debug.internal.ui.viewers.model; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.debug.internal.core.commands.Request; import org.eclipse.debug.internal.ui.viewers.AsynchronousSchedulingRuleFactory; import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider; import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer; import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; import org.eclipse.jface.viewers.TreePath; -import org.eclipse.ui.progress.WorkbenchJob; +import org.eclipse.swt.widgets.Display; /** * @since 3.3 */ public abstract class ViewerUpdateMonitor extends Request implements IViewerUpdate { - private ModelContentProvider fContentProvider; + private TreeModelContentProvider fContentProvider; /** * Element's tree path @@ -71,42 +69,23 @@ public abstract class ViewerUpdateMonitor extends Request implements IViewerUpda */ private IPresentationContext fContext; - - protected WorkbenchJob fViewerUpdateJob; - /** * Constructs an update for the given content provider * * @param contentProvider content provider + * @param viewerInput Viewer input for update * @param elementPath path to associated model element - empty for root element * @param element associated model element + * @param elementContentProvider Content provider for this update. + * @param context Presentation contest for this update */ - public ViewerUpdateMonitor(ModelContentProvider contentProvider, Object viewerInput, TreePath elementPath, Object element, IElementContentProvider elementContentProvider, IPresentationContext context) { + public ViewerUpdateMonitor(TreeModelContentProvider contentProvider, Object viewerInput, TreePath elementPath, Object element, IElementContentProvider elementContentProvider, IPresentationContext context) { fContext = context; fViewerInput = viewerInput; fElementContentProvider = elementContentProvider; fContentProvider = contentProvider; fElement = element; fElementPath = elementPath; - // serialize updates per viewer - fViewerUpdateJob = new WorkbenchJob(contentProvider.getViewer().getDisplay(), "Asynchronous viewer update") { //$NON-NLS-1$ - public IStatus runInUIThread(IProgressMonitor monitor) { - // necessary to check if viewer is disposed - try { - if (!isCanceled() && !getContentProvider().isDisposed()) { - IStatus status = getStatus(); - if (status == null || status.isOK()) { - performUpdate(); - } - } - } finally { - getContentProvider().updateComplete(ViewerUpdateMonitor.this); - } - return Status.OK_STATUS; - } - }; - fViewerUpdateJob.setRule(getUpdateSchedulingRule()); - fViewerUpdateJob.setSystem(true); } /** @@ -123,7 +102,7 @@ public abstract class ViewerUpdateMonitor extends Request implements IViewerUpda * * @return the model content provider this update is being performed for */ - protected ModelContentProvider getContentProvider() { + protected TreeModelContentProvider getContentProvider() { return fContentProvider; } @@ -152,18 +131,14 @@ public abstract class ViewerUpdateMonitor extends Request implements IViewerUpda /** * Returns whether this request is done yet. * - * @return + * @return True if this update is done. */ protected synchronized boolean isDone() { return fDone; } protected void scheduleViewerUpdate() { - if(!isCanceled()) { - fViewerUpdateJob.schedule(); - } else { - getContentProvider().updateComplete(this); - } + getContentProvider().scheduleViewerUpdate(this); } /** @@ -205,6 +180,9 @@ public abstract class ViewerUpdateMonitor extends Request implements IViewerUpda /** * Returns whether this update or any coalesced updates is for an * element at the given path. + * @param path Element path to check. + * @return True if this update contains the given update path. + * * @since 3.6 */ abstract boolean containsUpdate(TreePath path); @@ -212,7 +190,7 @@ public abstract class ViewerUpdateMonitor extends Request implements IViewerUpda /** * Starts this request. Subclasses must override startRequest(). */ - final void start() { + protected void start() { synchronized (this) { if (fStarted) { return; @@ -273,4 +251,51 @@ public abstract class ViewerUpdateMonitor extends Request implements IViewerUpda public boolean isDelegated() { return fIsDelegated; } + + public boolean equals(Object obj) { + if (obj instanceof ViewerUpdateMonitor) { + return doEquals((ViewerUpdateMonitor)obj); + } + return false; + } + + public int hashCode() { + return doHashCode(); + } + + /** + * Checks whether the given update is equal as this. The update is equal if it's + * the same type of update and its updating the same elements. + * @param update Update to compare to. + * @return True if the given update is equals + * @since 3.8 + */ + abstract protected boolean doEquals(ViewerUpdateMonitor update); + + /** + * Calculates the hash code of the given update using the same parameters as doEquals(). + * @return Update's hash code. + * @since 3.8 + */ + abstract protected int doHashCode(); + + /** + * Executes the given runnable in the UI thread. If method is called in + * UI thread, then runnable is executed immediately, otherwise it's executed + * using <code>Display.asyncExec()</code>. Runnable is not executed if update is + * canceled or content provider is disposed. + * @since 3.8 + */ + protected void execInDisplayThread(Runnable runnable) { + ITreeModelViewer viewer = getContentProvider().getViewer(); + if (viewer != null && !isCanceled()) { + Display display = viewer.getDisplay(); + if (Thread.currentThread() == display.getThread()) { + runnable.run(); + } else { + display.asyncExec(runnable); + } + } + } + } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/VirtualCopyToClipboardActionDelegate.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/VirtualCopyToClipboardActionDelegate.java index abccaae9b..bc41949c5 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/VirtualCopyToClipboardActionDelegate.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/VirtualCopyToClipboardActionDelegate.java @@ -7,45 +7,117 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Wind River Systems - refactored on top of VirtualTreeModelViewer *******************************************************************************/ package org.eclipse.debug.internal.ui.viewers.model; import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.List; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.debug.internal.core.IInternalDebugCoreConstants; import org.eclipse.debug.internal.ui.DebugUIPlugin; import org.eclipse.debug.internal.ui.actions.AbstractDebugActionDelegate; import org.eclipse.debug.internal.ui.actions.ActionMessages; -import org.eclipse.debug.internal.ui.viewers.model.InternalTreeModelViewer.VirtualElement; -import org.eclipse.debug.internal.ui.viewers.model.InternalTreeModelViewer.VirtualModel; +import org.eclipse.debug.internal.ui.elements.adapters.VariableColumnPresentation; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDeltaVisitor; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdateListener; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IVirtualItemValidator; +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.TreeModelViewer; +import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualItem; +import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualItem.Index; +import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualTreeModelViewer; import org.eclipse.debug.ui.IDebugView; import org.eclipse.jface.action.IAction; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.dialogs.ProgressMonitorDialog; import org.eclipse.jface.operation.IRunnableWithProgress; -import org.eclipse.jface.viewers.ContentViewer; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.TreePath; +import org.eclipse.swt.SWT; import org.eclipse.swt.SWTError; import org.eclipse.swt.dnd.Clipboard; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.TextTransfer; import org.eclipse.swt.dnd.Transfer; -import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; public class VirtualCopyToClipboardActionDelegate extends AbstractDebugActionDelegate { - private ContentViewer fViewer; + private TreeModelViewer fClientViewer; private static final String TAB = "\t"; //$NON-NLS-1$ private static final String SEPARATOR = "line.separator"; //$NON-NLS-1$ + private class VirtualViewerListener implements IViewerUpdateListener, ILabelUpdateListener { + + private IProgressMonitor fProgressMonitor; + private int fRemainingUpdatesCount; + private boolean fViewerUpdatesComplete; + private boolean fLabelUpdatesComplete; + private int fSelectionRootDepth; + + public void labelUpdateStarted(ILabelUpdate update) {} + public void labelUpdateComplete(ILabelUpdate update) { + incrementProgress(1); + } + public void labelUpdatesBegin() { + fLabelUpdatesComplete = false; + } + public void labelUpdatesComplete() { + fLabelUpdatesComplete = true; + completeProgress(); + } + + public void updateStarted(IViewerUpdate update) {} + public void updateComplete(IViewerUpdate update) { + if (update instanceof IChildrenUpdate) { + incrementProgress(((IChildrenUpdate)update).getLength()); + } + } + public void viewerUpdatesBegin() { + fViewerUpdatesComplete = false; + } + public void viewerUpdatesComplete() { + fViewerUpdatesComplete = true; + completeProgress(); + } + + private void completeProgress() { + IProgressMonitor pm; + synchronized (VirtualCopyToClipboardActionDelegate.this) { + pm = fProgressMonitor; + } + if (pm != null && fLabelUpdatesComplete && fViewerUpdatesComplete) { + pm.done(); + } + } + + private void incrementProgress(int count) { + IProgressMonitor pm; + synchronized (VirtualCopyToClipboardActionDelegate.this) { + pm = fProgressMonitor; + fRemainingUpdatesCount -= count; + } + if (pm != null && fLabelUpdatesComplete && fViewerUpdatesComplete) { + pm.worked(count); + } + } + + } + + /** * @see AbstractDebugActionDelegate#initialize(IAction, ISelection) */ @@ -53,8 +125,8 @@ public class VirtualCopyToClipboardActionDelegate extends AbstractDebugActionDel if (!isInitialized()) { IDebugView adapter= (IDebugView)getView().getAdapter(IDebugView.class); if (adapter != null) { - if (adapter.getViewer() instanceof ContentViewer) { - setViewer((ContentViewer) adapter.getViewer()); + if (adapter.getViewer() instanceof TreeModelViewer) { + setViewer((TreeModelViewer) adapter.getViewer()); } adapter.setAction(getActionId(), action); } @@ -71,15 +143,17 @@ public class VirtualCopyToClipboardActionDelegate extends AbstractDebugActionDel * Appends the representation of the specified element (using the label provider and indent) * to the buffer. For elements down to stack frames, children representations * are append to the buffer as well. + * @param item Item to append to string + * @param buffer String buffer for copy text. + * @param indent Current indentation in tree text. */ - protected void append(VirtualElement item, StringBuffer buffer, int indent) { + protected void append(VirtualItem item, StringBuffer buffer, int indent) { for (int i= 0; i < indent; i++) { buffer.append(TAB); } - String[] labels = item.getLabel(); - int count = labels.length; - if(count > 0) { - for (int i = 0; i < count; i++) { + String[] labels = (String[]) item.getData(VirtualItem.LABEL_KEY); + if(labels != null && labels.length > 0) { + for (int i = 0; i < labels.length; i++) { String text = labels[i]; if(text != null && !text.trim().equals(IInternalDebugCoreConstants.EMPTY_STRING)) { buffer.append(text+TAB); @@ -89,108 +163,184 @@ public class VirtualCopyToClipboardActionDelegate extends AbstractDebugActionDel } } + private IPresentationContext makeVirtualPresentationContext(final IPresentationContext clientViewerContext) { + return new PresentationContext(clientViewerContext.getId()) { + + { + String[] clientProperties = clientViewerContext.getProperties(); + for (int i = 0; i < clientProperties.length; i++) { + setProperty(clientProperties[i], clientViewerContext.getProperty(clientProperties[i])); + } + + } + + public String[] getColumns() { + String[] clientColumns = super.getColumns(); + + if (clientColumns == null || clientColumns.length == 0) { + // No columns are used. + return null; + } + + // Try to find the name column. + for (int i = 0; i < clientColumns.length; i++) { + if (VariableColumnPresentation.COLUMN_VARIABLE_NAME.equals(clientColumns[i])) { + return new String[] { VariableColumnPresentation.COLUMN_VARIABLE_NAME }; + } + } + + return new String[] { clientColumns[0] }; + } + }; + } + + private int calcUpdatesCount(IModelDelta stateDelta) { + final int[] count = new int[] {0}; + stateDelta.accept( new IModelDeltaVisitor() { + public boolean visit(IModelDelta delta, int depth) { + if ((delta.getFlags() & (IModelDelta.EXPAND | IModelDelta.SELECT)) != 0) { + count[0]++; + return true; + } + return false; + } + }); + + // Double it to account for separate element and label update ticks. + return count[0] * 2; + } + + private class ItemsToCopyVirtualItemValidator implements IVirtualItemValidator { + + Set fItemsToCopy = Collections.EMPTY_SET; + + public boolean isItemVisible(VirtualItem item) { + return fItemsToCopy.contains(item); + } + + public void showItem(VirtualItem item) { + } + } + + private VirtualTreeModelViewer initVirtualViewer(TreeModelViewer clientViewer, VirtualViewerListener listener, ItemsToCopyVirtualItemValidator validator) { + Object input = clientViewer.getInput(); + ModelDelta stateDelta = new ModelDelta(input, IModelDelta.NO_CHANGE); + clientViewer.saveElementState(TreePath.EMPTY, stateDelta, IModelDelta.EXPAND); + listener.fRemainingUpdatesCount = calcUpdatesCount(stateDelta); + VirtualTreeModelViewer virtualViewer = new VirtualTreeModelViewer( + clientViewer.getDisplay(), + SWT.VIRTUAL, + makeVirtualPresentationContext(clientViewer.getPresentationContext()), + validator); + virtualViewer.addViewerUpdateListener(listener); + virtualViewer.addLabelUpdateListener(listener); + virtualViewer.setInput(input); + virtualViewer.updateViewer(stateDelta); + + // Parse selected items from client viewer and add them to the virtual viewer selection. + listener.fSelectionRootDepth = Integer.MAX_VALUE; + TreeItem[] selection = clientViewer.getTree().getSelection(); + Set vSelection = new HashSet(selection.length * 4/3); + for (int i = 0; i < selection.length; i++) { + TreePath parentPath = fClientViewer.getTreePathFromItem(selection[i].getParentItem()); + listener.fSelectionRootDepth = Math.min(parentPath.getSegmentCount() + 1, listener.fSelectionRootDepth); + VirtualItem parentVItem = virtualViewer.findItem(parentPath); + if (parentVItem != null) { + int index = -1; + TreeItem parentItem = selection[i].getParentItem(); + if (parentItem != null) { + index = parentItem.indexOf(selection[i]); + } else { + Tree parentTree = selection[i].getParent(); + index = parentTree.indexOf(selection[i]); + } + vSelection.add( parentVItem.getItem(new Index(index)) ); + if (!selection[i].getExpanded()) { + listener.fRemainingUpdatesCount += 2; + } + } + } + validator.fItemsToCopy = vSelection; + virtualViewer.getTree().validate(); + return virtualViewer; + } + + protected TreeItem[] getSelectedItems(TreeModelViewer clientViewer) { + return clientViewer.getTree().getSelection(); + } + /** * Do the specific action using the current selection. + * @param action Action that is running. */ public void run(final IAction action) { - if (fViewer instanceof InternalTreeModelViewer) { - InternalTreeModelViewer viewer = (InternalTreeModelViewer) fViewer; - TreeItem[] items = getPrunedSelection(); - TreePath root = null; - int[] indexes = null; - if (items.length != 0) { - TreeItem anItem = items[0]; - TreeItem rootItem = anItem.getParentItem(); - if (rootItem == null) { - root = TreePath.EMPTY; - } else { - root = viewer.getTreePathFromItem(rootItem); - } - indexes = new int[items.length]; - for (int i = 0; i < items.length; i++) { - TreeItem child = items[i]; - if (rootItem == null) { - indexes[i] = viewer.getTree().indexOf(child); - } else { - indexes[i] = rootItem.indexOf(child); - } - } - } - final VirtualModel model = viewer.buildVirtualModel(root, indexes); - ProgressMonitorDialog dialog = new TimeTriggeredProgressMonitorDialog(fViewer.getControl().getShell(), 500); - final IProgressMonitor monitor = dialog.getProgressMonitor(); - dialog.setCancelable(true); - - final String[] columns = viewer.getPresentationContext().getColumns(); - final Object[] result = new Object[1]; - IRunnableWithProgress runnable = new IRunnableWithProgress() { - public void run(final IProgressMonitor m) throws InvocationTargetException, InterruptedException { - result[0] = model.populate(m, DebugUIPlugin.removeAccelerators(getAction().getText()), columns); - } - }; - try { - dialog.run(true, true, runnable); - } catch (InvocationTargetException e) { - DebugUIPlugin.log(e); - return; - } catch (InterruptedException e) { - return; - } - - VirtualElement modelRoot = (VirtualElement) result[0]; - if (!monitor.isCanceled()) { - if (root != null) { - // walk down to nested root - int depth = root.getSegmentCount(); - for (int i = 0; i < depth; i++) { - VirtualElement[] children = modelRoot.getChildren(); - for (int j = 0; j < children.length; j++) { - VirtualElement ve = children[j]; - if (ve != null) { - modelRoot = ve; - break; - } - } - } - } - VirtualElement[] children = modelRoot.getChildren(); - if (children != null) { - StringBuffer buffer = new StringBuffer(); - for (int i = 0; i < children.length; i++) { - if (children[i] != null) { - copy(children[i], buffer, 0); - } - } - TextTransfer plainTextTransfer = TextTransfer.getInstance(); - Clipboard clipboard= new Clipboard(fViewer.getControl().getDisplay()); - try { - doCopy(clipboard, plainTextTransfer, buffer); - } finally { - clipboard.dispose(); - } - } + if (fClientViewer.getSelection().isEmpty()) { + writeBufferToClipboard(new StringBuffer("")); + return; + } + + final VirtualViewerListener listener = new VirtualViewerListener(); + ItemsToCopyVirtualItemValidator validator = new ItemsToCopyVirtualItemValidator(); + VirtualTreeModelViewer virtualViewer = initVirtualViewer(fClientViewer, listener, validator); + + ProgressMonitorDialog dialog = new TimeTriggeredProgressMonitorDialog(fClientViewer.getControl().getShell(), 500); + final IProgressMonitor monitor = dialog.getProgressMonitor(); + dialog.setCancelable(true); + + IRunnableWithProgress runnable = new IRunnableWithProgress() { + public void run(final IProgressMonitor m) throws InvocationTargetException, InterruptedException { + synchronized(listener) { + listener.fProgressMonitor = m; + listener.fProgressMonitor.beginTask(DebugUIPlugin.removeAccelerators(getAction().getText()), listener.fRemainingUpdatesCount); + } + + while ((!listener.fLabelUpdatesComplete || !listener.fViewerUpdatesComplete) && !listener.fProgressMonitor.isCanceled()) { + Thread.sleep(1); + } + synchronized(listener) { + listener.fProgressMonitor = null; + } } - + }; + try { + dialog.run(true, true, runnable); + } catch (InvocationTargetException e) { + DebugUIPlugin.log(e); + return; + } catch (InterruptedException e) { + return; } + + if (!monitor.isCanceled()) { + copySelectionToClipboard(virtualViewer, validator.fItemsToCopy, listener.fSelectionRootDepth); + } + + virtualViewer.removeLabelUpdateListener(listener); + virtualViewer.removeViewerUpdateListener(listener); + virtualViewer.dispose(); + } + + private void copySelectionToClipboard(VirtualTreeModelViewer virtualViewer, Set itemsToCopy, int selectionRootDepth) { + StringBuffer buffer = new StringBuffer(); + writeItemToBuffer (virtualViewer.getTree(), itemsToCopy, buffer, -selectionRootDepth); + writeBufferToClipboard(buffer); } - /** - * @param item - * @param buffer - */ - protected void copy(VirtualElement item, StringBuffer buffer, int indent) { - if (!item.isFiltered()) { - append(item, buffer, indent); - VirtualElement[] children = item.getChildren(); - if (children != null) { - for (int i = 0; i < children.length; i++) { - copy(children[i], buffer, indent + 1); - } + protected void writeItemToBuffer(VirtualItem item, Set itemsToCopy, StringBuffer buffer, int indent) { + if (itemsToCopy.contains(item)) { + append(item, buffer, indent); + } + VirtualItem[] children = item.getItems(); + if (children != null) { + for (int i = 0; i < children.length; i++) { + writeItemToBuffer(children[i], itemsToCopy, buffer, indent + 1); } } } - protected void doCopy(Clipboard clipboard, TextTransfer plainTextTransfer, StringBuffer buffer) { + protected void writeBufferToClipboard(StringBuffer buffer) { + TextTransfer plainTextTransfer = TextTransfer.getInstance(); + Clipboard clipboard= new Clipboard(fClientViewer.getControl().getDisplay()); try { clipboard.setContents( new String[]{buffer.toString()}, @@ -199,59 +349,20 @@ public class VirtualCopyToClipboardActionDelegate extends AbstractDebugActionDel if (e.code != DND.ERROR_CANNOT_SET_CLIPBOARD) { throw e; } - if (MessageDialog.openQuestion(fViewer.getControl().getShell(), ActionMessages.CopyToClipboardActionDelegate_Problem_Copying_to_Clipboard_1, ActionMessages.CopyToClipboardActionDelegate_There_was_a_problem_when_accessing_the_system_clipboard__Retry__2)) { // - doCopy(clipboard, plainTextTransfer, buffer); - } - } - } - - /** - * Returns the selected items in the tree, pruning children - * if from selected parents. - */ - protected TreeItem[] getPrunedSelection() { - Control control = fViewer.getControl(); - List items = new ArrayList(); - if (control instanceof Tree) { - Tree tree = (Tree) control; - TreeItem[] selection = tree.getSelection(); - if (selection.length == 0) { - selection = tree.getItems(); - } - - for (int i = 0; i < selection.length; i++) { - TreeItem item = selection[i]; - if (isEnabledFor(item.getData())) { - if (walkHierarchy(item, items)) { - items.add(item); - } - } + if (MessageDialog.openQuestion(fClientViewer.getControl().getShell(), ActionMessages.CopyToClipboardActionDelegate_Problem_Copying_to_Clipboard_1, ActionMessages.CopyToClipboardActionDelegate_There_was_a_problem_when_accessing_the_system_clipboard__Retry__2)) { // + writeBufferToClipboard(buffer); } + } finally { + clipboard.dispose(); } - return (TreeItem[]) items.toArray(new TreeItem[items.size()]); } - /** - * Returns whether the parent of the specified - * element is already contained in the collection. - */ - protected boolean walkHierarchy(TreeItem item, List elements) { - TreeItem parent= item.getParentItem(); - if (parent == null) { - return true; - } - if (elements.contains(parent)) { - return false; - } - return walkHierarchy(parent, elements); - } - - protected ContentViewer getViewer() { - return fViewer; + protected TreeModelViewer getViewer() { + return fClientViewer; } - protected void setViewer(ContentViewer viewer) { - fViewer = viewer; + protected void setViewer(TreeModelViewer viewer) { + fClientViewer = viewer; } /** * @see AbstractDebugActionDelegate#doAction(Object) diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/VirtualFindAction.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/VirtualFindAction.java index 4cea30d2b..598d78f69 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/VirtualFindAction.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/VirtualFindAction.java @@ -8,91 +8,224 @@ * Contributors: * IBM Corporation - initial implementation * Pawel Piech (Wind River) - added a breadcrumb mode to Debug view (Bug 252677) + * Wind River Systems - refactored on top of VirtualTreeModelViewer *******************************************************************************/ package org.eclipse.debug.internal.ui.viewers.model; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; import org.eclipse.debug.internal.ui.DebugUIPlugin; import org.eclipse.debug.internal.ui.IDebugHelpContextIds; import org.eclipse.debug.internal.ui.actions.ActionMessages; +import org.eclipse.debug.internal.ui.elements.adapters.VariableColumnPresentation; import org.eclipse.debug.internal.ui.viewers.FindElementDialog; -import org.eclipse.debug.internal.ui.viewers.model.InternalTreeModelViewer.VirtualElement; -import org.eclipse.debug.internal.ui.viewers.model.InternalTreeModelViewer.VirtualModel; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDeltaVisitor; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdateListener; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer; +import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualItem; +import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualTreeModelViewer; import org.eclipse.jface.action.Action; import org.eclipse.jface.dialogs.ProgressMonitorDialog; import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.TreePath; -import org.eclipse.jface.viewers.TreeSelection; import org.eclipse.jface.window.Window; +import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Image; - import org.eclipse.ui.IWorkbenchCommandConstants; import org.eclipse.ui.PlatformUI; - import org.eclipse.ui.texteditor.IUpdate; import com.ibm.icu.text.MessageFormat; /** - * Action which prompts the user to find/navigate to an element in a virtual tree. + * Action which prompts user with a filtered list selection dialog to find an element in tree. * * @since 3.3 */ public class VirtualFindAction extends Action implements IUpdate { - private InternalTreeModelViewer fViewer; + private TreeModelViewer fClientViewer; - class FindLabelProvider extends LabelProvider { - - public FindLabelProvider() { - } + private class VirtualViewerListener implements IViewerUpdateListener, ILabelUpdateListener { + + private boolean fViewerUpdatesComplete = false; + private boolean fLabelUpdatesComplete = false; + private IProgressMonitor fProgressMonitor; + private int fRemainingUpdatesCount = 0; + + public void labelUpdateStarted(ILabelUpdate update) {} + public void labelUpdateComplete(ILabelUpdate update) { + incrementProgress(1); + } + public void labelUpdatesBegin() { + fLabelUpdatesComplete = false; + } + public void labelUpdatesComplete() { + fLabelUpdatesComplete = true; + completeProgress(); + } + + public void updateStarted(IViewerUpdate update) {} + public void updateComplete(IViewerUpdate update) { + if (update instanceof IChildrenUpdate) { + incrementProgress(((IChildrenUpdate)update).getLength()); + } + } + public void viewerUpdatesBegin() { + fViewerUpdatesComplete = false; + } + public void viewerUpdatesComplete() { + fViewerUpdatesComplete = true; + completeProgress(); + } + + private void completeProgress() { + IProgressMonitor pm; + synchronized (this) { + pm = fProgressMonitor; + } + if (pm != null && fLabelUpdatesComplete && fViewerUpdatesComplete) { + pm.done(); + } + } + + private void incrementProgress(int count) { + IProgressMonitor pm; + synchronized (this) { + pm = fProgressMonitor; + fRemainingUpdatesCount -= count; + } + if (pm != null && fLabelUpdatesComplete && fViewerUpdatesComplete) { + pm.worked(count); + } + } + } + + private static class FindLabelProvider extends LabelProvider { + private VirtualTreeModelViewer fVirtualViewer; + private Map fTextCache = new HashMap(); + public FindLabelProvider(VirtualTreeModelViewer viewer, List items) { + fVirtualViewer = viewer; + for (int i = 0; i < items.size(); i++) { + VirtualItem item = (VirtualItem)items.get(i); + fTextCache.put(item, fVirtualViewer.getText(item, 0)); + } + } + public Image getImage(Object element) { - return ((VirtualElement)element).getImage(); + return fVirtualViewer.getImage((VirtualItem) element, 0); } public String getText(Object element) { - return ((VirtualElement)element).getLabel()[0]; + return (String)fTextCache.get(element); } - } - public VirtualFindAction(InternalTreeModelViewer viewer) { + public VirtualFindAction(TreeModelViewer viewer) { + fClientViewer = viewer; + setText(ActionMessages.FindAction_0); setId(DebugUIPlugin.getUniqueIdentifier() + ".FindElementAction"); //$NON-NLS-1$ PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IDebugHelpContextIds.FIND_ELEMENT_ACTION); setActionDefinitionId(IWorkbenchCommandConstants.EDIT_FIND_AND_REPLACE); - fViewer = viewer; + fClientViewer = viewer; } + private VirtualTreeModelViewer initVirtualViewer(TreeModelViewer clientViewer, VirtualViewerListener listener) { + Object input = clientViewer.getInput(); + ModelDelta stateDelta = new ModelDelta(input, IModelDelta.NO_CHANGE); + clientViewer.saveElementState(TreePath.EMPTY, stateDelta, IModelDelta.EXPAND); + listener.fRemainingUpdatesCount = calcUpdatesCount(stateDelta); + VirtualTreeModelViewer fVirtualViewer = new VirtualTreeModelViewer( + clientViewer.getDisplay(), + SWT.NONE, + makeVirtualPresentationContext(clientViewer.getPresentationContext())); + fVirtualViewer.addViewerUpdateListener(listener); + fVirtualViewer.addLabelUpdateListener(listener); + fVirtualViewer.setInput(input); + if (fVirtualViewer.canToggleColumns()) { + fVirtualViewer.setShowColumns(clientViewer.isShowColumns()); + } + fVirtualViewer.updateViewer(stateDelta); + return fVirtualViewer; + } + + private IPresentationContext makeVirtualPresentationContext(final IPresentationContext clientViewerContext) { + return new PresentationContext(clientViewerContext.getId()) { + + { + String[] clientProperties = clientViewerContext.getProperties(); + for (int i = 0; i < clientProperties.length; i++) { + setProperty(clientProperties[i], clientViewerContext.getProperty(clientProperties[i])); + } + + } + + public String[] getColumns() { + String[] clientColumns = super.getColumns(); + + if (clientColumns == null || clientColumns.length == 0) { + // No columns are used. + return null; + } + + // Try to find the name column. + for (int i = 0; i < clientColumns.length; i++) { + if (VariableColumnPresentation.COLUMN_VARIABLE_NAME.equals(clientColumns[i])) { + return new String[] { VariableColumnPresentation.COLUMN_VARIABLE_NAME }; + } + } + + return new String[] { clientColumns[0] }; + } + }; + } + public void run() { - final VirtualModel model = fViewer.buildVirtualModel(null, null); - ProgressMonitorDialog dialog = new TimeTriggeredProgressMonitorDialog(fViewer.getControl().getShell(), 500); + final VirtualViewerListener listener = new VirtualViewerListener(); + VirtualTreeModelViewer virtualViewer = initVirtualViewer(fClientViewer, listener); + + ProgressMonitorDialog dialog = new TimeTriggeredProgressMonitorDialog(fClientViewer.getControl().getShell(), 500); final IProgressMonitor monitor = dialog.getProgressMonitor(); dialog.setCancelable(true); - String[] columns = fViewer.getPresentationContext().getColumns(); - String[] temp = null; - if (columns == null || columns.length == 0) { - temp = null; - } else { - temp = new String[]{columns[0]}; - } - final String[] IDs = temp; - final Object[] result = new Object[1]; - IRunnableWithProgress runnable = new IRunnableWithProgress() { - public void run(final IProgressMonitor m) throws InvocationTargetException, InterruptedException { - result[0] = model.populate(m, DebugUIPlugin.removeAccelerators(getText()), IDs); - } - }; try { - dialog.run(true, true, runnable); + dialog.run( + true, true, + new IRunnableWithProgress() { + public void run(final IProgressMonitor m) throws InvocationTargetException, InterruptedException { + synchronized(listener) { + listener.fProgressMonitor = m; + listener.fProgressMonitor.beginTask(DebugUIPlugin.removeAccelerators(getText()), listener.fRemainingUpdatesCount); + } + + while ((!listener.fLabelUpdatesComplete || !listener.fViewerUpdatesComplete) && !listener.fProgressMonitor.isCanceled()) { + Thread.sleep(1); + } + synchronized(listener) { + listener.fProgressMonitor = null; + } + } + }); } catch (InvocationTargetException e) { DebugUIPlugin.log(e); return; @@ -100,25 +233,43 @@ public class VirtualFindAction extends Action implements IUpdate { return; } - VirtualElement root = (VirtualElement) result[0]; + VirtualItem root = virtualViewer.getTree(); if (!monitor.isCanceled()) { List list = new ArrayList(); collectAllChildren(root, list); - performFind(list.toArray()); + FindLabelProvider labelProvider = new FindLabelProvider(virtualViewer, list); + VirtualItem result = performFind(list, labelProvider); + if (result != null) { + setSelectionToClient(virtualViewer, labelProvider, result); + } } + virtualViewer.removeLabelUpdateListener(listener); + virtualViewer.removeViewerUpdateListener(listener); + virtualViewer.dispose(); } - /** - * Adds all children to the given list recursively. - * - * @param collect - */ - private void collectAllChildren(VirtualElement element, List collect) { - VirtualElement[] children = element.getChildren(); + private int calcUpdatesCount(IModelDelta stateDelta) { + final int[] count = new int[] {0}; + stateDelta.accept( new IModelDeltaVisitor() { + public boolean visit(IModelDelta delta, int depth) { + if ((delta.getFlags() & IModelDelta.EXPAND) != 0) { + count[0] += delta.getChildCount(); + return true; + } + return false; + } + }); + + // Double it to account for separate element and label update ticks. + return count[0] * 2; + } + + private void collectAllChildren(VirtualItem element, List collect) { + VirtualItem[] children = element.getItems(); if (children != null) { for (int i = 0; i < children.length; i++) { - if (!children[i].isFiltered()) { + if (!children[i].needsLabelUpdate()) { collect.add(children[i]); collectAllChildren(children[i], collect); } @@ -126,28 +277,43 @@ public class VirtualFindAction extends Action implements IUpdate { } } - protected void performFind(Object[] items) { - FindElementDialog dialog = new FindElementDialog(fViewer.getControl().getShell(), new FindLabelProvider(), items); + protected VirtualItem performFind(List items, FindLabelProvider labelProvider) { + FindElementDialog dialog = new FindElementDialog( + fClientViewer.getControl().getShell(), + labelProvider, + items.toArray()); dialog.setTitle(ActionMessages.FindDialog_3); dialog.setMessage(ActionMessages.FindDialog_1); if (dialog.open() == Window.OK) { Object[] elements = dialog.getResult(); if (elements.length == 1) { - VirtualElement element = (VirtualElement)elements[0]; - TreePath path = element.realize(); - if (path != null) { - fViewer.setSelection(new TreeSelection(path), true, true); - } else { - DebugUIPlugin.errorDialog(fViewer.getControl().getShell(), ActionMessages.VirtualFindAction_0, - MessageFormat.format(ActionMessages.VirtualFindAction_1, new String[]{element.getLabel()[0]}), - (IStatus)null); - } + return (VirtualItem)elements[0]; } } + return null; + } + + protected void setSelectionToClient(VirtualTreeModelViewer virtualViewer, ILabelProvider labelProvider, VirtualItem findItem) { + virtualViewer.getTree().setSelection(new VirtualItem[] { findItem } ); + ModelDelta stateDelta = new ModelDelta(virtualViewer.getInput(), IModelDelta.NO_CHANGE); + virtualViewer.saveElementState(TreePath.EMPTY, stateDelta, IModelDelta.SELECT); + fClientViewer.updateViewer(stateDelta); + + ISelection selection = fClientViewer.getSelection(); + if (!selection.isEmpty() && + selection instanceof IStructuredSelection && + ((IStructuredSelection)selection).getFirstElement().equals(findItem.getData()) ) { + } else { + DebugUIPlugin.errorDialog( + fClientViewer.getControl().getShell(), + ActionMessages.VirtualFindAction_0, + MessageFormat.format(ActionMessages.VirtualFindAction_1, new String[]{ labelProvider.getText(findItem) }), + new Status(IStatus.ERROR, DebugUIPlugin.getUniqueIdentifier(), ActionMessages.VirtualFindAction_1)); + } } public void update() { - setEnabled(fViewer.getInput() != null); + setEnabled(fClientViewer.getInput() != null); } } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/ICheckUpdate.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/ICheckUpdate.java index 91d90ef10..7e31d3589 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/ICheckUpdate.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/ICheckUpdate.java @@ -1,5 +1,5 @@ /***************************************************************** - * Copyright (c) 2009 Texas Instruments and others + * Copyright (c) 2009, 2011 Texas Instruments 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 @@ -28,8 +28,8 @@ public interface ICheckUpdate extends ILabelUpdate { /** * Sets the check state of the tree node. * - * @param checked - * @param grayed + * @param checked Whether element should be checked. + * @param grayed Whether element should be grayed out. */ public void setChecked(boolean checked, boolean grayed); diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/ICheckboxModelProxy.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/ICheckboxModelProxy.java index d1f720358..3513b2adf 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/ICheckboxModelProxy.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/ICheckboxModelProxy.java @@ -1,5 +1,5 @@ /***************************************************************** - * Copyright (c) 2009 Texas Instruments and others + * Copyright (c) 2009, 2011 Texas Instruments 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 @@ -19,8 +19,9 @@ import org.eclipse.jface.viewers.TreePath; * notification for check state changes in the tree. * * @since 3.6 + * @see IModelProxy */ -public interface ICheckboxModelProxy extends IModelProxy { +public interface ICheckboxModelProxy { /** * Notifies the receiver that the given element has had its diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IChildrenCountUpdate.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IChildrenCountUpdate.java index 09cb08993..55818113c 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IChildrenCountUpdate.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IChildrenCountUpdate.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006 IBM Corporation and others. + * Copyright (c) 2006, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -14,9 +14,8 @@ package org.eclipse.debug.internal.ui.viewers.model.provisional; /** * Request monitor used to collect the number of children for an element in a viewer. - * <p> - * Clients are not intended to implement this interface. - * </p> + * + * @noimplement This interface is not intended to be implemented by clients. * @since 3.3 */ public interface IChildrenCountUpdate extends IViewerUpdate { diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IChildrenUpdate.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IChildrenUpdate.java index 643a8379d..9ee12df28 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IChildrenUpdate.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IChildrenUpdate.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006 IBM Corporation and others. + * Copyright (c) 2006, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -14,9 +14,8 @@ package org.eclipse.debug.internal.ui.viewers.model.provisional; /** * Context sensitive children update request for a parent and subrange of its * children. - * <p> - * Clients are not intended to implement this interface. - * </p> + * + * @noimplement This interface is not intended to be implemented by clients. * @since 3.3 */ public interface IChildrenUpdate extends IViewerUpdate { @@ -40,9 +39,7 @@ public interface IChildrenUpdate extends IViewerUpdate { * Sets the child for this request's parent at the given offset. * * @param child child - * @param index child offset - * - * TODO: what to do with <code>null</code> + * @param offset child offset */ public void setChild(Object child, int offset); } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IColumnPresentation.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IColumnPresentation.java index acdd43c22..991765351 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IColumnPresentation.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IColumnPresentation.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2007 IBM Corporation and others. + * Copyright (c) 2006, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -25,7 +25,7 @@ public interface IColumnPresentation { * Initializes this column presentation to be used in the * given context. * - * @param context + * @param context Presentation context. */ public void init(IPresentationContext context); diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IElementCompareRequest.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IElementCompareRequest.java index 7f6dfc45b..d6258f468 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IElementCompareRequest.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IElementCompareRequest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006 IBM Corporation and others. + * Copyright (c) 2006, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -13,9 +13,8 @@ package org.eclipse.debug.internal.ui.viewers.model.provisional; /** * Request to compare an element to a previously created memento. - * <p> - * Clients are not intended to implement this interface. - * </p> + * + * @noimplement This interface is not intended to be implemented by clients. * @since 3.3 */ public interface IElementCompareRequest extends IElementMementoRequest { diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IElementContentProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IElementContentProvider.java index b98cdbcc0..d3b682880 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IElementContentProvider.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IElementContentProvider.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2008 IBM Corporation and others. + * Copyright (c) 2006, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -13,7 +13,12 @@ package org.eclipse.debug.internal.ui.viewers.model.provisional; /** - * Provides content for an element in a virtual viewer. + * Provides content for an element in a tree model viewer. + * <p> + * Note: provider methods are called in the Display thread of the viewer. + * To avoid blocking the UI, long running operations should be performed + * asynchronously. + * </p> * * @since 3.3 */ diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IElementEditor.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IElementEditor.java index 9d04de4f8..e1890aa4f 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IElementEditor.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IElementEditor.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2007 IBM Corporation and others. + * Copyright (c) 2006, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -26,7 +26,7 @@ public interface IElementEditor { * if none. * * @param context presentation context - * @param id column id + * @param columnId column id * @param element object to be edited * @param parent parent control to create the cell editor in * @return cell editor or <code>null</code> @@ -36,6 +36,8 @@ public interface IElementEditor { /** * Returns a cell modifier for the specified element in the given context * or <code>null</code> if none. + * @param context Presentation context + * @param element Model element. * * @return cell modifier or <code>null</code> */ diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IElementLabelProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IElementLabelProvider.java index 0aaba8c6b..90551a572 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IElementLabelProvider.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IElementLabelProvider.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2008 IBM Corporation and others. + * Copyright (c) 2006, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -14,6 +14,11 @@ package org.eclipse.debug.internal.ui.viewers.model.provisional; /** * Provides context sensitive labels. Can be registered as an adapter for an element, * or implemented directly. + * <p> + * Note: provider methods are called in the Display thread of the viewer. + * To avoid blocking the UI, long running operations should be performed + * asynchronously. + * </p> * * @since 3.3 */ diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IElementMementoProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IElementMementoProvider.java index e4da42cd5..416072148 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IElementMementoProvider.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IElementMementoProvider.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2008 IBM Corporation and others. + * Copyright (c) 2006, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -15,6 +15,11 @@ package org.eclipse.debug.internal.ui.viewers.model.provisional; * Used to save and restore viewer selection/expansion state. A memento * provider adapter should be available from a viewer input element * in order to support viewer state save/restore. + * <p> + * Note: provider methods are called in the Display thread of the viewer. + * To avoid blocking the UI, long running operations should be performed + * asynchronously. + * </p> * * @since 3.3 */ diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IHasChildrenUpdate.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IHasChildrenUpdate.java index 6c7fab0ea..85232d9ed 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IHasChildrenUpdate.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IHasChildrenUpdate.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006 IBM Corporation and others. + * Copyright (c) 2006, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -13,9 +13,8 @@ package org.eclipse.debug.internal.ui.viewers.model.provisional; /** * Context sensitive update request for whether an element has children. - * <p> - * Clients are not intended to implement this interface. - * </p> + * + * @noimplement This interface is not intended to be implemented by clients. * @since 3.3 */ public interface IHasChildrenUpdate extends IViewerUpdate { diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/ILabelUpdate.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/ILabelUpdate.java index eebe886ec..451fd2a6d 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/ILabelUpdate.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/ILabelUpdate.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2008 IBM Corporation and others. + * Copyright (c) 2006, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -16,9 +16,8 @@ import org.eclipse.swt.graphics.RGB; /** * Context sensitive label update request for an element. - * <p> - * Clients are not intended to implement this interface. - * </p> + * + * @noimplement This interface is not intended to be implemented by clients. * @since 3.3 */ public interface ILabelUpdate extends IViewerUpdate { @@ -36,7 +35,7 @@ public interface ILabelUpdate extends IViewerUpdate { /** * Sets the text of the label of the specified column. Cannot be <code>null</code>. * - * @param text + * @param text to set to viewer * @param columnIndex column index (0 when no columns) */ public void setLabel(String text, int columnIndex); @@ -44,7 +43,7 @@ public interface ILabelUpdate extends IViewerUpdate { /** * Sets the font of the label. * - * @param fontData + * @param fontData to set to viewer * @param columnIndex column index (0 when no columns) */ public void setFontData(FontData fontData, int columnIndex); @@ -52,7 +51,7 @@ public interface ILabelUpdate extends IViewerUpdate { /** * Sets the image of the label. * - * @param image + * @param image to set to viewer * @param columnIndex column index (0 when no columns) */ public void setImageDescriptor(ImageDescriptor image, int columnIndex); @@ -60,7 +59,7 @@ public interface ILabelUpdate extends IViewerUpdate { /** * Sets the foreground color of the label. * - * @param foreground + * @param foreground to set to viewer * @param columnIndex column index (0 when no columns) */ public void setForeground(RGB foreground, int columnIndex); @@ -68,7 +67,7 @@ public interface ILabelUpdate extends IViewerUpdate { /** * Sets the background color of the label. * - * @param background + * @param background to set to viewer * @param columnIndex column index (0 when no columns) */ public void setBackground(RGB background, int columnIndex); diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IModelChangedListener.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IModelChangedListener.java index f812ee94b..98470bc83 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IModelChangedListener.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IModelChangedListener.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2006 IBM Corporation and others. + * Copyright (c) 2005, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -16,7 +16,7 @@ package org.eclipse.debug.internal.ui.viewers.model.provisional; * * @since 3.2 * @see IModelProxy - * @see IModelDeltaNode + * @see IModelDelta */ public interface IModelChangedListener { 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 18fd862ed..3fb1dd65c 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2009 IBM Corporation and others. + * Copyright (c) 2005, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -26,10 +26,8 @@ package org.eclipse.debug.internal.ui.viewers.model.provisional; * </ul> * </p> * <p> - * Clients are not intended to implement this interface directly. Instead, clients - * creating and firing model deltas should create instances of - * {@link org.eclipse.debug.internal.ui.viewers.update.ModelDelta}. - * </p> + * @noimplement Clients are not intended to implement this interface directly. Instead, clients + * creating and firing model deltas should create instances of {@link ModelDelta}. * </p> * @since 3.2 */ @@ -185,7 +183,7 @@ public interface IModelDelta { /** * Accepts the given visitor. * - * @param visitor + * @param visitor delta visitor to accept * @since 3.3 */ public void accept(IModelDeltaVisitor visitor); diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IModelProxy.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IModelProxy.java index e661b1e97..adabf4e78 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IModelProxy.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IModelProxy.java @@ -26,38 +26,48 @@ import org.eclipse.jface.viewers.Viewer; * within that model for a specific presentation context. * </p> * <p> - * Clients may implement this interface. Implementations of this interface - * must subclass {@link AbstractModelProxy}. + * Note: provider methods are called in the Display thread of the viewer. + * To avoid blocking the UI, long running operations should be performed + * asynchronously. * </p> + * + * @noimplement Clients are not intended to implement this interface directly. Instead, clients + * creating and firing model deltas should create instances of {@link AbstractModelProxy}. * @see IModelDelta * @see IModelProxyFactory * @see IModelChangedListener * @see ICheckboxModelProxy + * @see IModelProxy2 * @since 3.2 */ public interface IModelProxy { - + /** * Notification this model proxy has been created and is about to be installed * in the following context. This is the first method called after a model proxy - * is created. + * is created and it's called in a job thread and not on a display thread. * <p> * This method is called by the asynchronous viewer framework and should not * be called by clients. * </p> * @param context presentation context in which the proxy will be installed + * @see IModelProxy2#initialize(ITreeModelViewer) */ public void init(IPresentationContext context); /** * Notification this model proxy has been installed in the specified * viewer. This indicates that the model proxy has been created and registered - * model change listeners are ready to process deltas. + * model change listeners are ready to process deltas. This method is called + * by the {@link AbstractModelProxy} base class using a job and NOT in viewers + * display thread. It allows the client to initialize the proxy without + * blocking the UI. The default implementaiton is a no-op. * <p> * This method is called by the asynchronous viewer framework and should not * be called by clients. * </p> - * @param viewer viewer + * @param viewer viewer + * @see IModelProxy2#initialize(ITreeModelViewer) * @since 3.3 */ public void installed(Viewer viewer); diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IModelProxy2.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IModelProxy2.java new file mode 100644 index 000000000..7187edc48 --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IModelProxy2.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.viewers.model.provisional; + +import org.eclipse.debug.internal.ui.viewers.provisional.AbstractModelProxy; +import org.eclipse.jface.viewers.Viewer; + +/** + * Extension to the model proxy interface which allows the proxy to be initialized + * on the viewer's Display thread + * + * @noimplement Clients are not intended to implement this interface directly. Instead, clients + * creating and firing model deltas should create instances of {@link AbstractModelProxy}. + * @since 3.8 + */ +public interface IModelProxy2 extends IModelProxy { + + /** + * Initialize model proxy with given tree model viewer. This method is + * called on the viewer's Display thread and is guaranteed to be called + * before the dispose() method is called on the same proxy. The default + * implementation of this method calls {@link #init(IPresentationContext)} + * and {@link #installed(Viewer)} asynchornously and not in the Display + * thread. + * <p> + * This method is called by the asynchronous viewer framework and should + * not be called by clients. + * </p> + * @param viewer Viewer that is installing this model proxy. + * + */ + public void initialize(ITreeModelViewer viewer); + +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IModelSelectionPolicy.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IModelSelectionPolicy.java index c0fec6504..f672adef1 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IModelSelectionPolicy.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IModelSelectionPolicy.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2007 IBM Corporation and others. + * Copyright (c) 2005, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -34,12 +34,11 @@ import org.eclipse.jface.viewers.ISelection; public interface IModelSelectionPolicy { /** - * Returns whether the given selection is contained in - * this model. + * Returns whether the given selection is contained in this model. * - * @param selection - * @param context - * @return + * @param selection Selection to check + * @param context Presentation context of the viewer. + * @return true if selection is contained in model. */ public boolean contains(ISelection selection, IPresentationContext context); @@ -48,10 +47,10 @@ public interface IModelSelectionPolicy { * existing selection. The policy is only asked about selections * that it contains. * - * @param existing - * @param candidate - * @param context - * @return + * @param existing Existing selection to check. + * @param candidate New proposed selection. + * @param context Presentation context of viewer. + * @return true if candidate selection should be set to viewer */ public boolean overrides(ISelection existing, ISelection candidate, IPresentationContext context); @@ -59,9 +58,9 @@ public interface IModelSelectionPolicy { * Returns whether the given selection should be maintained in the * face of a selection attempt from a different model. * - * @param selection - * @param context - * @return + * @param selection selection to test + * @param context Presentation context of viewer. + * @return true if selection is sticky */ public boolean isSticky(ISelection selection, IPresentationContext context); diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IStateUpdateListener.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IStateUpdateListener.java index efa321fdb..49830e0af 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IStateUpdateListener.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IStateUpdateListener.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2009 IBM Corporation and others. + * Copyright (c) 2009, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -20,23 +20,31 @@ public interface IStateUpdateListener { /** * Notification that a sequence of state saving updates are starting. + * + * @param input Input object for the state operation. */ public void stateSaveUpdatesBegin(Object input); /** * Notification that viewer updates are complete. Corresponds to * a <code>viewerUpdatesBegin()</code> notification. + * + * @param input Input object for the state operation. */ public void stateSaveUpdatesComplete(Object input); /** * Notification that a sequence of viewer updates are starting. + * + * @param input Input object for the state operation. */ public void stateRestoreUpdatesBegin(Object input); /** * Notification that viewer updates are complete. Corresponds to * a <code>viewerUpdatesBegin()</code> notification. + * + * @param input Input object for the state operation. */ public void stateRestoreUpdatesComplete(Object input); @@ -44,6 +52,7 @@ public interface IStateUpdateListener { * Notification that a specific update has started within * a sequence of updates. * + * @param input Input object for the state operation. * @param update update */ public void stateUpdateStarted(Object input, IViewerUpdate update); @@ -52,6 +61,7 @@ public interface IStateUpdateListener { * Notification that a specific update has completed within a * sequence of updates. * + * @param input Input object for the state operation. * @param update update */ public void stateUpdateComplete(Object input, IViewerUpdate update); diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/ITreeModelViewer.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/ITreeModelViewer.java new file mode 100644 index 000000000..25a04bc4c --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/ITreeModelViewer.java @@ -0,0 +1,278 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + * IBM Corporation - ongoing bug fixes and enhancements + *******************************************************************************/ +package org.eclipse.debug.internal.ui.viewers.model.provisional; + +import org.eclipse.debug.internal.ui.viewers.model.ILabelUpdateListener; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.TreePath; +import org.eclipse.jface.viewers.ViewerFilter; +import org.eclipse.jface.viewers.ViewerLabel; +import org.eclipse.swt.widgets.Display; + +/** + * Interface of an tree model viewer. It declares the common methods for the + * JFace-based {@link TreeModelViewer} and the UI-less + * {@link VirtualTreeModelViewer}. + * + * @since 3.8 + */ +public interface ITreeModelViewer extends ISelectionProvider { + + /** + * Constant indicating that all levels of the tree should be expanded or + * collapsed. + * + * @see #setAutoExpandLevel(int) + * @see #getAutoExpandLevel() + */ + public static final int ALL_LEVELS = -1; + + /** + * Returns the Display object that this viewer is in. The + * display object can be used by clients to access the display thread + * to call the viewer methods. + * + * @return The display. + */ + public Display getDisplay(); + + /** + * Returns this viewer's presentation context. + * + * @return presentation context + */ + public IPresentationContext getPresentationContext(); + + /** + * Returns the current input of this viewer, or <code>null</code> + * if none. The viewer's input provides the "model" for the viewer's + * content. + * + * @return Input object + */ + public Object getInput(); + + /** + * Sets the input of this viewer. Setting the input resets the + * viewer's contents and triggers an update starting at the input + * element. + * + * @param object Input element, or <code>null</code> if none. + */ + public void setInput(Object object); + + /** + * Returns the current selection in viewer. + * + * @return selection object + */ + public ISelection getSelection(); + + /** + * Sets a new selection for this viewer and optionally makes it visible. + * The selection is not set if the model selection policy overrides the + * attempt to set the selection. + * + * @param selection the new selection + * @param reveal <code>true</code> if the selection is to be made + * visible, and <code>false</code> otherwise + * @param force <code>true</code> if the selection should override the + * model selection policy + */ + public void setSelection(ISelection selection, boolean reveal, boolean force); + + /** + * Attempts to set the selection for this viewer and optionally makes it visible. + * The selection is not set if the model selection policy overrides the + * attempt to set the selection. + * + * @param selection the new selection + * @param reveal whether to make the selection visible after successfully setting + * the selection + * @param force whether to force the selection (override the model selection policy) + * @return <code>true</code> if the selection was set and <code>false</code> if the + * model selection policy overrides the selection attempt + */ + public boolean trySelection(ISelection selection, boolean reveal, boolean force); + + /** + * Returns the auto-expand level. + * + * @return non-negative level, or <code>ALL_LEVELS</code> if all levels of + * the tree are expanded automatically + * @see #setAutoExpandLevel + */ + public int getAutoExpandLevel(); + + /** + * Sets the auto-expand level to be used when the input of the viewer is set + * using {@link #setInput(Object)}. The value 0 means that there is no + * auto-expand; 1 means that the invisible root element is expanded (since + * most concrete implementations do not show the root element, there is usually + * no practical difference between using the values 0 and 1); 2 means that + * top-level elements are expanded, but not their children; 3 means that + * top-level elements are expanded, and their children, but not + * grandchildren; and so on. + * <p> + * The value <code>ALL_LEVELS</code> means that all subtrees should be + * expanded. + * </p> + * + * @param level + * non-negative level, or <code>ALL_LEVELS</code> to expand all + * levels of the tree + */ + public void setAutoExpandLevel(int level); + + /** + * Returns the label data for the given element and for the given column, + * Returns <code>null</code> if the given element is not found or is not + * materialized in the virtual viewer. Clients may listen to label update + * events to be notified when element labels are updated. + * + * @param path Path of the element. + * @param columnId ID of the column for which to return the label data. + * @return Label object containing the label information. Can be + * <code>null</code> if the given element is not found or is not + * materialized in the virtual viewer. + */ + public ViewerLabel getElementLabel(TreePath path, String columnId); + + /** + * Registers the specified listener for view update notifications. + * + * @param listener Listener to add + */ + public void addViewerUpdateListener(IViewerUpdateListener listener); + + /** + * Removes the specified listener from update notifications. + * + * @param listener Listener to remove + */ + public void removeViewerUpdateListener(IViewerUpdateListener listener); + + /** + * Registers the specified listener for state update notifications. + * + * @param listener Listener to add + */ + public void addStateUpdateListener(IStateUpdateListener listener); + + /** + * Removes the specified listener from state update notifications. + * + * @param listener Listener to remove + */ + public void removeStateUpdateListener(IStateUpdateListener listener); + + /** + * Registers the specified listener for view label update notifications. + * + * @param listener Listener to add + */ + public void addLabelUpdateListener(ILabelUpdateListener listener); + + /** + * Removes the specified listener from view label update notifications. + * + * @param listener Listener to remove + */ + public void removeLabelUpdateListener(ILabelUpdateListener listener); + + /** + * Registers the given listener for model delta notification. + * This listener is called immediately after the viewer processes + * the delta. + * + * @param listener Listener to add + */ + public void addModelChangedListener(IModelChangedListener listener); + + /** + * Removes the given listener from model delta notification. + * + * @param listener Listener to remove + */ + public void removeModelChangedListener(IModelChangedListener listener); + + /** + * Writes state information into a delta for the sub-tree at the given + * path. It adds delta nodes and IModelDelta.EXPAND and IModelDelta.SELECT + * 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>. + * @return Returns whether the state was saved for the given path. Will + * return <code>false</code> if an element at the given path cannot + * be found. + */ + public boolean saveElementState(TreePath path, ModelDelta delta, int flagsToSave); + + /** + * Causes the viewer to process the given delta as if it came from a + * model proxy. This method is intended to be used to restore state + * saved using {@link #saveElementState(TreePath, ModelDelta, int)}. + * + * @param delta Delta to process. + */ + public void updateViewer(IModelDelta delta); + + /** + * Triggers an update of the given element and its children. If + * multiple instances of the given element are found in the tree, + * they will all be updated. + * + * @param element Element to update. + */ + public void refresh(Object element); + + /** + * Triggers a full update of all the elements in the tree. + */ + public void refresh(); + + /** + * Returns the paths at which the given element is found realized in viewer + * or an empty array if not found. + * + * @param element Element to find. + * @return Array of paths for given element. + */ + public TreePath[] getElementPaths(Object element); + + /** + * Returns filters currently configured in viewer. + * + * @return filter array in viewer. + */ + public ViewerFilter[] getFilters(); + + /** + * Add a new filter to use in viewer. + * + * @param filter Filter to add. + */ + public void addFilter(ViewerFilter filter); + + /** + * Sets viewer filters to the filters in array. + * + * @param filters New filter array to use. + */ + public void setFilters(ViewerFilter[] filters); + + } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IViewerUpdate.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IViewerUpdate.java index f2a5be0a3..baa6971ce 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IViewerUpdate.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IViewerUpdate.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2008 IBM Corporation and others. + * Copyright (c) 2006, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -15,9 +15,8 @@ import org.eclipse.jface.viewers.TreePath; /** * A context sensitive viewer update request. - * <p> - * Clients are not intended to implement this interface. - * </p> + * + * @noimplement This interface is not intended to be implemented by clients. * @since 3.3 */ public interface IViewerUpdate extends IRequest { diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IVirtualItemListener.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IVirtualItemListener.java new file mode 100644 index 000000000..4f5944752 --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IVirtualItemListener.java @@ -0,0 +1,28 @@ +package org.eclipse.debug.internal.ui.viewers.model.provisional; + +/** + * Interface for listeners that need to be notified when items + * are disposed or revealed. It should be implemented by the viewer. + * + * @see VirtualTreeModelViewer + * @since 3.8 + */ +public interface IVirtualItemListener { + + /** + * Called when the item has been shown in the virtual viewer's + * view-port. This indicates to the viewer that it should check + * the item's status and request needed data. + * + * @param item The item that was revealed. + */ + public void revealed(VirtualItem item); + + /** + * Called when an item is disposed. It tells the viewer to + * clean up any remaining mappings and cached data of this item. + * + * @param item The itam that was disposed. + */ + public void disposed(VirtualItem item); +}
\ No newline at end of file diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IVirtualItemValidator.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IVirtualItemValidator.java new file mode 100644 index 000000000..b460c486c --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IVirtualItemValidator.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.viewers.model.provisional; + + +/** + * A validator to be used with a VirtualTreeModelViewer to determine which viewer + * items should be updated by the viewer. + * + * @see VirtualTreeModelViewer + * @since 3.8 + */ +public interface IVirtualItemValidator { + + /** + * Allows the validator to determine whether the given item is to be deemed + * visible in the virtual tree. + * + * @param item Item to be tested. + * @return returns true if the item should be considered visible. + */ + public boolean isItemVisible(VirtualItem item); + + /** + * Indicates that the viewer requested to reveal the given item in viewer. + * + * @param item Item to show. + */ + public void showItem(VirtualItem item); +} 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 455573e42..1f4052251 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2009 IBM Corporation and others. + * Copyright (c) 2005, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -11,16 +11,14 @@ *******************************************************************************/ package org.eclipse.debug.internal.ui.viewers.model.provisional; - - - /** * A model delta. Used to create model deltas. * <p> - * Clients may instantiate this class; not intended to be subclassed. + * Clients may instantiate this class; not intended to be sub-classed. * </p> - * @see org.eclipse.debug.internal.ui.viewers.IModelDelta + * @see IModelDelta * @since 3.2 + * @noextend This class is not intended to be sub-classed by clients. */ public class ModelDelta implements IModelDelta { @@ -332,7 +330,7 @@ public class ModelDelta implements IModelDelta { /** * Sets this delta's element * - * @param element + * @param element element to set */ public void setElement(Object element) { fElement = element; @@ -341,7 +339,7 @@ public class ModelDelta implements IModelDelta { /** * Sets this delta's flags. * - * @param flags + * @param flags new flags to set */ public void setFlags(int flags) { fFlags = flags; @@ -350,7 +348,7 @@ public class ModelDelta implements IModelDelta { /** * Sets this delta's index * - * @param index + * @param index new index to set. * @since 3.6 */ public void setIndex(int index) { @@ -360,7 +358,7 @@ public class ModelDelta implements IModelDelta { /** * Sets this delta's child count. * - * @param count + * @param count New child count to set. */ public void setChildCount(int count) { fChildCount = count; diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/PresentationContext.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/PresentationContext.java index 1a4b03389..574964040 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/PresentationContext.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/PresentationContext.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2010 IBM Corporation and others. + * Copyright (c) 2005, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -84,7 +84,6 @@ public class PresentationContext implements IPresentationContext { * Constructs a presentation context for the given id and part. * The presentation context id and window are derived from the part. * - * @param id presentation context id * @param part presentation context part, can NOT be <code>null</code> */ public PresentationContext(IWorkbenchPart part) { diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/TreeModelViewer.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/TreeModelViewer.java index af173fea2..0c26078f9 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/TreeModelViewer.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/TreeModelViewer.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2010 IBM Corporation and others. + * Copyright (c) 2006, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -20,7 +20,7 @@ import org.eclipse.ui.IMemento; /** * A tree viewer for a model. * <p> - * Style flags supported by this viewer are the same as for {@link TreeViewer}, + * Style flags supported by this viewer are the same as for {@link org.eclipse.jface.viewers.TreeViewer}, * except: * <ul> * <li>SWT.VIRTUAL - Indicates that the viewer should be in lazy mode. This @@ -117,7 +117,7 @@ public class TreeModelViewer extends InternalTreeModelViewer { /** * Initializes viewer state from the memento * - * @param memento + * @param memento the {@link IMemento} to read from */ public void initState(IMemento memento) { super.initState(memento); @@ -126,17 +126,15 @@ public class TreeModelViewer extends InternalTreeModelViewer { /** * Save viewer state into the given memento. * - * @param memento + * @param memento the {@link IMemento} to save to */ public void saveState(IMemento memento) { super.saveState(memento); } - /** - * Returns whether columns are being displayed currently. - * - * @return - */ + /** + * @return Returns true if columns are being displayed currently. + */ public boolean isShowColumns() { return super.isShowColumns(); } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/TreeModelViewerFilter.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/TreeModelViewerFilter.java new file mode 100644 index 000000000..077938619 --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/TreeModelViewerFilter.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.viewers.model.provisional; + +import org.eclipse.jface.viewers.ViewerFilter; + +/** + * Viewer filter for the Tree Model Viewer which allows more efficient filtering + * in the lazy viewer. + * <p> + * The standard {@link ViewerFilter} class must be applied to all elements in the + * tree, thus forcing the lazy viewer to retrieve all children of all elements and + * defeating the lazy loading behavior. This class adds an {@link #isApplicable(ITreeModelViewer, Object)} + * method, which can be used by the filter to discern which parent elements the + * filter should apply to. + * </p> + * + * @since 3.8 + */ +abstract public class TreeModelViewerFilter extends ViewerFilter { + + /** + * Determines whether the filter applies to the given parent element. + * @return Returns true if the viewer should use the given filter on the + * given element. + */ + abstract public boolean isApplicable(ITreeModelViewer viewer, Object parentElement); +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/ViewerInputService.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/ViewerInputService.java index e24ecc3cb..f595c60c4 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/ViewerInputService.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/ViewerInputService.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2009 IBM Corporation and others. + * Copyright (c) 2007, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -18,9 +18,10 @@ import org.eclipse.debug.internal.ui.viewers.model.ViewerInputUpdate; * Service to compute a viewer input from a source object * for a given presentation context. * <p> - * This class may be instantiated. Not intended to be subclassed. + * This class may be instantiated, but it not intended to be sub-classed. * </p> * @since 3.4 + * @noextend This class is not intended to be subclassed by clients. */ public class ViewerInputService { @@ -55,8 +56,8 @@ public class ViewerInputService { /** * Constructs a viewer input service for the given requester and presentation context. * + * @param viewer for which inputs are required * @param requestor client requesting viewer inputs - * @param context context for which inputs are required */ public ViewerInputService(TreeModelViewer viewer, IViewerInputRequestor requestor) { fRequestor = requestor; 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/provisional/VirtualItem.java index 1365c5ff2..0a190b278 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/provisional/VirtualItem.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2009 Wind River Systems and others. + * Copyright (c) 2011 Wind River Systems and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -8,7 +8,7 @@ * Contributors: * Wind River Systems - initial API and implementation *******************************************************************************/ -package org.eclipse.debug.internal.ui.viewers.model; +package org.eclipse.debug.internal.ui.viewers.model.provisional; import java.util.HashMap; import java.util.Iterator; @@ -18,20 +18,22 @@ import java.util.TreeMap; import org.eclipse.core.runtime.Assert; /** - * Virtual item, which is analogous to the SWT's tree item. + * Virtual item, which is analogous to the SWT's tree item. This class is used + * by the {@link VirtualTreeModelViewer}. * - * @since 3.5 + * @see VirtualTreeModelViewer + * @since 3.8 */ -class VirtualItem { +public class VirtualItem { // Data keys for display attributes of an item. - static String LABEL_KEY = "LABEL_KEY"; //$NON-NLS-1$ - static String IMAGE_KEY = "IMAGE_KEY"; //$NON-NLS-1$ - static String FONT_KEY = "FONT_KEY"; //$NON-NLS-1$ - static String FOREGROUND_KEY = "FOREGROUND_KEY"; //$NON-NLS-1$ - static String BACKGROUND_KEY = "BACKGROUND_KEY"; //$NON-NLS-1$ + public static String LABEL_KEY = "LABEL_KEY"; //$NON-NLS-1$ + public static String IMAGE_KEY = "IMAGE_KEY"; //$NON-NLS-1$ + public static String FONT_KEY = "FONT_KEY"; //$NON-NLS-1$ + public static String FOREGROUND_KEY = "FOREGROUND_KEY"; //$NON-NLS-1$ + public static String BACKGROUND_KEY = "BACKGROUND_KEY"; //$NON-NLS-1$ - static String ELEMENT_DATA_KEY = "element"; //$NON-NLS-1$ + public static String ELEMENT_DATA_KEY = "element"; //$NON-NLS-1$ /** * Index object of a tree item. It allows the indexes to be modified @@ -103,7 +105,7 @@ class VirtualItem { private boolean fExpanded = false; /** - * The cound of child items. <code>-1</code> indicates that the count + * The count of child items. <code>-1</code> indicates that the count * is not known. */ private int fItemCount = -1; @@ -134,41 +136,49 @@ class VirtualItem { */ private boolean fDisposed = false; - - VirtualItem(VirtualItem parent, Index index) { + + /** + * Virtual item constructor. + * @param parent parent virtual item + * @param index index of the item in the parent + */ + public VirtualItem(VirtualItem parent, Index index) { fParent = parent; fIndex = index; } - void setNeedsCountUpdate() { - fNeedsCountUpdate = true; - fItemCount = -1; - } - - void setNeedsLabelUpdate() { - fNeedsLabelUpdate = true; - } - - void setNeedsDataUpdate() { - fNeedsDataUpdate = true; - } - - void clear(Index index) { + /** + * Clears the child item at the given index. + * @param index index of item to clear. + */ + public void clear(Index index) { VirtualItem item = (VirtualItem)fItems.remove(index); if (item != null) { item.dispose(); } } - VirtualItem getParent() { + /** + * Returns the parent item. + * @return parent item. + */ + public VirtualItem getParent() { return fParent; } - Index getIndex() { + /** + * @return Returns the index of this item. + */ + public Index getIndex() { return fIndex; } - VirtualItem findItem(Object element) { + /** + * Finds the given item in the child items of this element. + * @param element Data object of the item to be found. + * @return Item if found, <code>null</code> if not. + */ + public VirtualItem findItem(Object element) { for (Iterator itr = fItems.values().iterator(); itr.hasNext();) { VirtualItem next = (VirtualItem)itr.next(); Object nextData = next.getData(); @@ -179,35 +189,81 @@ class VirtualItem { return null; } - boolean needsDataUpdate() { + /** + * @return Returns whether the data element of this item is stale. + */ + public boolean needsDataUpdate() { return fNeedsDataUpdate; } - void clearNeedsDataUpdate() { + /** + * Marks the item as having a stale data item. + */ + public void setNeedsDataUpdate() { + fNeedsDataUpdate = true; + } + + /** + * Clears the stale status of the item's data element. + */ + public void clearNeedsDataUpdate() { fNeedsDataUpdate = false; } - boolean needsCountUpdate() { + /** + * @return Returns whether the item has stale item count. + */ + public boolean needsCountUpdate() { return fNeedsCountUpdate; } + + /** + * Marks the item as having a stale child count. + */ + public void setNeedsCountUpdate() { + fNeedsCountUpdate = true; + fItemCount = -1; + } - void clearNeedsCountUpdate() { + /** + * Clears the stale status of the item's child count. + */ + public void clearNeedsCountUpdate() { fNeedsCountUpdate = false; } - boolean needsLabelUpdate() { + /** + * @return Returns whether the item has stale label. + */ + public boolean needsLabelUpdate() { return fNeedsLabelUpdate; } - void clearNeedsLabelUpdate() { + /** + * Marks the item as having a stale label data. + */ + public void setNeedsLabelUpdate() { + fNeedsLabelUpdate = true; + } + + /** + * Clears the stale status of the item's label. + */ + public void clearNeedsLabelUpdate() { fNeedsLabelUpdate = false; } - boolean isDisposed() { + /** + * @return Returns whether the item has been disposed. + */ + public boolean isDisposed() { return fDisposed; } - void dispose() { + /** + * Disposes the item. + */ + public void dispose() { fData.clear(); for (Iterator itr = fItems.values().iterator(); itr.hasNext();) { ((VirtualItem)itr.next()).dispose(); @@ -218,23 +274,43 @@ class VirtualItem { findTree().fireItemDisposed(this); } - Object getData (String key) { + /** + * @param key Key to retrieve data for. + * @return Returns item data corresponding to given key. + */ + public Object getData (String key) { return fData.get(key); } - void setData(String key, Object data) { + /** + * Sets given data element for given key. + * @param key Key for data. + * @param data Data value. + */ + public void setData(String key, Object data) { fData.put(key, data); } - void setData(Object data) { + /** + * Sets the item's data element. + * @param data Item's new element. + */ + public void setData(Object data) { fData.put(ELEMENT_DATA_KEY, data); } - Object getData () { + /** + * @return Returns item's data element. + */ + public Object getData () { return fData.get(ELEMENT_DATA_KEY); } - - void setExpanded(boolean expanded) { + + /** + * Marks the given item as expanded or collapsed. + * @param expanded If true, item will be marked as expanded. + */ + public void setExpanded(boolean expanded) { if (fExpanded == expanded) { return; } @@ -255,11 +331,18 @@ class VirtualItem { } } - boolean getExpanded() { + /** + * @return Returns item's expanded state. + */ + public boolean getExpanded() { return fExpanded; } - void setHasItems(boolean hasChildren) { + /** + * Sets the flag indicating whether item has child items. + * @param hasChildren Set to true if child has items. + */ + public void setHasItems(boolean hasChildren) { fHasItems = hasChildren; if (!fHasItems) { if (getItemCount() != 0) { @@ -270,11 +353,18 @@ class VirtualItem { } } - boolean hasItems() { + /** + * @return Returns true if item has child items. + */ + public boolean hasItems() { return fHasItems; } - void setItemCount(int count) { + /** + * Sets the item's child count. + * @param count Child count. + */ + public void setItemCount(int count) { fItemCount = count; for (Iterator itr = fItems.entrySet().iterator(); itr.hasNext();) { Map.Entry entry = (Map.Entry)itr.next(); @@ -299,11 +389,20 @@ class VirtualItem { } } - int getItemCount() { + /** + * @return Returns item's child count. + */ + public int getItemCount() { return fItemCount; } - VirtualItem getItem(Index index) { + /** + * Returns the child item at given index. Child item is created if needed. + * + * @param index Index of the child item. + * @return Child item. + */ + public VirtualItem getItem(Index index) { ensureItems(); VirtualItem item = (VirtualItem)fItems.get(index); @@ -314,7 +413,10 @@ class VirtualItem { return item; } - boolean childrenNeedDataUpdate() { + /** + * @return Returns true if any of the child items need a data update. + */ + public boolean childrenNeedDataUpdate() { if (getItemCount() == 0) { return false; } @@ -330,11 +432,23 @@ class VirtualItem { return false; } - VirtualItem[] getItems() { + /** + * Returns an array of current child items. The returned array contains + * only the items that have been created. It may not contain as many items as the + * item count. + * + * @return Child items array. + */ + public VirtualItem[] getItems() { return (VirtualItem[]) fItems.values().toArray(new VirtualItem[fItems.size()]); } - VirtualItem addItem(int position) { + /** + * Adds a child item at the given index position. + * @param position The index position to inser the new item at. + * @return Returns the added item. + */ + public VirtualItem addItem(int position) { if (!fHasItems) { fHasItems = true; } @@ -360,7 +474,11 @@ class VirtualItem { return newChild; } - void remove(Index position) { + /** + * Removes the item at the given index. + * @param position Index of the item to remove. + */ + public void remove(Index position) { fItemCount--; if (fItemCount < 0) { fHasItems = false; diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/VirtualTree.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/VirtualTree.java index 9cd993390..661a1ea76 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/VirtualTree.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/VirtualTree.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2009 Wind River Systems and others. + * Copyright (c) 2011 Wind River Systems and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -8,7 +8,7 @@ * Contributors: * Wind River Systems - initial API and implementation *******************************************************************************/ -package org.eclipse.debug.internal.ui.viewers.model; +package org.eclipse.debug.internal.ui.viewers.model.provisional; import java.util.HashSet; import java.util.Iterator; @@ -18,11 +18,13 @@ import org.eclipse.swt.SWT; /** - * Tree of virtual items that is analogous to SWT's tree control. + * Tree of virtual items that is analogous to SWT's tree control. The tree is used + * by the {@link VirtualTreeModelViewer}. * - * @since 3.5 + * @see VirtualTreeModelViewer + * @since 3.8 */ -class VirtualTree extends VirtualItem { +public class VirtualTree extends VirtualItem { /** * Lazy virtual tree does not retrieve elements or labels, @@ -30,33 +32,25 @@ class VirtualTree extends VirtualItem { */ private boolean fLazy; - /** - * The item currently at the top of the "virtual" view-port. - */ - private VirtualItem fTopItem; + private IVirtualItemValidator fValidator; - /** - * Interface for listeners that need to be notified when items - * are disposed or revealed. It should be implemented by the viewer. - */ - public static interface IVirtualItemListener { - - /** - * Called when the item has been shown in the virtual viewer's - * view-port. This indicates to the viewer that it should check - * the item's status and request needed data. - * - * @param item The item that was revealed. - */ - public void revealed(VirtualItem item); + private class SelectedItemValidator implements IVirtualItemValidator { + public boolean isItemVisible(VirtualItem item) { + // visible items. For now only mark the selected items as visible. + for (int i = 0; i < fSelection.length; i++) { + VirtualItem selectionItem = fSelection[i]; + while (selectionItem != null) { + if (item.equals(selectionItem)) { + return true; + } + selectionItem = selectionItem.getParent(); + } + } + return false; + } - /** - * Called when an item is disposed. It tells the viewer to - * clean up any remaining mappings and cached data of this item. - * - * @param item The itam that was disposed. - */ - public void disposed(VirtualItem item); + public void showItem(VirtualItem item) { + } } /** @@ -70,83 +64,95 @@ class VirtualTree extends VirtualItem { */ private VirtualItem[] fSelection = new VirtualItem[0]; - VirtualTree(int style) { + /** + * Constructs the virtual tree with the given style and validator. + * + * @param style The style flag. Only SWT.VIRTUAL flag is used. + * @param validator Item validator used to determine item visibility. + */ + public VirtualTree(int style, IVirtualItemValidator validator) { super(null, new VirtualItem.Index(0)); fLazy = (style & SWT.VIRTUAL) != 0; + if (fLazy && validator == null) { + fValidator = new SelectedItemValidator(); + } else { + fValidator = validator; + } clearNeedsLabelUpdate(); clearNeedsDataUpdate(); } - void dispose() { + /** + * Disposes the virtual tree. + */ + public void dispose() { super.dispose(); fVirtualItemListeners.clear(); } - void setNeedsCountUpdate() { + public void setNeedsCountUpdate() { super.setNeedsCountUpdate(); clearNeedsLabelUpdate(); clearNeedsDataUpdate(); } - void setNeedsLabelUpdate() { + public void setNeedsLabelUpdate() { // no-op } - void setData(String key, Object data) { + public void setData(String key, Object data) { super.setData(key, data); if (data == null) { clearNeedsDataUpdate(); } } - void addItemListener(IVirtualItemListener listener) { + /** + * Adds a listener for when virtual items are revealed in the view. + * @param listener Listener to add to list of listeners. + */ + public void addItemListener(IVirtualItemListener listener) { fVirtualItemListeners.add(listener); } - void removeItemListener(IVirtualItemListener listener) { + public void removeItemListener(IVirtualItemListener listener) { fVirtualItemListeners.remove(listener); } - VirtualItem[] getSelection() { + public VirtualItem[] getSelection() { return fSelection; } - void setSelection(VirtualItem[] items) { + public void setSelection(VirtualItem[] items) { fSelection = items; } - void showItem(VirtualItem item) { - setTopItem(item); + public void showItem(VirtualItem item) { + if (fValidator != null) { + fValidator.showItem(item); + } } - void fireItemDisposed(VirtualItem item) { + public void fireItemDisposed(VirtualItem item) { for (Iterator itr = fVirtualItemListeners.iterator(); itr.hasNext();) { ((IVirtualItemListener)itr.next()).disposed(item); } } - void fireItemRevealed(VirtualItem item) { + public void fireItemRevealed(VirtualItem item) { for (Iterator itr = fVirtualItemListeners.iterator(); itr.hasNext();) { ((IVirtualItemListener)itr.next()).revealed(item); } } - void setData(Object data) { + public void setData(Object data) { super.setData(data); // The root item always has children as long as the input is non-null, // so that it should be expanded. setHasItems(data != null); } - void setTopItem(VirtualItem item) { - fTopItem = item; - } - - VirtualItem getTopItem() { - return fTopItem; - } - - void setHasItems(boolean hasChildren) { + public void setHasItems(boolean hasChildren) { super.setHasItems(hasChildren); // The root item is always expanded as long as it has children. if (hasChildren) { @@ -154,30 +160,25 @@ class VirtualTree extends VirtualItem { } } - boolean isItemVisible(VirtualItem item) { - if (!fLazy) { - // If not in lazy mode, all items are visible. - return true; - } else { - // TODO: use top item and visible item count to determine list of - // visible items. For now only mark the selected items as visible. - for (int i = 0; i < fSelection.length; i++) { - VirtualItem selectionItem = fSelection[i]; - while (selectionItem != null) { - if (item.equals(selectionItem)) { - return true; - } - selectionItem = selectionItem.getParent(); - } - } - return false; + /** + * Returns whether the given item is considered visible by the tree as + * determined by its virtual item validator. + * + * @param item Item to check. + * @return true if items is vislble. + * @see IVirtualItemValidator + */ + public boolean isItemVisible(VirtualItem item) { + if (fLazy) { + return fValidator.isItemVisible(item); } + return true; } /** * Validates the entire tree. */ - void validate() { + public void validate() { validate(VirtualTree.this); } @@ -187,7 +188,7 @@ class VirtualTree extends VirtualItem { * * @param item The item which to validate. */ - void validate(VirtualItem item) { + public void validate(VirtualItem item) { if (item.needsDataUpdate()) { if (isItemVisible(item)) { fireItemRevealed(item); diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/VirtualTreeModelViewer.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/VirtualTreeModelViewer.java index 7db158ddb..1b57cb020 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/VirtualTreeModelViewer.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/VirtualTreeModelViewer.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2010 Wind River Systems and others. + * Copyright (c) 2008, 2011 Wind River Systems and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -31,15 +31,37 @@ import org.eclipse.ui.IMemento; * </ul> * </p> * @since 3.5 - * @noextend + * @noextend This class is not intended to be sub-classed by clients. */ public class VirtualTreeModelViewer extends InternalVirtualTreeModelViewer { + /** + * Creates a virtual tree model viewer. + * @param display Display used by the viewer to call the data providers + * on the UI thread. + * @param style Stlye flags. + * @param context Viewer's presentation context. + */ public VirtualTreeModelViewer(Display display, int style, IPresentationContext context) { - super(display, style, context); + super(display, style, context, null); } + /** + * Creates a virtual tree model viewer. + * @param display Display used by the viewer to call the data providers + * on the UI thread. + * @param style style flags. + * @param context Viewer's presentation context. + * @param validator Optional validator that is used to determine which items should be + * considered visible when SWT.VIRTUAL style is used. If <code>null</code> then the + * standard validator is used that updates only the selected items. + * + * @since 3.8 + */ + public VirtualTreeModelViewer(Display display, int style, IPresentationContext context, IVirtualItemValidator validator) { + super(display, style, context, validator); + } /** * Returns this viewer's presentation context. @@ -119,7 +141,7 @@ public class VirtualTreeModelViewer extends InternalVirtualTreeModelViewer { /** * Initializes viewer state from the memento * - * @param memento + * @param memento the {@link IMemento} to read from */ public void initState(IMemento memento) { super.initState(memento); @@ -128,17 +150,15 @@ public class VirtualTreeModelViewer extends InternalVirtualTreeModelViewer { /** * Save viewer state into the given memento. * - * @param memento + * @param memento the {@link IMemento} to save to */ public void saveState(IMemento memento) { super.saveState(memento); } /** - * Returns whether columns are being displayed currently. - * - * @return - */ + * @return Returns true if columns are being displayed currently. + */ public boolean isShowColumns() { return super.isShowColumns(); } @@ -172,4 +192,15 @@ public class VirtualTreeModelViewer extends InternalVirtualTreeModelViewer { public ViewerLabel getElementLabel(TreePath path, String columnId) { return super.getElementLabel(path, columnId); } + + public VirtualItem[] findItems(Object elementOrTreePath) { + return super.findItems(elementOrTreePath); + } + + public VirtualItem findItem(TreePath path) { + return super.findItem(path); + } + + + } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/provisional/AbstractModelProxy.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/provisional/AbstractModelProxy.java index e3da944d9..9d944ad27 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/provisional/AbstractModelProxy.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/provisional/AbstractModelProxy.java @@ -10,15 +10,20 @@ *******************************************************************************/ package org.eclipse.debug.internal.ui.viewers.provisional; +import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.ISafeRunnable; +import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.ListenerList; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.SafeRunner; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; import org.eclipse.debug.internal.ui.DebugUIPlugin; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelChangedListener; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy2; import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer; import org.eclipse.jface.viewers.Viewer; /** @@ -28,12 +33,15 @@ import org.eclipse.jface.viewers.Viewer; * </p> * @since 3.2 */ -public abstract class AbstractModelProxy implements IModelProxy { +public abstract class AbstractModelProxy implements IModelProxy2 { private IPresentationContext fContext; - private Viewer fViewer; + private boolean fInstalled = false; + private ITreeModelViewer fViewer; private boolean fDisposed = false; - + private Job fInstallJob; + + private ListenerList fListeners = new ListenerList(); // debug flags @@ -74,6 +82,10 @@ public abstract class AbstractModelProxy implements IModelProxy { * @param delta model delta to broadcast */ public void fireModelChanged(IModelDelta delta) { + synchronized(this) { + if (!fInstalled || fDisposed) return; + } + final IModelDelta root = getRootDelta(delta); Object[] listeners = getListeners(); for (int i = 0; i < listeners.length; i++) { @@ -111,16 +123,58 @@ public abstract class AbstractModelProxy implements IModelProxy { * @see org.eclipse.debug.internal.ui.viewers.IModelProxy#dispose() */ public synchronized void dispose() { + if (fInstallJob != null) { + fInstallJob.cancel(); + fInstallJob = null; + } fDisposed = true; fContext = null; fViewer = null; } - /* (non-Javadoc) - * @see org.eclipse.debug.internal.ui.viewers.IModelProxy#init(org.eclipse.debug.internal.ui.viewers.IPresentationContext) - */ - public void init(IPresentationContext context) { - fContext = context; + protected synchronized void setInstalled(boolean installed) { + fInstalled = installed; + } + + protected synchronized boolean isInstalled() { + return fInstalled; + } + + protected synchronized void setDisposed(boolean disposed) { + fDisposed = disposed; + } + + public void initialize(ITreeModelViewer viewer) { + setDisposed(false); + + synchronized(this) { + fViewer = viewer; + fContext = viewer.getPresentationContext(); + fInstallJob = new Job("Model Proxy installed notification job") {//$NON-NLS-1$ + protected IStatus run(IProgressMonitor monitor) { + synchronized(this) { + fInstallJob = null; + } + if (!monitor.isCanceled()) { + init(getTreeModelViewer().getPresentationContext()); + setInstalled(true); + installed(getViewer()); + } + return Status.OK_STATUS; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.runtime.jobs.Job#shouldRun() + */ + public boolean shouldRun() { + return !isDisposed(); + } + }; + fInstallJob.setSystem(true); + } + fInstallJob.schedule(); } /** @@ -129,18 +183,23 @@ public abstract class AbstractModelProxy implements IModelProxy { * @return presentation context, or <code>null</code> if this * model proxy has been disposed */ - public IPresentationContext getPresentationContext() { + public synchronized IPresentationContext getPresentationContext() { return fContext; } /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.IModelProxy#init(org.eclipse.debug.internal.ui.viewers.IPresentationContext) + */ + public void init(IPresentationContext context) { + } + + /* (non-Javadoc) * * Subclasses should override as required. * * @see org.eclipse.debug.internal.ui.viewers.provisional.IModelProxy#installed(org.eclipse.jface.viewers.Viewer) */ public void installed(Viewer viewer) { - fViewer = viewer; } /** @@ -149,8 +208,17 @@ public abstract class AbstractModelProxy implements IModelProxy { * @return viewer or <code>null</code> if not installed */ protected Viewer getViewer() { - return fViewer; + return (Viewer)fViewer; } + + /** + * Returns the viewer this proxy is installed in. + * + * @return viewer or <code>null</code> if not installed + */ + protected ITreeModelViewer getTreeModelViewer() { + return fViewer; + } /* (non-Javadoc) * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy#isDisposed() diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/breakpoints/BreakpointsView.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/breakpoints/BreakpointsView.java index 131a58c7b..10e6855e4 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/breakpoints/BreakpointsView.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/breakpoints/BreakpointsView.java @@ -69,9 +69,9 @@ import org.eclipse.debug.internal.ui.breakpoints.provisional.IBreakpointOrganize import org.eclipse.debug.internal.ui.breakpoints.provisional.IBreakpointUIConstants; import org.eclipse.debug.internal.ui.elements.adapters.DefaultBreakpointsViewInput; import org.eclipse.debug.internal.ui.preferences.IDebugPreferenceConstants; -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.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer; import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdateListener; diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchView.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchView.java index c3a97a3ed..6234f3208 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchView.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchView.java @@ -532,7 +532,7 @@ public class LaunchView extends AbstractDebugView fAddToFavoritesAction = new AddToFavoritesAction(); fEditSourceAction = new EditSourceLookupPathAction(this); fLookupAction = new LookupSourceAction(this); - setAction(FIND_ACTION, new VirtualFindAction((InternalTreeModelViewer) getViewer())); + setAction(FIND_ACTION, new VirtualFindAction((TreeModelViewer) getViewer())); addCapabilityAction(new TerminateCommandAction(), TERMINATE); addCapabilityAction(new DisconnectCommandAction(), DISCONNECT); diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchViewCopyToClipboardActionDelegate.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchViewCopyToClipboardActionDelegate.java index bcd222e33..b4c203002 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchViewCopyToClipboardActionDelegate.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchViewCopyToClipboardActionDelegate.java @@ -12,6 +12,7 @@ package org.eclipse.debug.internal.ui.views.launch; import org.eclipse.debug.internal.ui.viewers.model.InternalTreeModelViewer; import org.eclipse.debug.internal.ui.viewers.model.VirtualCopyToClipboardActionDelegate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ITreeSelection; import org.eclipse.jface.viewers.TreePath; @@ -24,7 +25,7 @@ import org.eclipse.swt.widgets.Widget; */ public class LaunchViewCopyToClipboardActionDelegate extends VirtualCopyToClipboardActionDelegate { - protected TreeItem[] getPrunedSelection() { + protected TreeItem[] getSelectedItems(TreeModelViewer clientViewer) { LaunchView view = (LaunchView)getView(); if (view.isBreadcrumbVisible()) { ISelection selection = getSelection(); @@ -33,15 +34,15 @@ public class LaunchViewCopyToClipboardActionDelegate extends VirtualCopyToClipbo if (!selection.isEmpty()) { path = ((ITreeSelection)selection).getPaths()[0]; } - return getSelectedItems((InternalTreeModelViewer)getViewer(), path); + return getSelectedItemsInTreeViewer((TreeModelViewer)getViewer(), path); } return new TreeItem[0]; } else { - return super.getPrunedSelection(); + return super.getSelectedItems(clientViewer); } } - private TreeItem[] getSelectedItems(InternalTreeModelViewer viewer, TreePath path) { + private TreeItem[] getSelectedItemsInTreeViewer(TreeModelViewer viewer, TreePath path) { Widget item = viewer.findItem(path); if (item instanceof TreeItem) { return new TreeItem[] { (TreeItem)item }; |