From ab53de8c4f3d39ad57212bde6a62913b56992128 Mon Sep 17 00:00:00 2001 From: Anton Leherbauer Date: Thu, 17 Dec 2009 09:43:46 +0000 Subject: [PATCH] [255026] [expressions][cdi] Support for advanced expression hover --- .../ExpressionInformationControlCreator.java | 441 ++++++++++++++++++ .../debug/ui/AbstractDsfDebugTextHover.java | 75 ++- .../dsf/debug/ui/IDsfDebugUIConstants.java | 9 +- .../expression/ExpressionVMProvider.java | 29 +- .../expression/SingleExpressionVMNode.java | 327 +++++++++++++ 5 files changed, 875 insertions(+), 6 deletions(-) create mode 100644 dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/ExpressionInformationControlCreator.java create mode 100644 dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/SingleExpressionVMNode.java diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/ExpressionInformationControlCreator.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/ExpressionInformationControlCreator.java new file mode 100644 index 00000000000..c61b3863d6d --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/ExpressionInformationControlCreator.java @@ -0,0 +1,441 @@ +/******************************************************************************* + * Copyright (c) 2008, 2009 IBM Corporation 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: + * IBM Corporation - initial API and implementation + * Wind River Systems - adapted for DSF + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui; + +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; +import org.eclipse.cdt.dsf.debug.ui.IDsfDebugUIConstants; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.model.IVariable; +import org.eclipse.debug.internal.ui.SWTFactory; +import org.eclipse.debug.internal.ui.model.elements.ElementContentProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputRequestor; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdateListener; +import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ViewerInputService; +import org.eclipse.debug.internal.ui.views.variables.details.DefaultDetailPane; +import org.eclipse.debug.internal.ui.views.variables.details.DetailPaneProxy; +import org.eclipse.debug.internal.ui.views.variables.details.IDetailPaneContainer; +import org.eclipse.debug.ui.AbstractDebugView; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.jface.text.AbstractInformationControl; +import org.eclipse.jface.text.IInformationControl; +import org.eclipse.jface.text.IInformationControlCreator; +import org.eclipse.jface.text.IInformationControlExtension2; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.StructuredViewer; +import org.eclipse.jface.viewers.TreeSelection; +import org.eclipse.jface.viewers.ViewerFilter; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.SashForm; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Layout; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchPartSite; + +/** + * Creates an information control to display an expression in a hover control. + *
This class is derivative work from JDT's ExpressionInformationControlCreator. + * + * @noextend This class is not intended to be subclassed by clients. + * + * @since 2.1 + */ +@SuppressWarnings("restriction") +public class ExpressionInformationControlCreator implements IInformationControlCreator { + + class ExpressionInformationControl extends AbstractInformationControl implements IInformationControlExtension2, IViewerInputRequestor { + + /** + * Dialog setting key for height + */ + private static final String HEIGHT = "HEIGHT"; //$NON-NLS-1$ + + /** + * Dialog setting key for width. + */ + private static final String WIDTH = "WIDTH"; //$NON-NLS-1$ + + /** + * Dialog setting key for tree sash weight + */ + private static final String SASH_WEIGHT_TREE = "SashWeightTree"; //$NON-NLS-1$ + + /** + * Dialog setting key for details sash weight + */ + private static final String SASH_WEIGHT_DETAILS = "SashWeightDetails"; //$NON-NLS-1$ + + /** + * Variable to display. + */ + private Object fVariable; + + private TreeModelViewer fViewer; + private SashForm fSashForm; + private Composite fDetailPaneComposite; + private DetailPaneProxy fDetailPane; + private Tree fTree; + + private ViewerInputService fInputService; + + /** + * Creates the content for the root element of the tree viewer in the hover + */ + private class TreeRoot extends ElementContentProvider { + @Override + protected int getChildCount(Object element, IPresentationContext context, IViewerUpdate monitor) throws CoreException { + return 1; + } + @Override + protected Object[] getChildren(Object parent, int index, int length, IPresentationContext context, IViewerUpdate monitor) throws CoreException { + return new Object[] { fVariable }; + } + @Override + protected boolean supportsContextId(String id) { + return true; + } + } + + /** + * Inner class implementing IDetailPaneContainer methods. Handles changes to detail + * pane and provides limited access to the detail pane proxy. + */ + private class DetailPaneContainer implements IDetailPaneContainer { + + /* + * @see org.eclipse.debug.internal.ui.views.variables.details.IDetailPaneContainer#getCurrentPaneID() + */ + public String getCurrentPaneID() { + return fDetailPane.getCurrentPaneID(); + } + + /* + * @see org.eclipse.debug.internal.ui.views.variables.details.IDetailPaneContainer#getCurrentSelection() + */ + public IStructuredSelection getCurrentSelection() { + return (IStructuredSelection)fViewer.getSelection(); + } + + /* + * @see org.eclipse.debug.internal.ui.views.variables.details.IDetailPaneContainer#refreshDetailPaneContents() + */ + public void refreshDetailPaneContents() { + fDetailPane.display(getCurrentSelection()); + } + + /* + * @see org.eclipse.debug.internal.ui.views.variables.details.IDetailPaneContainer#getParentComposite() + */ + public Composite getParentComposite() { + return fDetailPaneComposite; + } + + /* + * @see org.eclipse.debug.internal.ui.views.variables.details.IDetailPaneContainer#getWorkbenchPartSite() + */ + public IWorkbenchPartSite getWorkbenchPartSite() { + return null; + } + + /* + * @see org.eclipse.debug.internal.ui.views.variables.details.IDetailPaneContainer#paneChanged(java.lang.String) + */ + public void paneChanged(String newPaneID) { + if (DefaultDetailPane.ID.equals(newPaneID)){ + fDetailPane.getCurrentControl().setBackground(getShell().getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND)); + } + } + + } + + /** + * Constructs a new control in the given shell. + * + * @param parentShell shell + * @param resize whether resize is supported + */ + ExpressionInformationControl(Shell parentShell, boolean resize) { + super(parentShell, resize); + create(); + } + + @Override + public Point computeSizeHint() { + IDialogSettings settings = getDialogSettings(false); + if (settings != null) { + int x = getIntSetting(settings, WIDTH); + if (x > 0) { + int y = getIntSetting(settings, HEIGHT); + if (y > 0) { + return new Point(x,y); + } + } + } + return super.computeSizeHint(); + } + + /** + * Returns the dialog settings for this hover or null if none + * + * @param create whether to create the settings + */ + private IDialogSettings getDialogSettings(boolean create) { + IDialogSettings settings = DsfUIPlugin.getDefault().getDialogSettings(); + IDialogSettings section = settings.getSection(this.getClass().getName()); + if (section == null & create) { + section = settings.addNewSection(this.getClass().getName()); + } + return section; + } + + /** + * Returns an integer value in the given dialog settings or -1 if none. + * + * @param settings dialog settings + * @param key key + * @return value or -1 if not present + */ + private int getIntSetting(IDialogSettings settings, String key) { + try { + return settings.getInt(key); + } catch (NumberFormatException e) { + return -1; + } + } + + @Override + public void dispose() { + persistSettings(getShell()); + super.dispose(); + } + + /** + * Persists dialog settings. + * + * @param shell + */ + private void persistSettings(Shell shell) { + if (shell != null && !shell.isDisposed()) { + if (isResizable()) { + IDialogSettings settings = getDialogSettings(true); + Point size = shell.getSize(); + settings.put(WIDTH, size.x); + settings.put(HEIGHT, size.y); + int[] weights = fSashForm.getWeights(); + settings.put(SASH_WEIGHT_TREE, weights[0]); + settings.put(SASH_WEIGHT_DETAILS, weights[1]); + } + } + } + + @Override + public void setVisible(boolean visible) { + if (!visible) { + persistSettings(getShell()); + } + super.setVisible(visible); + } + + @Override + protected void createContent(Composite parent) { + + fSashForm = new SashForm(parent, parent.getStyle()); + fSashForm.setOrientation(SWT.VERTICAL); + + // update presentation context + AbstractDebugView view = getViewToEmulate(); + IPresentationContext context = new PresentationContext(IDsfDebugUIConstants.ID_EXPRESSION_HOVER); + if (view != null) { + // copy over properties + IPresentationContext copy = ((TreeModelViewer)view.getViewer()).getPresentationContext(); + try { + String[] properties = copy.getProperties(); + for (int i = 0; i < properties.length; i++) { + String key = properties[i]; + context.setProperty(key, copy.getProperty(key)); + } + } catch (NoSuchMethodError e) { + // ignore + } + } + + fViewer = new TreeModelViewer(fSashForm, SWT.NO_TRIM | SWT.MULTI | SWT.VIRTUAL, context); + fViewer.setAutoExpandLevel(1); + + if (view != null) { + // copy over filters + StructuredViewer structuredViewer = (StructuredViewer) view.getViewer(); + if (structuredViewer != null) { + ViewerFilter[] filters = structuredViewer.getFilters(); + for (int i = 0; i < filters.length; i++) { + fViewer.addFilter(filters[i]); + } + } + } + fInputService = new ViewerInputService(fViewer, this); + + fDetailPaneComposite = SWTFactory.createComposite(fSashForm, 1, 1, GridData.FILL_BOTH); + Layout layout = fDetailPaneComposite.getLayout(); + if (layout instanceof GridLayout) { + GridLayout gl = (GridLayout) layout; + gl.marginHeight = 0; + gl.marginWidth = 0; + } + + fDetailPane = new DetailPaneProxy(new DetailPaneContainer()); + fDetailPane.display(null); // Bring up the default pane so the user doesn't see an empty composite + + fTree = fViewer.getTree(); + fTree.addSelectionListener(new SelectionListener() { + public void widgetSelected(SelectionEvent e) { + fDetailPane.display((IStructuredSelection)fViewer.getSelection()); + } + public void widgetDefaultSelected(SelectionEvent e) {} + }); + + initSashWeights(); + + // add update listener to auto-select and display details of root expression + fViewer.addViewerUpdateListener(new IViewerUpdateListener() { + public void viewerUpdatesComplete() { + } + public void viewerUpdatesBegin() { + } + public void updateStarted(IViewerUpdate update) { + } + public void updateComplete(IViewerUpdate update) { + if (update instanceof IChildrenUpdate) { + fViewer.removeViewerUpdateListener(this); + fViewer.getDisplay().timerExec(100, new Runnable() { + public void run() { + TreeSelection selection = new TreeSelection(fViewer.getTopElementPath()); + fViewer.setSelection(selection); + fDetailPane.display(selection); + }}); + } + } + }); + + setBackgroundColor(getShell().getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND)); + } + + + /** + * Attempts to find an appropriate view to emulate, this will either be the + * variables view or the expressions view. + * @return a view to emulate or null + */ + private AbstractDebugView getViewToEmulate() { + IWorkbenchPage page = DsfUIPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getActivePage(); + AbstractDebugView expressionsView = (AbstractDebugView) page.findView(IDebugUIConstants.ID_EXPRESSION_VIEW); + if (expressionsView != null && expressionsView.isVisible()) { + return expressionsView; + } + AbstractDebugView variablesView = (AbstractDebugView) page.findView(IDebugUIConstants.ID_VARIABLE_VIEW); + if (variablesView != null && variablesView.isVisible()) { + return variablesView; + } + if (expressionsView != null) { + return expressionsView; + } + return variablesView; + } + + /** + * Initializes the sash form weights from the preference store (using default values if + * no sash weights were stored previously). + */ + protected void initSashWeights(){ + IDialogSettings settings = getDialogSettings(false); + if (settings != null) { + int tree = getIntSetting(settings, SASH_WEIGHT_TREE); + if (tree > 0) { + int details = getIntSetting(settings, SASH_WEIGHT_DETAILS); + if (details > 0) { + fSashForm.setWeights(new int[]{tree, details}); + } + } + } + } + + @Override + public void setBackgroundColor(Color background) { + super.setBackgroundColor(background); + fDetailPaneComposite.setBackground(background); + fTree.setBackground(background); + } + + @Override + public void setFocus() { + super.setFocus(); + fTree.setFocus(); + } + + /* + * @see org.eclipse.jface.text.IInformationControlExtension#hasContents() + */ + public boolean hasContents() { + return fVariable != null; + } + + /* + * @see org.eclipse.jface.text.IInformationControlExtension2#setInput(java.lang.Object) + */ + public void setInput(Object input) { + if (input instanceof IExpressionDMContext) { + fVariable = input; + fInputService.resolveViewerInput(input); + } else if (input instanceof IVariable) { + fVariable = input; + fViewer.setInput(new TreeRoot()); + } + } + + @Override + public IInformationControlCreator getInformationPresenterControlCreator() { + return new ExpressionInformationControlCreator() { + @Override + public IInformationControl createInformationControl(Shell shell) { + return new ExpressionInformationControl(shell, true); + } + }; + } + + public void viewerInputComplete(IViewerInputUpdate update) { + fViewer.setInput(fVariable = update.getInputElement()); + } + + } + + /* + * @see org.eclipse.jface.text.IInformationControlCreator#createInformationControl(org.eclipse.swt.widgets.Shell) + */ + public IInformationControl createInformationControl(Shell parent) { + return new ExpressionInformationControl(parent, false); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/AbstractDsfDebugTextHover.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/AbstractDsfDebugTextHover.java index ee6668c8404..1d74b8d3eaa 100644 --- a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/AbstractDsfDebugTextHover.java +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/AbstractDsfDebugTextHover.java @@ -10,9 +10,13 @@ *******************************************************************************/ package org.eclipse.cdt.dsf.debug.ui; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; + import org.eclipse.cdt.debug.ui.editors.AbstractDebugTextHover; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.Query; +import org.eclipse.cdt.dsf.debug.internal.ui.ExpressionInformationControlCreator; import org.eclipse.cdt.dsf.debug.service.IExpressions; import org.eclipse.cdt.dsf.debug.service.IFormattedValues; import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; @@ -24,13 +28,21 @@ import org.eclipse.cdt.dsf.service.DsfServicesTracker; import org.eclipse.cdt.dsf.service.DsfSession; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.debug.core.model.IDebugModelProvider; +import org.eclipse.jface.text.DefaultInformationControl; +import org.eclipse.jface.text.IInformationControl; +import org.eclipse.jface.text.IInformationControlCreator; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITextHoverExtension2; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.editors.text.EditorsUI; /** - * An implementation of AbstractDebugTextHover using DSF services. + * An implementation of AbstractDebugTextHover using DSF services * * @since 2.1 */ -abstract public class AbstractDsfDebugTextHover extends AbstractDebugTextHover { +abstract public class AbstractDsfDebugTextHover extends AbstractDebugTextHover implements ITextHoverExtension2 { /** * Returns the debug model ID that this debug text hover is to be used for. @@ -121,4 +133,63 @@ abstract public class AbstractDsfDebugTextHover extends AbstractDebugTextHover { return null; } + /** + * Returns whether the expression explorer information control should be used. + * The default implementation returns false. + */ + protected boolean useExpressionExplorer() { + return false; + } + + @Override + public IInformationControlCreator getHoverControlCreator() { + if (useExpressionExplorer()) { + return new ExpressionInformationControlCreator(); + } else { + return new IInformationControlCreator() { + public IInformationControl createInformationControl(Shell parent) { + return new DefaultInformationControl(parent, EditorsUI.getTooltipAffordanceString()); + } + }; + } + } + + /* + * @see org.eclipse.jface.text.ITextHoverExtension2#getHoverInfo2(org.eclipse.jface.text.ITextViewer, org.eclipse.jface.text.IRegion) + */ + public Object getHoverInfo2(ITextViewer textViewer, IRegion hoverRegion) { + final String simpleInfo = getHoverInfo(textViewer, hoverRegion); + if (!useExpressionExplorer() || simpleInfo == null) { + return simpleInfo; + } + // improved version using ExpressionInformationControlCreator + // see also getHoverControlCreator() + final String text; + text= getExpressionText(textViewer, hoverRegion); + if (text != null && text.length() > 0) { + final IFrameDMContext frameDmc = getFrame(); + final DsfSession dsfSession = DsfSession.getSession(frameDmc.getSessionId()); + Callable callable = new Callable() { + public IExpressionDMContext call() throws Exception { + DsfServicesTracker tracker = new DsfServicesTracker(DsfUIPlugin.getBundleContext(), frameDmc.getSessionId()); + try { + IExpressions expressions = tracker.getService(IExpressions.class); + if (expressions != null) { + return expressions.createExpression(frameDmc, text); + } + return null; + } finally { + tracker.dispose(); + } + } + }; + try { + return dsfSession.getExecutor().submit(callable).get(); + } catch (InterruptedException e) { + } catch (ExecutionException e) { + } + } + return null; + } + } diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/IDsfDebugUIConstants.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/IDsfDebugUIConstants.java index b77138a5b3f..de4ddfa2f56 100644 --- a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/IDsfDebugUIConstants.java +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/IDsfDebugUIConstants.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2008 Wind River Systems and others. + * Copyright (c) 2006, 2009 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 @@ -79,4 +79,11 @@ public interface IDsfDebugUIConstants { * @since 2.0 */ public static final String PREF_COLOR_STALE_DATA_BACKGROUND= PLUGIN_ID + ".staledata.background"; //$NON-NLS-1$ + + /** + * Presentation context id for the expression hover. + * + * @since 2.1 + */ + public static final String ID_EXPRESSION_HOVER= PLUGIN_ID + ".expression_hover"; //$NON-NLS-1$ } 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 index 1f419e7b4eb..935fca4f2a9 100644 --- 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2008 Wind River Systems and others. + * Copyright (c) 2006, 2009 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 @@ -19,6 +19,7 @@ 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.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; @@ -46,6 +47,7 @@ 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; @@ -210,8 +212,13 @@ public class ExpressionVMProvider extends AbstractDMVMProvider /* * Now the Over-arching management node. */ - ExpressionManagerVMNode expressionManagerNode = new ExpressionManagerVMNode(this); - addChildNodes(rootNode, new IVMNode[] {expressionManagerNode}); + 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}); + } /* * The expression view wants to support fully all of the components of the register view. @@ -372,4 +379,20 @@ public class ExpressionVMProvider extends AbstractDMVMProvider // 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); + update.setInputElement(vmNode.createVMContext(dmc)); + update.done(); + return; + } + } + super.update(update); + } } diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/SingleExpressionVMNode.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/SingleExpressionVMNode.java new file mode 100644 index 00000000000..46e8744698b --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/SingleExpressionVMNode.java @@ -0,0 +1,327 @@ +/******************************************************************************* + * Copyright (c) 2009 Wind River Systems, Inc. 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.LinkedList; +import java.util.List; + +import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.expression.ExpressionVMProvider; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.expression.ExpressionsChangedEvent; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.expression.InvalidExpressionVMContext; +import org.eclipse.cdt.dsf.ui.concurrent.ViewerCountingRequestMonitor; +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMContext; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.VMDelta; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.model.IDebugTarget; +import org.eclipse.debug.core.model.IExpression; +import org.eclipse.debug.core.model.IValue; +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.IElementLabelProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.jface.viewers.TreePath; + +/** + * A VM node for displaying a single expression in the expression hover. + */ +public class SingleExpressionVMNode extends AbstractDMVMNode implements IElementLabelProvider { + + private static class RootDMVMContext extends AbstractVMContext implements IDMVMContext { + private final IDMContext fDmc; + public RootDMVMContext(IVMNode node, IDMContext dmc) { + super(node); + fDmc = dmc; + } + public IDMContext getDMContext() { + return fDmc; + } + @SuppressWarnings("rawtypes") + @Override + public Object getAdapter(Class adapter) { + Object superAdapter = super.getAdapter(adapter); + if (superAdapter != null) { + return superAdapter; + } else { + // Delegate to the Data Model to find the context. + if (adapter.isInstance(fDmc)) { + return fDmc; + } else { + return fDmc.getAdapter(adapter); + } + } + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof RootDMVMContext)) return false; + RootDMVMContext otherVmc = (RootDMVMContext)other; + return getVMNode().equals(otherVmc.getVMNode()) && + fDmc.equals(otherVmc.fDmc); + } + + @Override + public int hashCode() { + return getVMNode().hashCode() + fDmc.hashCode(); + } + + } + + private static class SimpleExpression implements IExpression { + private String fExpressionText; + SimpleExpression(String text) { + fExpressionText = text; + } + public void dispose() { + } + public IDebugTarget getDebugTarget() { + return null; + } + public String getExpressionText() { + return fExpressionText; + } + public IValue getValue() { + return null; + } + public ILaunch getLaunch() { + return null; + } + public String getModelIdentifier() { + return null; + } + @SuppressWarnings("rawtypes") + public Object getAdapter(Class adapter) { + return null; + } + + } + + private static class SingleExpressionManager { + private static final IExpression[] NO_EXPRESSIONS = {}; + IExpression fExpression; + public IExpression[] getExpressions() { + if (fExpression != null) { + return new IExpression[] { fExpression }; + } + return NO_EXPRESSIONS; + } + public void setExpression(IExpression expression) { + fExpression = expression; + } + + } + + /** Local reference to the expression manager */ + private final SingleExpressionManager fManager; + + public SingleExpressionVMNode(ExpressionVMProvider provider) { + super(provider, provider.getSession(), IExpressionDMContext.class); + fManager = new SingleExpressionManager(); + } + + @Override + public String toString() { + return "SingleExpressionVMNode"; //$NON-NLS-1$ + } + + private ExpressionVMProvider getExpressionVMProvider() { + return (ExpressionVMProvider)getVMProvider(); + } + + @Override + public void update(IHasChildrenUpdate[] updates) { + // Test availability of children based on whether there are any expressions + // in the manager. We assume that the getExpressions() will just read + // local state data, so we don't bother using a job to perform this + // operation. + for (int i = 0; i < updates.length; i++) { + updates[i].setHasChilren(fManager.getExpressions().length != 0); + updates[i].done(); + } + } + + @Override + public void update(IChildrenCountUpdate[] updates) { + for (IChildrenCountUpdate update : updates) { + if (!checkUpdate(update)) continue; + + // We assume that the getExpressions() will just read local state data, + // so we don't bother using a job to perform this operation. + update.setChildCount(fManager.getExpressions().length); + update.done(); + } + } + + @Override + public void update(final IChildrenUpdate[] updates) { + for (IChildrenUpdate update : updates) { + doUpdateChildren(update); + } + } + + public void doUpdateChildren(final IChildrenUpdate update) { + final IExpression[] expressions = fManager.getExpressions(); + + // For each (expression) element in update, find the layout node that can + // parse it. And for each expression that has a corresponding layout node, + // call IExpressionLayoutNode#getElementForExpression to generate a VMC. + // Since the last is an async call, we need to create a multi-RM to wait + // for all the calls to complete. + final CountingRequestMonitor multiRm = new ViewerCountingRequestMonitor(getVMProvider().getExecutor(), update); + int multiRmCount = 0; + + int lowOffset= update.getOffset(); + if (lowOffset < 0) { + lowOffset = 0; + } + int length= update.getLength(); + if (length <= 0) { + length = expressions.length; + } + final int highOffset= lowOffset + length; + for (int i = lowOffset; i < highOffset && i < expressions.length + 1; i++) { + if (i < expressions.length) { + multiRmCount++; + final int childIndex = i; + final IExpression expression = expressions[i]; + // getElementForExpression() accepts a IElementsUpdate as an argument. + // Construct an instance of VMElementsUpdate which will call a + // the request monitor when it is finished. The request monitor + // will in turn set the element in the update argument in this method. + ((ExpressionVMProvider)getVMProvider()).update( + new VMExpressionUpdate( + update, expression, + new DataRequestMonitor(getVMProvider().getExecutor(), multiRm) { + @Override + protected void handleSuccess() { + update.setChild(getData(), childIndex); + multiRm.done(); + } + + @Override + protected void handleError() { + update.setChild(new InvalidExpressionVMContext(SingleExpressionVMNode.this, expression), childIndex); + multiRm.done(); + } + }) + ); + } + } + + // If no expressions were parsed, we're finished. + // Set the count to the counting RM. + multiRm.setDoneCount(multiRmCount); + } + + public void update(ILabelUpdate[] updates) { + // The label update handler only handles labels for the invalid expression VMCs. + // The expression layout nodes are responsible for supplying label providers + // for their VMCs. + for (ILabelUpdate update : updates) { + update.done(); + } + } + + public int getDeltaFlags(Object event) { + int retVal = 0; + + // Add a flag if the list of expressions in the global expression manager has changed. + if (event instanceof ExpressionsChangedEvent) { + retVal |= IModelDelta.ADDED | IModelDelta.REMOVED | IModelDelta.INSERTED | IModelDelta.CONTENT ; + } + + for (IExpression expression : fManager.getExpressions()) { + retVal |= getExpressionVMProvider().getDeltaFlagsForExpression(expression, event); + } + + return retVal; + } + + public void buildDelta(final Object event, final VMDelta parentDelta, final int nodeOffset, final RequestMonitor requestMonitor) { + if (event instanceof ExpressionsChangedEvent) { + buildDeltaForExpressionsChangedEvent((ExpressionsChangedEvent)event, parentDelta, nodeOffset, requestMonitor); + } else { + + // For each expression, find its corresponding node and ask that + // layout node for its delta flags for given event. If there are delta flags to be + // generated, call the asynchronous method to do so. + CountingRequestMonitor multiRm = new CountingRequestMonitor(getExecutor(), requestMonitor); + + int buildDeltaForExpressionCallCount = 0; + + IExpression[] expressions = fManager.getExpressions(); + for (int i = 0; i < expressions.length; i++ ) { + int flags = getExpressionVMProvider().getDeltaFlagsForExpression(expressions[i], event); + // If the given expression has no delta flags, skip it. + if (flags == IModelDelta.NO_CHANGE) continue; + + int elementOffset = nodeOffset >= 0 ? nodeOffset + i : -1; + getExpressionVMProvider().buildDeltaForExpression( + expressions[i], elementOffset, event, parentDelta, getTreePathFromDelta(parentDelta), + new RequestMonitor(getExecutor(), multiRm)); + buildDeltaForExpressionCallCount++; + } + + multiRm.setDoneCount(buildDeltaForExpressionCallCount); + } + } + + private void buildDeltaForExpressionsChangedEvent(ExpressionsChangedEvent event, VMDelta parentDelta, + int nodeOffset, RequestMonitor requestMonitor) + { + CountingRequestMonitor multiRm = new CountingRequestMonitor(getExecutor(), requestMonitor); + for (int i = 0; i < event.getExpressions().length; i++) { + int expIndex = event.getIndex() != -1 + ? nodeOffset + event.getIndex() + i + : -1; + getExpressionVMProvider().buildDeltaForExpression( + event.getExpressions()[i], expIndex, event, parentDelta, getTreePathFromDelta(parentDelta), + new RequestMonitor(getExecutor(), multiRm)); + } + multiRm.setDoneCount(event.getExpressions().length); + } + + private TreePath getTreePathFromDelta(IModelDelta delta) { + List elementList = new LinkedList(); + IModelDelta listDelta = delta; + elementList.add(0, listDelta.getElement()); + while (listDelta.getParentDelta() != null) { + elementList.add(0, listDelta.getElement()); + listDelta = listDelta.getParentDelta(); + } + return new TreePath(elementList.toArray()); + } + + @Override + protected void updateElementsInSessionThread(IChildrenUpdate update) { + doUpdateChildren(update); + } + + @Override + public IDMVMContext createVMContext(IDMContext dmc) { + return new RootDMVMContext(getVMProvider().getRootVMNode(), dmc); + } + + void setExpression(IExpressionDMContext dmc) { + String text = dmc.getExpression(); + fManager.setExpression(new SimpleExpression(text)); + } +}