/******************************************************************************* * Copyright (c) 2006, 2010 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.debug.ui.viewmodel.expression; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.RejectedExecutionException; import org.eclipse.cdt.dsf.concurrent.DsfRunnable; import org.eclipse.cdt.dsf.concurrent.RequestMonitor; import org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.DsfCastToTypeSupport; import org.eclipse.cdt.dsf.debug.service.ICachingService; import org.eclipse.cdt.dsf.debug.service.IExpressions; import org.eclipse.cdt.dsf.debug.service.IExpressions2; import org.eclipse.cdt.dsf.debug.service.IRegisters; import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent; import org.eclipse.cdt.dsf.debug.ui.DsfDebugUITools; import org.eclipse.cdt.dsf.debug.ui.IDsfDebugUIConstants; import org.eclipse.cdt.dsf.debug.ui.viewmodel.register.RegisterBitFieldVMNode; import org.eclipse.cdt.dsf.debug.ui.viewmodel.register.RegisterGroupVMNode; import org.eclipse.cdt.dsf.debug.ui.viewmodel.register.RegisterVMNode; import org.eclipse.cdt.dsf.debug.ui.viewmodel.register.SyncRegisterDataAccess; import org.eclipse.cdt.dsf.debug.ui.viewmodel.variable.SyncVariableDataAccess; import org.eclipse.cdt.dsf.debug.ui.viewmodel.variable.VariableVMNode; import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; import org.eclipse.cdt.dsf.service.DsfServicesTracker; import org.eclipse.cdt.dsf.service.DsfSession; import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMAdapter; import org.eclipse.cdt.dsf.ui.viewmodel.DefaultVMContentProviderStrategy; import org.eclipse.cdt.dsf.ui.viewmodel.IRootVMNode; import org.eclipse.cdt.dsf.ui.viewmodel.IVMModelProxy; import org.eclipse.cdt.dsf.ui.viewmodel.IVMNode; import org.eclipse.cdt.dsf.ui.viewmodel.VMDelta; import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider; import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.RootDMVMNode; import org.eclipse.cdt.dsf.ui.viewmodel.update.AutomaticUpdatePolicy; import org.eclipse.cdt.dsf.ui.viewmodel.update.IVMUpdatePolicy; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.model.IExpression; import org.eclipse.debug.internal.core.IExpressionsListener2; import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation; import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.TreePath; /** * The expression provider is used to populate the contents of the expressions * view. The node hierarchy in this view is a little different than in a typical * provider: the expression manager node should be registered as the single child * of the root node and no nodes should be registered as children of expression node. * Instead the top level expression nodes should be registered with a call to * {@link #setExpressionNodes(IExpressionVMNode[])}. And each expression node can * have its own sub-hierarchy of elements as needed. However all nodes configured * with this provider (with the exception of the root and the expression manager) * should implement {@link IExpressionVMNode}. */ @SuppressWarnings("restriction") public class ExpressionVMProvider extends AbstractDMVMProvider implements IExpressionsListener2 { private IExpressionVMNode[] fExpressionNodes; private IPropertyChangeListener fPreferencesListener = new IPropertyChangeListener() { public void propertyChange(PropertyChangeEvent event) { String property = event.getProperty(); if (property.equals(IDsfDebugUIConstants.PREF_WAIT_FOR_VIEW_UPDATE_AFTER_STEP_ENABLE)) { IPreferenceStore store = DsfDebugUITools.getPreferenceStore(); setDelayEventHandleForViewUpdate(store.getBoolean(property)); } } }; private IPropertyChangeListener fPresentationContextListener = new IPropertyChangeListener() { public void propertyChange(PropertyChangeEvent event) { handleEvent(event); } }; public ExpressionVMProvider(AbstractVMAdapter adapter, IPresentationContext context, DsfSession session) { super(adapter, context, session); context.addPropertyChangeListener(fPresentationContextListener); IPreferenceStore store = DsfDebugUITools.getPreferenceStore(); store.addPropertyChangeListener(fPreferencesListener); setDelayEventHandleForViewUpdate(store.getBoolean(IDsfDebugUIConstants.PREF_WAIT_FOR_VIEW_UPDATE_AFTER_STEP_ENABLE)); // The VM provider has to handle all events that result in model deltas. // Add the provider as listener to expression changes events. DebugPlugin.getDefault().getExpressionManager().addExpressionListener(this); configureLayout(); } @Override protected DefaultVMContentProviderStrategy createContentStrategy() { return new ExpressionVMProviderContentStragegy(this); } @Override protected IVMModelProxy createModelProxyStrategy(Object rootElement) { return new ExpressionVMProviderModelProxyStrategy(this, rootElement); } /** * Updates the given expression element. This method is used by the * expression manager node to obtain a view model element based on the * {@link IExpression} retrieved from the expression manager. The * implementation of this method (which is in the content strategy), * checks the configured expression nodes to see which one can * process the given expression, when it finds it it delegates * to that expression node's {@link IExpressionVMNode#update(IExpressionUpdate)} * method. * @param update Expression update to process. */ public void update(IExpressionUpdate update) { ((ExpressionVMProviderContentStragegy)getContentStrategy()).update(update); } /** * Retrieves the delta flags that can be generated for the given expression * and the given event. This method is used by the * expression manager node to obtain the delta flags based on the * {@link IExpression} retrieved from the expression manager. The * implementation of this method (which is in the model proxy strategy), * checks the configured expression nodes to see which one can * process the given expression, when it finds it it delegates * to that expression node's {@link IExpressionVMNode#getDeltaFlagsForExpression(IExpression, Object)} * method. */ public int getDeltaFlagsForExpression(IExpression expression, Object event) { // Workaround: find the first active proxy and use it. final List<IVMModelProxy> activeModelProxies= getActiveModelProxies(); int count = activeModelProxies.size(); if (count > 0) { return ((ExpressionVMProviderModelProxyStrategy)activeModelProxies.get(count - 1)).getDeltaFlagsForExpression(expression, event); } return 0; } /** * Builds the model delta based on the given expression * and the given event. This method is used by the * expression manager to build the delta based on the * {@link IExpression} retrieved from the expression manager. The * implementation of this method (which is in the model proxy strategy), * checks the configured expression nodes to see which one can * process the given expression, when it finds it it delegates * to that expression node's {@link IExpressionVMNode#buildDeltaForExpression(IExpression, int, Object, ModelDelta, TreePath, RequestMonitor)} * and {@link IExpressionVMNode#buildDeltaForExpressionElement(Object, int, Object, ModelDelta, RequestMonitor) * methods. */ public void buildDeltaForExpression(final IExpression expression, final int expressionElementIdx, final Object event, final VMDelta parentDelta, final TreePath path, final RequestMonitor rm) { // Workaround: find the first active proxy and use it. if (!getActiveModelProxies().isEmpty()) { ((ExpressionVMProviderModelProxyStrategy)getActiveModelProxies().get(0)).buildDeltaForExpression( expression, expressionElementIdx, event, parentDelta, path, rm); } else { rm.done(); } } /** * Configures the given nodes as the top-level expression nodes. */ protected void setExpressionNodes(IExpressionVMNode[] nodes) { fExpressionNodes = nodes; // Call the base class to make sure that the nodes are also // returned by the getAllNodes method. for (IExpressionVMNode node : nodes) { addNode(node); } } /** * Returns the list of configured top-level expression nodes. * @return */ public IExpressionVMNode[] getExpressionNodes() { return fExpressionNodes; } /** * Configures the nodes of this provider. This method may be over-ridden by * sub classes to create an alternate configuration in this provider. */ protected void configureLayout() { /* * Allocate the synchronous data providers. */ SyncRegisterDataAccess syncRegDataAccess = new SyncRegisterDataAccess(getSession()); SyncVariableDataAccess syncvarDataAccess = new SyncVariableDataAccess(getSession()) ; /* * Create the top level node which provides the anchor starting point. */ IRootVMNode rootNode = new RootDMVMNode(this); /* * Now the Over-arching management node. */ if (IDsfDebugUIConstants.ID_EXPRESSION_HOVER.equals(getPresentationContext().getId())) { SingleExpressionVMNode expressionManagerNode = new SingleExpressionVMNode(this); addChildNodes(rootNode, new IVMNode[] { expressionManagerNode }); } else { ExpressionManagerVMNode expressionManagerNode = new ExpressionManagerVMNode(this); addChildNodes(rootNode, new IVMNode[] {expressionManagerNode}); } // Disabled expression node intercepts disabled expressions and prevents them from being // evaluated by other nodes. IExpressionVMNode disabledExpressionNode = new DisabledExpressionVMNode(this); /* * The expression view wants to support fully all of the components of the register view. */ IExpressionVMNode registerGroupNode = new RegisterGroupVMNode(this, getSession(), syncRegDataAccess); IExpressionVMNode registerNode = new RegisterVMNode(this, getSession(), syncRegDataAccess); addChildNodes(registerGroupNode, new IExpressionVMNode[] {registerNode}); /* * Create the next level which is the bit-field level. */ IVMNode bitFieldNode = new RegisterBitFieldVMNode(this, getSession(), syncRegDataAccess); addChildNodes(registerNode, new IVMNode[] { bitFieldNode }); /* * Create the support for the SubExpressions. Anything which is brought into the expressions * view comes in as a fully qualified expression so we go directly to the SubExpression layout * node. */ VariableVMNode variableNode = new VariableVMNode(this, getSession(), syncvarDataAccess); addChildNodes(variableNode, new IExpressionVMNode[] {variableNode}); /* * Hook up IExpressions2 if it exists. */ hookUpCastingSupport(syncvarDataAccess, variableNode); /* * Tell the expression node which sub-nodes it will directly support. It is very important * that the variables node be the last in this chain. The model assumes that there is some * form of metalanguage expression syntax which each of the nodes evaluates and decides if * they are dealing with it or not. The variables node assumes that the expression is fully * qualified and there is no analysis or subdivision of the expression it will parse. So it * it currently the case that the location of the nodes within the array being passed in is * the order of search/evaluation. Thus variables wants to be last. Otherwise it would just * assume what it was passed was for it and the real node which wants to handle it would be * left out in the cold. */ setExpressionNodes(new IExpressionVMNode[] {disabledExpressionNode, registerGroupNode, variableNode}); /* * Let the work know which is the top level node. */ setRootNode(rootNode); } private void hookUpCastingSupport(final SyncVariableDataAccess syncvarDataAccess, final VariableVMNode variableNode) { try { getSession().getExecutor().execute(new DsfRunnable() { public void run() { DsfServicesTracker tracker = new DsfServicesTracker(DsfUIPlugin.getBundleContext(), getSession().getId()); IExpressions2 expressions2 = tracker.getService(IExpressions2.class); if (expressions2 != null) { variableNode.setCastToTypeSupport(new DsfCastToTypeSupport( getSession(), ExpressionVMProvider.this, syncvarDataAccess)); } tracker.dispose(); } }); } catch (RejectedExecutionException e) { // Session disposed, ignore. } } /** * Finds the expression node which can parse the given expression. This * method is used by the expression content and model proxy strategies. * * @param parentNode The parent of the nodes to search. If <code>null</code>, * then the top level expressions will be searched. * @param expression The expression object. * @return The matching expression node. */ public IExpressionVMNode findNodeToParseExpression(IExpressionVMNode parentNode, IExpression expression) { IVMNode[] childNOdes; if (parentNode == null) { childNOdes = getExpressionNodes(); } else { childNOdes = getChildVMNodes(parentNode); } for (IVMNode childNode : childNOdes) { if (childNode instanceof IExpressionVMNode) { IExpressionVMNode childExpressionNode = (IExpressionVMNode)childNode; if (childExpressionNode.canParseExpression(expression)) { return childExpressionNode; } else if (!childExpressionNode.equals(parentNode)) { // The above check is to make sure that child isn't the same as // parent to avoid recursive loops. IExpressionVMNode matchingNode = findNodeToParseExpression(childExpressionNode, expression); if (matchingNode != null) { return matchingNode; } } } } return null; } @Override public void dispose() { DebugPlugin.getDefault().getExpressionManager().removeExpressionListener(this); DsfDebugUITools.getPreferenceStore().removePropertyChangeListener(fPreferencesListener); getPresentationContext().removePropertyChangeListener(fPresentationContextListener); super.dispose(); } @Override public IColumnPresentation createColumnPresentation(IPresentationContext context, Object element) { return new ExpressionColumnPresentation(); } @Override public String getColumnPresentationId(IPresentationContext context, Object element) { return ExpressionColumnPresentation.ID; } @Override protected IVMUpdatePolicy[] createUpdateModes() { return new IVMUpdatePolicy[] { new AutomaticUpdatePolicy(), new ExpressionsManualUpdatePolicy(), new ExpressionsBreakpointHitUpdatePolicy() }; } public void expressionsAdded(IExpression[] expressions) { expressionsListChanged(ExpressionsChangedEvent.Type.ADDED, expressions, -1); } public void expressionsRemoved(IExpression[] expressions) { expressionsListChanged(ExpressionsChangedEvent.Type.REMOVED, expressions, -1); } public void expressionsInserted(IExpression[] expressions, int index) { expressionsListChanged(ExpressionsChangedEvent.Type.INSERTED, expressions, index); } public void expressionsMoved(IExpression[] expressions, int index) { expressionsListChanged(ExpressionsChangedEvent.Type.MOVED, expressions, index); } public void expressionsChanged(IExpression[] expressions) { expressionsListChanged(ExpressionsChangedEvent.Type.CHANGED, expressions, -1); } private void expressionsListChanged(ExpressionsChangedEvent.Type type, IExpression[] expressions, int index) { Set<Object> rootElements = new HashSet<Object>(); for (IVMModelProxy proxy : getActiveModelProxies()) { rootElements.add(proxy.getRootElement()); } handleEvent(new ExpressionsChangedEvent(type, rootElements, expressions, index)); } @Override protected boolean canSkipHandlingEvent(Object newEvent, Object eventToSkip) { // To optimize the performance of the view when stepping rapidly, skip all // other events when a suspended event is received, including older suspended // events. return newEvent instanceof ISuspendedDMEvent; } @Override public void refresh() { super.refresh(); try { getSession().getExecutor().execute(new DsfRunnable() { public void run() { DsfServicesTracker tracker = new DsfServicesTracker(DsfUIPlugin.getBundleContext(), getSession().getId()); IExpressions expressionsService = tracker.getService(IExpressions.class); if (expressionsService instanceof ICachingService) { ((ICachingService)expressionsService).flushCache(null); } IRegisters registerService = tracker.getService(IRegisters.class); if (registerService instanceof ICachingService) { ((ICachingService)registerService).flushCache(null); } tracker.dispose(); } }); } catch (RejectedExecutionException e) { // Session disposed, ignore. } } @Override public void update(IViewerInputUpdate update) { if (IDsfDebugUIConstants.ID_EXPRESSION_HOVER.equals(getPresentationContext().getId())) { Object input = update.getElement(); if (input instanceof IExpressionDMContext) { IExpressionDMContext dmc = (IExpressionDMContext) input; SingleExpressionVMNode vmNode = (SingleExpressionVMNode) getChildVMNodes(getRootVMNode())[0]; vmNode.setExpression(dmc); final IDMVMContext viewerInput= vmNode.createVMContext(dmc); // provide access to viewer (needed by details pane) getPresentationContext().setProperty("__viewerInput", viewerInput); //$NON-NLS-1$ update.setInputElement(viewerInput); update.done(); return; } } super.update(update); } }