diff options
Diffstat (limited to 'dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/ExpressionVMProvider.java')
-rw-r--r-- | dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/ExpressionVMProvider.java | 379 |
1 files changed, 379 insertions, 0 deletions
diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/ExpressionVMProvider.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/ExpressionVMProvider.java new file mode 100644 index 00000000000..3f608a9dc0a --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/ExpressionVMProvider.java @@ -0,0 +1,379 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 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.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.service.ICachingService; +import org.eclipse.cdt.dsf.debug.service.IExpressions; +import org.eclipse.cdt.dsf.debug.service.IRegisters; +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.numberformat.FormattedValuePreferenceStore; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.numberformat.IFormattedValuePreferenceStore; +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.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.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. + if (!getActiveModelProxies().isEmpty()) { + return ((ExpressionVMProviderModelProxyStrategy)getActiveModelProxies().get(0)).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() { + + IFormattedValuePreferenceStore prefStore = FormattedValuePreferenceStore.getDefault(); + + /* + * 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. + */ + ExpressionManagerVMNode expressionManagerNode = new ExpressionManagerVMNode(this); + addChildNodes(rootNode, new IVMNode[] {expressionManagerNode}); + + /* + * 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(prefStore, this, getSession(), syncRegDataAccess); + addChildNodes(registerGroupNode, new IExpressionVMNode[] {registerNode}); + + /* + * Create the next level which is the bit-field level. + */ + IVMNode bitFieldNode = new RegisterBitFieldVMNode(prefStore, 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. + */ + IExpressionVMNode variableNode = new VariableVMNode(prefStore, this, getSession(), syncvarDataAccess); + addChildNodes(variableNode, new IExpressionVMNode[] {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[] {registerGroupNode, variableNode}); + + /* + * Let the work know which is the top level node. + */ + setRootNode(rootNode); + } + + /** + * 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. + } + } +} |