/******************************************************************************* * Copyright (c) 2006, 2012 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * IBM Corporation - initial API and implementation * Pawel Piech (Wind River) - added support for a virtual tree model viewer (Bug 242489) *******************************************************************************/ package org.eclipse.debug.internal.ui.viewers.model; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.debug.internal.core.commands.Request; import org.eclipse.debug.internal.ui.DebugUIPlugin; 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.swt.widgets.Display; /** * @since 3.3 */ public abstract class ViewerUpdateMonitor extends Request implements IViewerUpdate { private TreeModelContentProvider fContentProvider; /** * Element's tree path */ private TreePath fElementPath; /** * Element */ private Object fElement; /** * Element content provider */ private IElementContentProvider fElementContentProvider; /** * Whether this request's 'done' method has been called. */ private boolean fDone = false; /** * Whether this request has been started */ private boolean fStarted = false; /** * Viewer input at the time the request was made */ private Object fViewerInput = null; /** * Whether this update has been delegated to another content provider * @since 3.4 */ private boolean fIsDelegated = false; /** * Presentation context */ private IPresentationContext fContext; /** * 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(TreeModelContentProvider contentProvider, Object viewerInput, TreePath elementPath, Object element, IElementContentProvider elementContentProvider, IPresentationContext context) { fContext = context; // Bug 380288: Catch and log a race condition where the viewer input is null. if (viewerInput == null) { DebugUIPlugin.log(new NullPointerException("Input to viewer update should not be null")); //$NON-NLS-1$ } fViewerInput = viewerInput; fElementContentProvider = elementContentProvider; fContentProvider = contentProvider; fElement = element; fElementPath = elementPath; } /** * Returns the scheduling rule for viewer update job. * * @return rule or null */ protected ISchedulingRule getUpdateSchedulingRule() { return AsynchronousSchedulingRuleFactory.getDefault().newSerialPerObjectRule(getContentProvider()); } /** * Returns the model content provider this update is being performed for. * * @return the model content provider this update is being performed for */ protected TreeModelContentProvider getContentProvider() { return fContentProvider; } /** * Returns the element content provider to use for this request * * @return element content provider */ protected IElementContentProvider getElementContentProvider() { return fElementContentProvider; } /* (non-Javadoc) * @see org.eclipse.core.runtime.IProgressMonitor#done() */ @Override public final void done() { synchronized (this) { if (isDone()) { return; } fDone = true; } scheduleViewerUpdate(); } /** * Returns whether this request is done yet. * * @return True if this update is done. */ protected synchronized boolean isDone() { return fDone; } protected void scheduleViewerUpdate() { getContentProvider().scheduleViewerUpdate(this); } /** * Notification this update has been completed and should now be applied to * this update's viewer. This method is called in the UI thread. */ protected abstract void performUpdate(); /* (non-Javadoc) * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate#getPresentationContext() */ @Override public IPresentationContext getPresentationContext() { return fContext; } /* (non-Javadoc) * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate#getElement() */ @Override public Object getElement() { return fElement; } /* (non-Javadoc) * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate#getElementPath() */ @Override public TreePath getElementPath() { return fElementPath; } /** * Returns whether this request can coalesce the given request, and performs the * coalesce if it can. * * @param update request to coalesce with this request * @return whether it worked */ abstract boolean coalesce(ViewerUpdateMonitor update); /** * 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); /** * Starts this request. Subclasses must override startRequest(). */ protected void start() { synchronized (this) { if (fStarted) { return; } fStarted = true; } getContentProvider().updateStarted(this); if (!isCanceled()) { startRequest(); } else { done(); } } /* (non-Javadoc) * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate#getViewerInput() */ @Override public Object getViewerInput() { return fViewerInput; } /** * Subclasses must override to initiate specific request types. */ abstract void startRequest(); /** * Returns the priority of this request. Subclasses must override. The * highest priority is 1. Priorities indicate the order that waiting * requests should be started in (for example, 'hasChildren' before 'update child count'). * * @return priority */ abstract int getPriority(); /** * Returns a path used to schedule this request - i.e. based on this path, this * request will be scheduled to run when no requests are running against the * same element or a parent of the element denoted by the path. * * @return path used to schedule request */ abstract TreePath getSchedulingPath(); /** * Sets whether this update has been delegated to another content provider * @param delegated whether the update has been delegated * @since 3.4 */ public void setDelegated(boolean delegated) { fIsDelegated = delegated; } /** * @return whether this update has been delegated to another content provider * @since 3.4 */ public boolean isDelegated() { return fIsDelegated; } @Override public boolean equals(Object obj) { if (obj instanceof ViewerUpdateMonitor) { return doEquals((ViewerUpdateMonitor)obj); } return false; } @Override 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 Display.asyncExec(). 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); } } } }