/******************************************************************************* * Copyright (c) 2010, 2012 Nokia 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: * Nokia - Initial API and implementation * Marc Khouzam (Ericsson) - Turn off casting for expression-group or * pattern expressions (bug 394408) *******************************************************************************/ package org.eclipse.cdt.dsf.debug.internal.ui.viewmodel; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutionException; import org.eclipse.cdt.debug.core.model.ICastToArray; import org.eclipse.cdt.debug.core.model.ICastToType; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; import org.eclipse.cdt.dsf.concurrent.Query; import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent; import org.eclipse.cdt.dsf.datamodel.DMContexts; import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionChangedDMEvent; import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMData; import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionGroupDMContext; import org.eclipse.cdt.dsf.debug.service.IExpressions.IIndexedPartitionDMContext; import org.eclipse.cdt.dsf.debug.service.IExpressions2; import org.eclipse.cdt.dsf.debug.service.IExpressions2.CastInfo; import org.eclipse.cdt.dsf.debug.service.IExpressions2.ICastedExpressionDMContext; import org.eclipse.cdt.dsf.debug.ui.viewmodel.variable.MessagesForVariablesVM; import org.eclipse.cdt.dsf.debug.ui.viewmodel.variable.SyncVariableDataAccess; 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.datamodel.AbstractDMVMProvider; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.PlatformObject; import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.DebugException; /** * This provides {@link ICastToType} and {@link ICastToArray} support on * expression nodes. */ public class DsfCastToTypeSupport { private final DsfServicesTracker serviceTracker; private final AbstractDMVMProvider dmvmProvider; private final SyncVariableDataAccess fSyncVariableDataAccess; /** expression memento to casting context (TODO: persist these; bug 228301)*/ private Map<String, CastInfo> fCastedExpressionStorage = new HashMap<String, CastInfo>(); public class CastImplementation extends PlatformObject implements ICastToArray { private final IExpressionDMContext exprDMC; private String memento; public CastImplementation(IExpressionDMContext exprDMC) { this.exprDMC = exprDMC; this.memento = createCastedExpressionMemento(exprDMC); } public class TestExpressions2Query extends Query<Boolean> { public TestExpressions2Query() { super(); } @Override protected void execute(final DataRequestMonitor<Boolean> rm) { /* * We're in another dispatch, so we must guard against executor * shutdown again. */ final DsfSession session = DsfSession.getSession( dmvmProvider.getSession().getId()); if (session == null) { rm.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, "Debug session already shut down.", null)); //$NON-NLS-1$ rm.done(); return; } DsfServicesTracker tracker = new DsfServicesTracker( DsfUIPlugin.getBundleContext(), dmvmProvider.getSession().getId()); IExpressions2 expressions2 = tracker.getService(IExpressions2.class); rm.setData(expressions2 != null); rm.done(); tracker.dispose(); } } private boolean isValid() { if (exprDMC instanceof IIndexedPartitionDMContext || exprDMC instanceof IExpressionGroupDMContext) { return false; } TestExpressions2Query query = new TestExpressions2Query(); dmvmProvider.getSession().getExecutor().execute(query); try { /* * Return value is irrelevant, any error would come through with an * exception. */ return query.get(); } catch (InterruptedException e) { assert false; return false; } catch (ExecutionException e) { return false; } } private void throwIfNotValid() throws DebugException { if (!isValid()) throw new DebugException(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, MessagesForVariablesVM.VariableVMNode_CannotCastVariable, null)); } /* * (non-Javadoc) * @see org.eclipse.cdt.debug.core.model.ICastToType#canCast() */ @Override public boolean canCast() { return isValid(); } /* * (non-Javadoc) * @see org.eclipse.cdt.debug.core.model.ICastToType#getCurrentType() */ @Override public String getCurrentType() { // get expected casted type first, if possible (if there's an error in the type, // the expression might not evaluate successfully) CastInfo castDMC = fCastedExpressionStorage.get(memento); if (castDMC != null && castDMC.getTypeString() != null) return castDMC.getTypeString(); // else, get the actual type IExpressionDMData data = fSyncVariableDataAccess.readVariable(exprDMC); if (data != null) return data.getTypeName(); return ""; //$NON-NLS-1$ } /* * (non-Javadoc) * @see org.eclipse.cdt.debug.core.model.ICastToType#cast(java.lang.String) */ @Override public void cast(String type) throws DebugException { throwIfNotValid(); CastInfo currentContext = fCastedExpressionStorage.get(memento); updateCastInformation(type, currentContext != null ? currentContext.getArrayStartIndex() : 0, currentContext != null ? currentContext.getArrayCount() : 0); } /* * (non-Javadoc) * @see org.eclipse.cdt.debug.core.model.ICastToType#restoreOriginal() */ @Override public void restoreOriginal() throws DebugException { throwIfNotValid(); fCastedExpressionStorage.remove(memento); fireExpressionChangedEvent(exprDMC); } /* * (non-Javadoc) * @see org.eclipse.cdt.debug.core.model.ICastToType#isCasted() */ @Override public boolean isCasted() { if (isValid()) return fCastedExpressionStorage.containsKey(memento); else return false; } /* * (non-Javadoc) * @see org.eclipse.cdt.debug.core.model.ICastToArray#canCastToArray() */ @Override public boolean canCastToArray() { return isValid(); } /* * (non-Javadoc) * @see org.eclipse.cdt.debug.core.model.ICastToArray#castToArray(int, int) */ @Override public void castToArray(int startIndex, int length) throws DebugException { throwIfNotValid(); CastInfo currentContext = fCastedExpressionStorage.get(memento); updateCastInformation(currentContext != null ? currentContext.getTypeString() : null, startIndex, length); } private void updateCastInformation( String type, int arrayStartIndex, int arrayCount) { final CastInfo info = new CastInfo(type, arrayStartIndex, arrayCount); fCastedExpressionStorage.put(memento, info); fireExpressionChangedEvent(exprDMC); } private class ExpressionChangedEvent extends AbstractDMEvent<IExpressionDMContext> implements IExpressionChangedDMEvent { public ExpressionChangedEvent(IExpressionDMContext context) { super(context); } } private void fireExpressionChangedEvent(IExpressionDMContext exprDMC) { ExpressionChangedEvent event = new ExpressionChangedEvent(exprDMC); dmvmProvider.handleEvent(event); } } public DsfCastToTypeSupport(DsfSession session, AbstractDMVMProvider dmvmProvider, SyncVariableDataAccess fSyncVariableDataAccess) { this.dmvmProvider = dmvmProvider; this.fSyncVariableDataAccess = fSyncVariableDataAccess; this.serviceTracker = new DsfServicesTracker(DsfUIPlugin.getBundleContext(), session.getId()); } /* (non-Javadoc) * @see org.eclipse.cdt.dsf.debug.ui.viewmodel.variable.ICastSupportTarget#createCastedExpressionMemento(org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext, java.lang.String) */ public String createCastedExpressionMemento(IExpressionDMContext exprDMC) { // go to the original variable first if (exprDMC instanceof ICastedExpressionDMContext) { IExpressionDMContext origExpr = DMContexts.getAncestorOfType(exprDMC.getParents()[0], IExpressionDMContext.class); if (origExpr == null) { assert false; } else { exprDMC = origExpr; } } // TODO: the memento doesn't really strictly define the expression's context; // we should fetch module name, function name, etc. to be more useful (but do that asynchronously) String expression = exprDMC.getExpression(); String memento = exprDMC.getSessionId() + "." + expression; //$NON-NLS-1$ return memento; } /* (non-Javadoc) * @see org.eclipse.cdt.dsf.debug.ui.viewmodel.variable.ICastSupportTarget#replaceWihCastedExpression(org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext) */ public IExpressionDMContext replaceWithCastedExpression( IExpressionDMContext exprDMC) { IExpressions2 expression2Service = serviceTracker.getService(IExpressions2.class); if (expression2Service == null) return exprDMC; if (!fCastedExpressionStorage.isEmpty()) { String memento = createCastedExpressionMemento(exprDMC); CastInfo castInfo = fCastedExpressionStorage.get(memento); if (castInfo != null) { return expression2Service.createCastedExpression(exprDMC, castInfo); } } return exprDMC; } /** * Get the ICastToArray (and ICastToType) implementation for the expression. * This does not necessarily return a unique object for each call. * @param exprDMC * @return {@link ICastToArray} */ public ICastToArray getCastImpl(IExpressionDMContext exprDMC) { return new CastImplementation(exprDMC); } }