blob: 71fdb44a9903a88fccbbc27111e658b65678bfaf [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2016 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.cdt.dsf.ui.viewmodel;
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.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.internal.DsfPlugin;
import org.eclipse.cdt.dsf.internal.LoggingUtils;
import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin;
import org.eclipse.cdt.dsf.ui.concurrent.SimpleDisplayExecutor;
import org.eclipse.cdt.dsf.ui.concurrent.ViewerDataRequestMonitor;
import org.eclipse.cdt.dsf.ui.viewmodel.update.UserEditEvent;
import org.eclipse.core.runtime.Platform;
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.IColumnPresentation;
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.IHasChildrenUpdate;
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.IPresentationContext;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputProvider;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputUpdate;
import org.eclipse.swt.widgets.Display;
/**
* View model provider implements the asynchronous view model functionality for
* a single view. This provider is just a holder which further delegates the
* model provider functionality to the view model nodes that need
* to be configured with each provider.
*
* <p/>
* The view model provider, often does not provide the model for the entire
* view. Rather, it needs to be able to plug in at any level in the viewer's
* content model and provide data for a sub-tree.
*
* <p/>
* Clients are intended to extend this class.
*
* @see IModelProxy
* @see IVMNode
*
* @since 1.0
*/
abstract public class AbstractVMProvider implements IVMProvider, IVMEventListener
{
// debug flags
/** @since 1.1 */
public static String DEBUG_PRESENTATION_ID = null;
/** @since 1.1 */
public static boolean DEBUG_CONTENT_PROVIDER = false;
/** @since 1.1 */
public static boolean DEBUG_DELTA = false;
static {
DEBUG_PRESENTATION_ID = Platform.getDebugOption("org.eclipse.cdt.dsf.ui/debug/vm/presentationId"); //$NON-NLS-1$
if (!DsfUIPlugin.DEBUG || "".equals(DEBUG_PRESENTATION_ID)) { //$NON-NLS-1$
DEBUG_PRESENTATION_ID = null;
}
DEBUG_CONTENT_PROVIDER = DsfUIPlugin.DEBUG && Boolean.parseBoolean(
Platform.getDebugOption("org.eclipse.cdt.dsf.ui/debug/vm/contentProvider")); //$NON-NLS-1$
DEBUG_DELTA = DsfUIPlugin.DEBUG && Boolean.parseBoolean(
Platform.getDebugOption("org.eclipse.cdt.dsf.ui/debug/vm/delta")); //$NON-NLS-1$
}
/** Reference to the VM adapter that owns this provider */
private final AbstractVMAdapter fVMAdapter;
/** The presentation context that this provider is associated with */
private final IPresentationContext fPresentationContext;
/**
* The executor that this VM provider operates in. This executor will be
* initialized properly when we can access the display from the
* IPresentationContext object (bug 213629). For now utilize the
* assumption that there is only one display.
*/
private final Executor fExecutor = SimpleDisplayExecutor.getSimpleDisplayExecutor(Display.getDefault());
/**
* The element content provider implementation that this provider delegates to.
* Sub-classes may override the content strategy used for custom functionality.
*/
private final IElementContentProvider fContentStrategy;
/**
* The list of active model proxies in this provider. A new model
* proxy is created when a viewer has a new input element
* (see {@link #createModelProxy(Object, IPresentationContext)}).
* Typically there will be only one active model proxy in a given
* provider. However, if a view model provider fills only a sub-tree
* in a viewer, and there are several sub-trees active in the same viewer
* at the same time, each of these sub-trees will have it's own model
* proxy.
*/
private List<IVMModelProxy> fActiveModelProxies = new LinkedList<IVMModelProxy>();
/**
* Convenience constant.
*/
private static final IVMNode[] EMPTY_NODES_ARRAY = new IVMNode[0];
/**
* The mapping of parent to child nodes.
*/
private Map<IVMNode,IVMNode[]> fChildNodesMap =
new HashMap<IVMNode,IVMNode[]>();
/**
* Cached array of all the configured view model nodes. It is generated
* based on the child nodes map.
*/
private IVMNode[] fNodesListCache = null;
/**
* Flag indicating that the provider is disposed.
*/
private boolean fDisposed = false;
/**
* The root node for this model provider. The root layout node could be
* null when first created, to allow sub-classes to properly configure the
* root node in the sub-class constructor.
*/
private IRootVMNode fRootNode;
private class EventInfo {
EventInfo(Object event, RequestMonitor rm) {
fEvent = event;
fClientRm = rm;
}
Object fEvent;
RequestMonitor fClientRm;
}
private class ModelProxyEventQueue {
/** The event actively being handled */
EventInfo fCurrentEvent;
/**
* The request monitor we created to handle fCurrentEvent. It is
* responsible for calling <code>done</code> on the client RM of that
* event.
*/
RequestMonitor fCurrentRm;
/** The queue */
List<EventInfo> fEventQueue = new LinkedList<EventInfo>();
}
private Map<IVMModelProxy, ModelProxyEventQueue> fProxyEventQueues = new HashMap<IVMModelProxy, ModelProxyEventQueue>();
/**
* Constructs the view model provider for given DSF session. The
* constructor is thread-safe to allow VM provider to be constructed
* synchronously when a call to getAdapter() is made on an element
* in a view.
*/
public AbstractVMProvider(AbstractVMAdapter adapter, IPresentationContext presentationContext) {
fVMAdapter = adapter;
fPresentationContext = presentationContext;
fContentStrategy = createContentStrategy();
}
@Override
public IPresentationContext getPresentationContext() {
return fPresentationContext;
}
@Override
public AbstractVMAdapter getVMAdapter() {
return fVMAdapter;
}
/**
* Creates the strategy class that will be used to implement the content
* provider interface of this view model provider. This method can be
* overridden by sub-classes to provider custom content provider strategy.
* <p/>
* Note this method can be called by the base class constructor, therefore
* it should not reference any fields initialized in the sub-class.
*
* @return New content provider implementation.
*/
protected IElementContentProvider createContentStrategy() {
return new DefaultVMContentProviderStrategy(this);
}
/**
* Access method for the content provider strategy.
*
* @return Content provider implementation currently being used by this
* class.
*/
protected IElementContentProvider getContentStrategy() {
return fContentStrategy;
}
/**
* Creates the strategy class that will be used to implement the content
* model proxy of this view model provider. It is normally called by
* {@link #createModelProxy(Object, IPresentationContext)} every time the
* input in the viewer is updated. This method can be overridden by
* sub-classes to provider custom model proxy strategy.
*
* @return New model proxy implementation.
*/
protected IVMModelProxy createModelProxyStrategy(Object rootElement) {
return new DefaultVMModelProxyStrategy(this, rootElement);
}
/**
* Returns the list of active proxies in this provider. The returned
* list is not a copy and if a sub-class modifies this list, it will
* modify the current list of active proxies. This allows the
* sub-classes to change how the active proxies are managed and
* retained.
*/
protected List<IVMModelProxy> getActiveModelProxies() {
return fActiveModelProxies;
}
/**
* Processes the given event in the given provider, sending model
* deltas if necessary.
*/
public void handleEvent(final Object event) {
handleEvent(event, null);
}
/**
* {@inheritDoc}
* @since 1.1
*/
@Override
public void handleEvent(final Object event, RequestMonitor rm) {
if (DEBUG_DELTA && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) {
trace(event, null, null, EventHandlerAction.received);
}
CountingRequestMonitor crm = new CountingRequestMonitor(getExecutor(), rm);
final List<IVMModelProxy> activeModelProxies= new ArrayList<IVMModelProxy>(getActiveModelProxies());
crm.setDoneCount(activeModelProxies.size());
for (final IVMModelProxy proxyStrategy : activeModelProxies) {
// If the event is generated by the model proxy, only process it for the proxy that created it.
if ( event instanceof ModelProxyInstalledEvent &&
!((ModelProxyInstalledEvent)event).getModelProxy().equals(proxyStrategy) )
{
crm.done();
continue;
}
// Process the event only if there are potential delta flags that may be generated.
// Also, process the event if it is a result of the user modifying something
// so that the cache is properly updated.
if (proxyStrategy.isDeltaEvent(event) || event instanceof UserEditEvent) {
if (!fProxyEventQueues.containsKey(proxyStrategy)) {
fProxyEventQueues.put(proxyStrategy, new ModelProxyEventQueue());
}
// If the event queue is empty, directly handle the new event. Otherwise queue it.
final ModelProxyEventQueue queue = fProxyEventQueues.get(proxyStrategy);
if (queue.fCurrentEvent != null) {
assert queue.fCurrentRm != null;
// Iterate through the events in the queue and check if
// they can be skipped. If they can be skipped, then just
// mark their RM as done. Stop iterating through the queue
// if an event that cannot be skipped is encountered.
while (!queue.fEventQueue.isEmpty()) {
EventInfo eventToSkipInfo = queue.fEventQueue.get(queue.fEventQueue.size() - 1);
if (canSkipHandlingEvent(event, eventToSkipInfo.fEvent)) {
if (DEBUG_DELTA && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) {
trace(event, eventToSkipInfo.fEvent, proxyStrategy, EventHandlerAction.skipped);
}
queue.fEventQueue.remove(queue.fEventQueue.size() - 1);
eventToSkipInfo.fClientRm.done();
} else {
break;
}
}
// If the queue is empty check if the current event
// being processed can be skipped. If so, cancel its
// processing
if (queue.fEventQueue.isEmpty() && canSkipHandlingEvent(event, queue.fCurrentEvent.fEvent)) {
if (DEBUG_DELTA && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) {
trace(event, queue.fCurrentEvent.fEvent, proxyStrategy, EventHandlerAction.canceled);
}
queue.fCurrentRm.cancel();
}
if (DEBUG_DELTA && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) {
trace(event, null, proxyStrategy, EventHandlerAction.queued);
}
queue.fEventQueue.add(new EventInfo(event, crm));
} else {
doHandleEvent(queue, proxyStrategy, new EventInfo(event, crm));
}
} else {
crm.done();
}
}
// Discard the event queues of proxies that have been removed
List<IVMModelProxy> activeProxies = getActiveModelProxies();
for (Iterator<IVMModelProxy> itr = fProxyEventQueues.keySet().iterator(); itr.hasNext();) {
if (!activeProxies.contains(itr.next())) {
itr.remove();
}
}
}
private void doHandleEvent(final ModelProxyEventQueue queue, final IVMModelProxy proxyStrategy, final EventInfo eventInfo) {
// Do handle event is a sort of a recursive asynchronous method. It
// calls the asynchronous handleEvent() to process the event from the
// eventInfo argument. When handleEvent() completes, this method
// (doHandleEvent) checks whether there is any more events in the queue
// that should be handled. If there are, doHandleEvent calls itself
// to process the next event in the queue.
assert queue.fCurrentEvent == null && queue.fCurrentRm == null;
queue.fCurrentEvent = eventInfo;
queue.fCurrentRm = new RequestMonitor(getExecutor(), eventInfo.fClientRm) {
@Override
protected void handleCompleted() {
eventInfo.fClientRm.done();
queue.fCurrentEvent = null;
queue.fCurrentRm = null;
if (!queue.fEventQueue.isEmpty() && !fDisposed) {
EventInfo nextEventInfo = queue.fEventQueue.remove(0);
doHandleEvent(queue, proxyStrategy, nextEventInfo);
}
}
};
handleEvent(proxyStrategy, eventInfo.fEvent, queue.fCurrentRm);
}
/**
* Handles the given event for the given proxy strategy.
* <p>
* This method is called by the base {@link #handleEvent(Object)}
* implementation to handle the given event using the given model proxy.
* The default implementation of this method checks whether the given
* proxy is active and if the proxy is active, it is called to generate the
* delta which is then sent to the viewer.
* </p>
* @param proxyStrategy Model proxy strategy to use to process this event.
* @param event Event to process.
* @param rm Request monitor to call when processing the event is
* completed.
*/
protected void handleEvent(final IVMModelProxy proxyStrategy, final Object event, final RequestMonitor rm) {
if (!proxyStrategy.isDisposed()) {
if (DEBUG_DELTA && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) {
trace(event, null, proxyStrategy, EventHandlerAction.processing);
}
proxyStrategy.createDelta(
event,
new DataRequestMonitor<IModelDelta>(getExecutor(), rm) {
@Override
public void handleSuccess() {
proxyStrategy.fireModelChanged(getData());
if (DEBUG_DELTA && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) {
trace(event, null, proxyStrategy, EventHandlerAction.firedDeltaFor);
}
rm.done();
}
@Override public String toString() {
return "Result of a delta for event: '" + event.toString() + "' in VMP: '" + AbstractVMProvider.this + "'" + "\n" + getData().toString(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
}
});
} else {
rm.done();
}
}
/**
* Determines whether processing of a given event can be skipped. This
* method is called when there are multiple events waiting to be processed
* by the provider. As new events are received from the model, they are
* compared with the events in the queue using this method, events at the
* end of the queue are tested for removal. If this method returns that a
* given event can be skipped in favor of the new event, the skipped event
* is removed from the queue. This process is repeated with the new event
* until an event which cannot be stopped is found or the queue goes empty.
* <p>
* This method may be overriden by specific view model provider
* implementations extending this abstract class.
* </p>
* @param newEvent New event that was received from the model.
* @param eventToSkip Event which is currently at the end of the queue.
* @return True if the event at the end of the queue can be skipped in
* favor of the new event.
*/
protected boolean canSkipHandlingEvent(Object newEvent, Object eventToSkip) {
return false;
}
/** @since 1.1 */
@Override
public boolean shouldWaitHandleEventToComplete() {
return false;
}
@Override
public IRootVMNode getRootVMNode() {
return fRootNode;
}
@Override
public IVMNode[] getAllVMNodes() {
if (fNodesListCache != null) {
return fNodesListCache;
}
List<IVMNode> list = new ArrayList<IVMNode>();
for (IVMNode node : fChildNodesMap.keySet()) {
if (node != null) {
list.add(node);
}
}
fNodesListCache = list.toArray(new IVMNode[list.size()]);;
return fNodesListCache;
}
@Override
public IVMNode[] getChildVMNodes(IVMNode node) {
IVMNode[] retVal = fChildNodesMap.get(node);
if (retVal != null) {
return retVal;
}
return EMPTY_NODES_ARRAY;
}
/**
* Configures the given array of nodes as children of the given parent node.
* Sub-classes should call this method to define the hierarchy of nodes.
*/
protected void addChildNodes(IVMNode parentNode, IVMNode[] childNodes) {
// Add to the child nodes array.
IVMNode[] existingChildNodes = fChildNodesMap.get(parentNode);
if (existingChildNodes == null) {
fChildNodesMap.put(parentNode, childNodes);
} else {
IVMNode[] newNodes = new IVMNode[existingChildNodes.length + childNodes.length];
System.arraycopy(existingChildNodes, 0, newNodes, 0, existingChildNodes.length);
System.arraycopy(childNodes, 0, newNodes, existingChildNodes.length, childNodes.length);
fChildNodesMap.put(parentNode, newNodes);
}
// Make sure that each new expression node has an entry of its own.
for (IVMNode childNode : childNodes) {
addNode(childNode);
}
fNodesListCache = null;
}
/**
* Adds the given node to configured nodes, without creating any
* parent-child relationship for it. It is useful for providers which do have
* a strict tree hierarchy of nodes.
*/
protected void addNode(IVMNode node) {
if (!fChildNodesMap.containsKey(node)) {
fChildNodesMap.put(node, EMPTY_NODES_ARRAY);
}
}
/**
* Clears all configured nodes, including the root node. This allows a
* subclass to reset and reconfigure its nodes.
*/
protected void clearNodes() {
clearNodes(true);
for (IVMNode node : fChildNodesMap.keySet()) {
node.dispose();
}
fChildNodesMap.clear();
fRootNode = null;
}
/**
* Clears all configured nodes. This allows a subclass to reset and
* reconfigure its nodes.
*
* @param clearRootNode Flag indicating whether to also clear the root node.
* @since 2.1
*/
protected void clearNodes(boolean clearRootNode) {
for (IVMNode node : fChildNodesMap.keySet()) {
if ( !clearRootNode || !node.equals(getRootVMNode()) ) {
node.dispose();
}
}
fChildNodesMap.clear();
if (clearRootNode) {
fRootNode = null;
} else {
fChildNodesMap.put(getRootVMNode(), EMPTY_NODES_ARRAY);
}
}
/**
* Sets the root node for this provider.
*/
protected void setRootNode(IRootVMNode rootNode) {
fRootNode = rootNode;
}
/** Called to dispose the provider. */
@Override
public void dispose() {
clearNodes();
fRootNode = null;
fDisposed = true;
}
@Override
public void update(final IHasChildrenUpdate[] updates) {
fContentStrategy.update(updates);
}
@Override
public void update(final IChildrenCountUpdate[] updates) {
fContentStrategy.update(updates);
}
@Override
public void update(final IChildrenUpdate[] updates) {
fContentStrategy.update(updates);
}
/**
* Calls the given view model node to perform the given updates. This method
* is called by view model provider and its helper classes instead of
* calling the IVMNode method directly, in order to allow additional
* processing of the update. For example the AbstractCachingVMProvider
* overrides this method to optionally return the results for an update from
* a cache.
*
* [node] represents the type of the child element, not of the parent. In
* other words, the update requests are asking if one or more model elements
* of a particular type (thread, e.g.) have children. But [node] does not
* represent that type. It represents the type of the potential children
* (frame, e.g.)
*/
@Override
public void updateNode(final IVMNode node, IHasChildrenUpdate[] updates) {
IHasChildrenUpdate[] updateProxies = new IHasChildrenUpdate[updates.length];
for (int i = 0; i < updates.length; i++) {
final IHasChildrenUpdate update = updates[i];
if (DEBUG_CONTENT_PROVIDER && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) {
DsfUIPlugin.debug("updateNodeHasChildren(node = " + node + ", update = " + update + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
updateProxies[i] = new VMHasChildrenUpdate(
update,
new ViewerDataRequestMonitor<Boolean>(getExecutor(), update) {
@Override
protected void handleSuccess() {
update.setHasChilren(getData());
update.done();
}
@Override
protected void handleErrorOrWarning() {
if (getStatus().getCode() == IDsfStatusConstants.NOT_SUPPORTED) {
if (DEBUG_CONTENT_PROVIDER && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) {
DsfUIPlugin.debug("not-supported:updateNodeHasChildren(node = " + node + ", update = " + update + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
updateNode(
node,
new VMChildrenUpdate(
update, -1, -1,
new ViewerDataRequestMonitor<List<Object>>(getExecutor(), update) {
@Override
protected void handleSuccess() {
update.setHasChilren( !getData().isEmpty() );
update.done();
}
})
);
} else {
update.setStatus(getStatus());
update.done();
}
}
});
}
node.update(updateProxies);
}
/**
* Calls the given view model node to perform the given updates. This
* method is called by view model provider and it's helper classes instead
* of calling the IVMNode method directly, in order to allow additional
* processing of the update. For example the AbstractCachingVMProvider
* overrides this method to optionally return the results for an update from
* a cache.
*/
@Override
public void updateNode(final IVMNode node, final IChildrenCountUpdate update) {
if (DEBUG_CONTENT_PROVIDER && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) {
DsfUIPlugin.debug("updateNodeChildCount(node = " + node + ", update = " + update + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
node.update(new IChildrenCountUpdate[] {
new VMChildrenCountUpdate(
update,
new ViewerDataRequestMonitor<Integer>(getExecutor(), update) {
@Override
protected void handleSuccess() {
update.setChildCount(getData());
update.done();
}
@Override
protected void handleErrorOrWarning() {
if (getStatus().getCode() == IDsfStatusConstants.NOT_SUPPORTED) {
if (DEBUG_CONTENT_PROVIDER && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) {
DsfUIPlugin.debug("not-supported:updateNodeChildCount(node = " + node + ", update = " + update + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
updateNode(
node,
new VMChildrenUpdate(
update, -1, -1,
new ViewerDataRequestMonitor<List<Object>>(getExecutor(), update) {
@Override
protected void handleSuccess() {
update.setChildCount( getData().size() );
update.done();
}
})
);
} else {
super.handleErrorOrWarning();
}
}
})
});
}
/**
* Calls the given view model node to perform the given updates. This
* method is called by view model provider and it's helper classes instead
* of calling the IVMNode method directly, in order to allow additional
* processing of the update. For example the AbstractCachingVMProvider
* overrides this method to optionally return the results for an update from
* a cache.
*/
@Override
public void updateNode(IVMNode node, IChildrenUpdate update) {
if (DEBUG_CONTENT_PROVIDER && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) {
DsfUIPlugin.debug("updateNodeChildren(node = " + node + ", update = " + update + ")"); //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$
}
node.update(new IChildrenUpdate[] { update });
}
/**
* Returns whether this provider has been disposed.
*/
protected boolean isDisposed() {
return fDisposed;
}
/**
* The abstract provider uses a the display-thread executor so that the
* provider will operate on the same thread as the viewer. This way no
* synchronization is necessary when the provider is called by the viewer.
* Also, the display thread is likely to be shut down long after any of the
* view models are disposed, so the users of this abstract provider do not
* need to worry about the executor throwing the {@link RejectedExecutionException}
* exception.
*/
@Override
public Executor getExecutor() {
return fExecutor;
}
@Override
public IModelProxy createModelProxy(Object element, IPresentationContext context) {
// Iterate through the current active proxies to try to find a proxy with the same
// element and re-use it if found. Only disposed proxies can be re-used because
// multiple viewers cannot use the same proxy. Also at this time purge other proxies
// that are no longer installed.
IVMModelProxy proxy = null;
for (Iterator<IVMModelProxy> itr = getActiveModelProxies().iterator(); itr.hasNext();) {
IVMModelProxy next = itr.next();
if (next != null) {
if (next.getRootElement().equals(element) && next.isDisposed()) {
proxy = next;
} else if (next.isDisposed()) {
itr.remove();
}
}
}
if (proxy == null) {
proxy = createModelProxyStrategy(element);
getActiveModelProxies().add(proxy);
} else if (proxy.isDisposed()) {
// DSF is capable of re-using old proxies which were previously
// disposed. However, the viewer which installs a proxy using
// a background job to install the proxy calls
// IModelProxy.isDisposed(), to check whether the proxy was disposed
// before it could be installed. We need to clear the disposed flag
// of the re-used proxy here, otherwise the proxy will never get used.
// Calling init here will cause the init() method to be called twice
// so the IVMModelProxy needs to be prepared for that.
// See bug 241024.
proxy.init(context);
}
return proxy;
}
/**
* Creates the column presentation for the given object. This method is meant
* to be overriden by deriving class to provide view-specific functionality.
* The default is to return null, meaning no columns.
* <p>
* The viewer only reads the column presentation for the root/input element of
* the tree/table, so the VMProvider must be configured to own the root element
* in the view in order for this setting to be effective.
* <p>
* Note: since the IColumnEditorFactory interface is synchronous, and since
* column info is fairly static, this method is thread-safe, and it will
* not be called on the executor thread.
*
* @see IColumnPresentationFactory#createColumnPresentation(IPresentationContext, Object)
*/
@Override
public IColumnPresentation createColumnPresentation(IPresentationContext context, Object element) {
return null;
}
/**
* Returns the ID of the column presentation for the given object. This method
* is meant to be overriden by deriving class to provide view-specific
* functionality. The default is to return null, meaning no columns.
* <p>
* The viewer only reads the column presentation for the root/input element of
* the tree/table, so the VMProvider must be configured to own the root element
* in the view in order for this setting to be effective.
* <p>
* Note: since the IColumnEditorFactory interface is synchronous, and since
* column info is fairly static, this method is thread-safe, and it will
* not be called on the executor thread.
*
* @see IColumnEditorFactory#getColumnEditorId(IPresentationContext, Object)
*/
@Override
public String getColumnPresentationId(IPresentationContext context, Object element) {
return null;
}
/**
* Calculates the proxy input object to be used for the given input in the given
* viewer. By default no proxy object is used an the given element is used
* as the input into the view.
* <p>
* Sub classes can override this method for view-specific behavior.
*
* @see IViewerInputProvider
*/
@Override
public void update(IViewerInputUpdate update) {
update.setInputElement(update.getElement());
update.done();
}
/**
* Used for tracing event handling
*/
private enum EventHandlerAction {
received,
queued,
processing,
firedDeltaFor,
skipped,
canceled
}
/**
* Trace that we've reached a particular phase of the handling of an event
* for a particular proxy.
*
* @param event
* the event being handled
* @param skippedOrCanceledEvent
* for a 'skip' or 'cancel' action, this is the event that is
* being dismissed. Otherwise null
* @param proxy
* the target proxy; n/a (null) for a 'received' action.
* @param action
* what phased of the event handling has been reached
*/
private void trace(Object event, Object skippedOrCanceledEvent, IVMModelProxy proxy, EventHandlerAction action) {
assert DEBUG_DELTA;
StringBuilder str = new StringBuilder();
str.append(DsfPlugin.getDebugTime());
str.append(' ');
if (action == EventHandlerAction.skipped || action == EventHandlerAction.canceled) {
str.append(LoggingUtils.toString(this)).append(' ').append(action).append(" event ").append(LoggingUtils.toString(skippedOrCanceledEvent)).append(" because of event ").append(LoggingUtils.toString(event)); //$NON-NLS-1$ //$NON-NLS-2$
}
else {
str.append(LoggingUtils.toString(this)).append(' ').append(action).append(" event ").append(LoggingUtils.toString(event)); //$NON-NLS-1$
}
if (action != EventHandlerAction.received) {
str.append(" for proxy ").append(LoggingUtils.toString(proxy)).append(", whose root is ").append(LoggingUtils.toString(proxy.getRootElement())); //$NON-NLS-1$ //$NON-NLS-2$
}
DsfUIPlugin.debug(str.toString());
}
}