/******************************************************************************* * Copyright (c) 2009, 2015 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.RequestMonitor; import org.eclipse.cdt.dsf.datamodel.IDMContext; import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; import org.eclipse.cdt.dsf.ui.concurrent.ViewerDataRequestMonitor; import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMContext; import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMNode; import org.eclipse.cdt.dsf.ui.viewmodel.IVMNode; import org.eclipse.cdt.dsf.ui.viewmodel.VMDelta; 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.debug.internal.ui.viewers.model.provisional.IViewerUpdate; import org.eclipse.jface.viewers.TreePath; /** * A VM node for displaying a single expression in the expression hover. * * @since 2.1 */ public class SingleExpressionVMNode extends AbstractVMNode implements IElementLabelProvider { private static class RootDMVMContext extends AbstractVMContext implements IDMVMContext { private final IDMContext fDmc; public RootDMVMContext(IVMNode node, IDMContext dmc) { super(node); fDmc = dmc; } @Override public IDMContext getDMContext() { return fDmc; } @SuppressWarnings("unchecked") @Override public <T> T getAdapter(Class<T> adapter) { T superAdapter = super.getAdapter(adapter); if (superAdapter != null) { return superAdapter; } else { // Delegate to the Data Model to find the context. if (adapter.isInstance(fDmc)) { return (T)fDmc; } else { return fDmc.getAdapter(adapter); } } } @Override public boolean equals(Object other) { if (this == other) { return true; } if (other instanceof RootDMVMContext) { RootDMVMContext otherVmc = (RootDMVMContext)other; return getVMNode().equals(otherVmc.getVMNode()) && fDmc.equals(otherVmc.fDmc); } return false; } @Override public int hashCode() { return getVMNode().hashCode() + fDmc.hashCode(); } @Override public String toString() { return fDmc.toString(); } } private static class SimpleExpression implements IExpression { private String fExpressionText; SimpleExpression(String text) { fExpressionText = text; } @Override public void dispose() { } @Override public IDebugTarget getDebugTarget() { return null; } @Override public String getExpressionText() { return fExpressionText; } @Override public IValue getValue() { return null; } @Override public ILaunch getLaunch() { return null; } @Override public String getModelIdentifier() { return null; } @Override public <T> T getAdapter(Class<T> adapter) { return null; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj instanceof SimpleExpression) { return fExpressionText.equals(((SimpleExpression) obj).getExpressionText()); } return false; } @Override public int hashCode() { return fExpressionText.hashCode(); } } public SingleExpressionVMNode(ExpressionVMProvider provider) { super(provider); } @Override public String toString() { return "SingleExpressionVMNode"; //$NON-NLS-1$ } private ExpressionVMProvider getExpressionVMProvider() { return (ExpressionVMProvider)getVMProvider(); } private IExpressionDMContext getUpdateExpressionDMC(IViewerUpdate update) { if (update.getElement() instanceof IDMVMContext) { IDMContext dmc = ((IDMVMContext)update.getElement()).getDMContext(); if (dmc instanceof IExpressionDMContext) { return (IExpressionDMContext)dmc; } } return null; } @Override public void update(IHasChildrenUpdate[] updates) { for (int i = 0; i < updates.length; i++) { updates[i].setHasChilren(getUpdateExpressionDMC(updates[i]) != null); 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(getUpdateExpressionDMC(update) != null ? 1 : 0); update.done(); } } @Override public void update(final IChildrenUpdate[] updates) { for (IChildrenUpdate update : updates) { IExpressionDMContext dmc = getUpdateExpressionDMC(update); if (dmc != null) { doUpdateChildren(update, new SimpleExpression(dmc.getExpression())); } else { handleFailedUpdate(update); } } } public void doUpdateChildren(final IChildrenUpdate update, final IExpression expression) { // 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 ViewerDataRequestMonitor<Object>(getVMProvider().getExecutor(), update) { @Override protected void handleSuccess() { update.setChild(getData(), 0); update.done(); } @Override protected void handleError() { update.setChild(new InvalidExpressionVMContext(SingleExpressionVMNode.this, expression), 0); update.done(); } }) ); } @Override 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(); } } @Override 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 ; } // The expression in the hover is not known here, so assume that all // expression nodes need to provide delta flags for event. Iterate // through them here and collect the flags. for (IExpressionVMNode node : getExpressionVMProvider().getExpressionNodes()) { retVal |= getDeltaFlagsForNode(node, event); } return retVal; } private int getDeltaFlagsForNode(IVMNode node, Object event) { int retVal = node.getDeltaFlags(event); for (IVMNode child : getVMProvider().getChildVMNodes(node)) { if (!node.equals(child)) { retVal |= getDeltaFlagsForNode(child, event); } } return retVal; } @Override 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 { Object parent = parentDelta.getElement(); if (parent instanceof IDMVMContext) { IDMContext dmc = ((IDMVMContext)parent).getDMContext(); if (dmc instanceof IExpressionDMContext) { IExpression expression = new SimpleExpression( ((IExpressionDMContext)dmc).getExpression() ); int flags = getExpressionVMProvider().getDeltaFlagsForExpression(expression, event); // If the given expression has no delta flags, skip it. if (flags != IModelDelta.NO_CHANGE) { getExpressionVMProvider().buildDeltaForExpression( expression, nodeOffset, event, parentDelta, getTreePathFromDelta(parentDelta), requestMonitor); return; } } } requestMonitor.done(); } } 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<Object> elementList = new LinkedList<Object>(); 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()); } public IDMVMContext createVMContext(IDMContext dmc) { return new RootDMVMContext(getVMProvider().getRootVMNode(), dmc); } }