Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/ContentTests.java232
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/InternalVirtualTreeModelViewer.java5
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/TreeModelLabelProvider.java108
3 files changed, 340 insertions, 5 deletions
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 1c1fcca1b..8f20ce316 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
@@ -10,11 +10,22 @@
*******************************************************************************/
package org.eclipe.debug.tests.viewer.model;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+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.ITreeModelContentProviderTarget;
import org.eclipse.debug.internal.ui.viewers.model.ITreeModelViewer;
+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.ModelDelta;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
@@ -27,7 +38,7 @@ import org.eclipse.ui.PlatformUI;
*
* @since 3.6
*/
-abstract public class ContentTests extends TestCase {
+abstract public class ContentTests extends TestCase implements ITestModelUpdatesListenerConstants {
Display fDisplay;
Shell fShell;
@@ -113,4 +124,223 @@ abstract public class ContentTests extends TestCase {
model.validateData(fViewer, TreePath.EMPTY);
}
+ /**
+ * Modified test model that optionally captures (i.e. doesn't compete)
+ * udpates after filling in their data.
+ */
+ class TestModelWithCapturedUpdates extends TestModel {
+
+ boolean fCaptureLabelUpdates = false;
+ boolean fCaptureChildrenUpdates = false;
+
+ List fCapturedUpdates = Collections.synchronizedList(new ArrayList());
+
+ 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 (fCaptureChildrenUpdates) {
+ fCapturedUpdates.add(updates[i]);
+ } else {
+ updates[i].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 (fCaptureLabelUpdates) {
+ fCapturedUpdates.add(updates[i]);
+ } else {
+ updates[i].done();
+ }
+ }
+ }
+ }
+
+ /**
+ * Test to make sure that label provider cancels stale updates and doesn't
+ * use data from stale updates to populate the viewer.<br>
+ * See bug 210027
+ */
+ public void testLabelUpdatesCompletedOutOfSequence1() {
+ TestModelWithCapturedUpdates model = new TestModelWithCapturedUpdates();
+ model.fCaptureLabelUpdates = true;
+
+ model.setRoot( new TestElement(model, "root", new TestElement[] {
+ new TestElement(model, "1", new TestElement[0]),
+ new TestElement(model, "2", new TestElement[0]),
+ }) );
+
+ // Set input into the view to update it, but block children updates.
+ // Wait for view to start retrieving content.
+ fViewer.setInput(model.getRootElement());
+ while (model.fCapturedUpdates.size() < model.getRootElement().fChildren.length) {
+ if (!fDisplay.readAndDispatch ()) fDisplay.sleep ();
+ }
+ List firstUpdates = model.fCapturedUpdates;
+ model.fCapturedUpdates = new ArrayList(2);
+
+// // Change the model and run another update set.
+ model.getElement(model.findElement("1")).setLabelAppendix(" - changed");
+ model.getElement(model.findElement("2")).setLabelAppendix(" - changed");
+ fListener.reset(TreePath.EMPTY, model.getRootElement(), -1, false, false);
+ model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT));
+ while (model.fCapturedUpdates.size() < model.getRootElement().fChildren.length) {
+ if (!fDisplay.readAndDispatch ()) fDisplay.sleep ();
+ }
+
+ // Complete the second set of children updates
+ for (int i = 0; i < model.fCapturedUpdates.size(); i++) {
+ ((ILabelUpdate)model.fCapturedUpdates.get(i)).done();
+ }
+
+ // Then complete the first set.
+ for (int i = 0; i < firstUpdates.size(); i++) {
+ ILabelUpdate capturedUpdate = (ILabelUpdate)firstUpdates.get(i);
+ Assert.assertTrue(capturedUpdate.isCanceled());
+ capturedUpdate.done();
+ }
+
+ while (!fListener.isFinished(CHILDREN_UPDATES)) if (!fDisplay.readAndDispatch ()) fDisplay.sleep ();
+
+ // Check viewer data
+ model.validateData(fViewer, TreePath.EMPTY);
+ }
+
+ /**
+ * Test to make sure that label provider cancels stale updates and doesn't
+ * use data from stale updates to populate the viewer.<br>
+ * This version of the test changes the elements in the view, and not just
+ * the elements' labels. In this case, the view should still cancel stale
+ * updates.<br>
+ * See bug 210027
+ */
+ public void testLabelUpdatesCompletedOutOfSequence2() {
+ TestModelWithCapturedUpdates model = new TestModelWithCapturedUpdates();
+ model.fCaptureLabelUpdates = true;
+
+ model.setRoot( new TestElement(model, "root", new TestElement[] {
+ new TestElement(model, "1", new TestElement[0]),
+ new TestElement(model, "2", new TestElement[0]),
+ }) );
+
+ // Set input into the view to update it, but block children updates.
+ // Wait for view to start retrieving content.
+ fViewer.setInput(model.getRootElement());
+ while (model.fCapturedUpdates.size() < model.getRootElement().fChildren.length) {
+ if (!fDisplay.readAndDispatch ()) fDisplay.sleep ();
+ }
+ List firstUpdates = model.fCapturedUpdates;
+ model.fCapturedUpdates = new ArrayList(2);
+
+ // Change the model and run another update set.
+ model.setElementChildren(TreePath.EMPTY, new TestElement[] {
+ new TestElement(model, "1-new", new TestElement[0]),
+ new TestElement(model, "2-new", new TestElement[0]),
+ });
+ fListener.reset(TreePath.EMPTY, model.getRootElement(), -1, false, false);
+ model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT));
+ while (model.fCapturedUpdates.size() < model.getRootElement().fChildren.length) {
+ if (!fDisplay.readAndDispatch ()) fDisplay.sleep ();
+ }
+
+ // Complete the second set of children updates
+ for (int i = 0; i < model.fCapturedUpdates.size(); i++) {
+ ((ILabelUpdate)model.fCapturedUpdates.get(i)).done();
+ }
+
+ // Then complete the first set.
+ for (int i = 0; i < firstUpdates.size(); i++) {
+ ILabelUpdate capturedUpdate = (ILabelUpdate)firstUpdates.get(i);
+ Assert.assertTrue(capturedUpdate.isCanceled());
+ capturedUpdate.done();
+ }
+
+ while (!fListener.isFinished(CHILDREN_UPDATES)) if (!fDisplay.readAndDispatch ()) fDisplay.sleep ();
+
+ // Check viewer data
+ model.validateData(fViewer, TreePath.EMPTY);
+ }
+
+ /**
+ * Test to make sure that content provider cancels stale updates and doesn't
+ * use data from stale updates to populate the viewer.<br>
+ * Note: this test is disabled because currently the viewer will not issue
+ * a new update for an until the previous update is completed. This is even
+ * if the previous update is canceled. If this behavior is changed at some
+ * point, then this test should be re-enabled.<br>
+ * See bug 210027
+ */
+ public void _x_testChildrenUpdatesCompletedOutOfSequence() {
+ TestModelWithCapturedUpdates model = new TestModelWithCapturedUpdates();
+ model.fCaptureChildrenUpdates = true;
+
+ model.setRoot( new TestElement(model, "root", new TestElement[] {
+ new TestElement(model, "1", new TestElement[0]),
+ new TestElement(model, "2", new TestElement[0]),
+ }) );
+
+ // Set input into the view to update it, but block children updates.
+ // Wait for view to start retrieving content.
+ fViewer.setInput(model.getRootElement());
+ while (!areCapturedChildrenUpdatesComplete(model.fCapturedUpdates, model.getRootElement().fChildren.length)) {
+ if (!fDisplay.readAndDispatch ()) fDisplay.sleep ();
+ }
+ IChildrenUpdate[] firstUpdates = (IChildrenUpdate[])model.fCapturedUpdates.toArray(new IChildrenUpdate[0]);
+ model.fCapturedUpdates.clear();
+
+ // Change the model and run another update set.
+ model.setElementChildren(TreePath.EMPTY, new TestElement[] {
+ new TestElement(model, "1-new", new TestElement[0]),
+ new TestElement(model, "2-new", new TestElement[0]),
+ });
+ fListener.reset(TreePath.EMPTY, model.getRootElement(), -1, false, false);
+ model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT));
+ while (!areCapturedChildrenUpdatesComplete(model.fCapturedUpdates, model.getRootElement().fChildren.length)) {
+ if (!fDisplay.readAndDispatch ()) fDisplay.sleep ();
+ }
+
+ // Complete the second set of children updates
+ for (int i = 0; i < model.fCapturedUpdates.size(); i++) {
+ ((IChildrenUpdate)model.fCapturedUpdates.get(i)).done();
+ }
+
+ // Then complete the first set.
+ for (int i = 0; i < firstUpdates.length; i++) {
+ firstUpdates[i].done();
+ }
+
+ while (!fListener.isFinished(CHILDREN_UPDATES)) if (!fDisplay.readAndDispatch ()) fDisplay.sleep ();
+
+ // Check viewer data
+ model.validateData(fViewer, TreePath.EMPTY);
+ }
+
+ private boolean areCapturedChildrenUpdatesComplete(List capturedUpdates, int childCount) {
+ List expectedChildren = new ArrayList();
+ for (int i = 0; i < childCount; i++) {
+ expectedChildren.add(new Integer(i));
+ }
+ IChildrenUpdate[] updates = (IChildrenUpdate[])capturedUpdates.toArray(new IChildrenUpdate[0]);
+ for (int i = 0; i < updates.length; i++) {
+ for (int j = 0; j < updates[i].getLength(); j++) {
+ expectedChildren.remove( new Integer(updates[i].getOffset() + j) );
+ }
+ }
+ return expectedChildren.isEmpty();
+ }
+
}
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 b33650bca..004896d68 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
@@ -1308,8 +1308,9 @@ public class InternalVirtualTreeModelViewer extends Viewer
childCount = items[0].getItemCount();
// Mimic the jface viewer behavior which returns 1 for child count
// for an item that has children but is not yet expanded.
- if (childCount == -1 && items[0].hasItems()) {
- childCount = 1;
+ // Return 0, if we do not know if the item has children.
+ if (childCount == -1) {
+ childCount = items[0].hasItems() ? 1 : 0;
}
}
return childCount;
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 b81197202..616110a97 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
@@ -28,6 +28,10 @@ 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;
+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.IPresentationContext;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.ColumnLabelProvider;
@@ -44,7 +48,9 @@ import org.eclipse.ui.progress.UIJob;
/**
* @since 3.3
*/
-public class TreeModelLabelProvider extends ColumnLabelProvider implements ITreeModelLabelProvider {
+public class TreeModelLabelProvider extends ColumnLabelProvider
+ implements ITreeModelLabelProvider, IModelChangedListener
+{
private ITreeModelLabelProviderTarget fViewer;
private List fComplete;
@@ -101,11 +107,37 @@ public class TreeModelLabelProvider extends ColumnLabelProvider implements ITree
*/
private List fUpdatesInProgress = new ArrayList();
+ /**
+ * Delta visitor actively cancels the outstanding label updates for
+ * elements that are changed and are about to be updated.
+ */
+ class Visitor implements IModelDeltaVisitor {
+ /* (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) {
+ if ((delta.getFlags() & IModelDelta.CONTENT) > 0) {
+ cancelElementUpdates(delta.getElement(), true);
+ return false;
+ } else if ((delta.getFlags() & IModelDelta.STATE) > 0) {
+ cancelElementUpdates(delta.getElement(), false);
+ return true;
+ }
+ return true;
+ }
+ }
+
+ /**
+ * Delta visitor
+ */
+ private Visitor fVisitor = new Visitor();
+
/**
* Constructs a new label provider on the given display
*/
public TreeModelLabelProvider(ITreeModelLabelProviderTarget viewer) {
fViewer = viewer;
+ fViewer.addModelChangedListener(this);
}
/**
@@ -178,6 +210,7 @@ public class TreeModelLabelProvider extends ColumnLabelProvider implements ITree
* @see org.eclipse.jface.viewers.BaseLabelProvider#dispose()
*/
public void dispose() {
+ fViewer.removeModelChangedListener(this);
synchronized (fUpdatesInProgress) {
Iterator updatesInProgress = fUpdatesInProgress.iterator();
while (updatesInProgress.hasNext()) {
@@ -220,6 +253,8 @@ public class TreeModelLabelProvider extends ColumnLabelProvider implements ITree
}
public synchronized boolean update(TreePath elementPath) {
+ cancelPathUpdates(elementPath);
+
String[] visibleColumns = fViewer.getVisibleColumns();
Object element = elementPath.getLastSegment();
IElementLabelProvider presentation = ViewerAdapterService.getLabelProvider(element);
@@ -247,6 +282,21 @@ public class TreeModelLabelProvider extends ColumnLabelProvider implements ITree
}
}
+ /**
+ * Cancel any outstanding updates that are running for this element.
+ */
+ 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 startRequests(UIJob updateJob) {
// Avoid calling providers inside a synchronized section. Instead
// copy the updates map into a new variable.
@@ -271,6 +321,56 @@ public class TreeModelLabelProvider extends ColumnLabelProvider implements ITree
}
}
+ /**
+ * Cancels all running updates for the given element. If seachFullPath is true,
+ * all updtes will be canceled which have the given element anywhere in their
+ * patch.
+ * @param element element to search for.
+ * @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
+ }
+ }
+ }
+ } else {
+ if (element.equals(currentUpdate.getElement())) {
+ currentUpdate.cancel();
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Cancels updates that have paths under the given path.
+ */
+ protected void cancelSubtreeUpdates(TreePath path) {
+ synchronized (fUpdatesInProgress) {
+ Iterator iterator = fUpdatesInProgress.iterator();
+ while (iterator.hasNext()) {
+ ILabelUpdate currentUpdate = (ILabelUpdate) iterator.next();
+ if (currentUpdate.getElementPath().startsWith(path, null)) {
+ currentUpdate.cancel();
+ }
+ }
+ }
+ }
+
/**
* Returns the presentation context for this label provider.
*
@@ -298,7 +398,6 @@ public class TreeModelLabelProvider extends ColumnLabelProvider implements ITree
updates = (LabelUpdate[]) fComplete.toArray(new LabelUpdate[fComplete.size()]);
fComplete = null;
}
- //System.out.println("Changed Labels: " + updates.length);
for (int i = 0; i < updates.length; i++) {
updates[i].update();
}
@@ -396,4 +495,9 @@ public class TreeModelLabelProvider extends ColumnLabelProvider implements ITree
}
}
+
+ public void modelChanged(IModelDelta delta, IModelProxy proxy) {
+ delta.accept(fVisitor);
+ }
+
}

Back to the top