/******************************************************************************* * 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()); } }