Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/TreeModelContentProvider.java')
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/TreeModelContentProvider.java1589
1 files changed, 1246 insertions, 343 deletions
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);
+ }
}
-
}

Back to the top