/******************************************************************************* * 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.register; import java.util.concurrent.RejectedExecutionException; import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.DsfRunnable; import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; import org.eclipse.cdt.dsf.concurrent.RequestMonitor; import org.eclipse.cdt.dsf.datamodel.DMContexts; import org.eclipse.cdt.dsf.datamodel.IDMContext; import org.eclipse.cdt.dsf.debug.service.IRegisters; import org.eclipse.cdt.dsf.debug.service.IRegisters.IGroupChangedDMEvent; import org.eclipse.cdt.dsf.debug.service.IRegisters.IGroupsChangedDMEvent; import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterGroupDMContext; import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterGroupDMData; import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent; import org.eclipse.cdt.dsf.debug.ui.viewmodel.IDebugVMConstants; import org.eclipse.cdt.dsf.debug.ui.viewmodel.expression.AbstractExpressionVMNode; import org.eclipse.cdt.dsf.debug.ui.viewmodel.expression.WatchExpressionCellModifier; import org.eclipse.cdt.dsf.debug.ui.viewmodel.variable.VariableLabelFont; import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; import org.eclipse.cdt.dsf.service.DsfSession; import org.eclipse.cdt.dsf.ui.concurrent.ViewerDataRequestMonitor; 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.properties.IElementPropertiesProvider; import org.eclipse.cdt.dsf.ui.viewmodel.properties.IPropertiesUpdate; import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelAttribute; import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelColumnInfo; import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelImage; import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelText; import org.eclipse.cdt.dsf.ui.viewmodel.properties.PropertiesBasedLabelProvider; import org.eclipse.cdt.dsf.ui.viewmodel.update.StaleDataLabelBackground; import org.eclipse.cdt.dsf.ui.viewmodel.update.StaleDataLabelForeground; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.model.IExpression; import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementCompareRequest; import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementEditor; import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider; import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoProvider; import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoRequest; 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.IPresentationContext; import org.eclipse.debug.ui.DebugUITools; import org.eclipse.debug.ui.IDebugUIConstants; import org.eclipse.debug.ui.actions.IWatchExpressionFactoryAdapter2; import org.eclipse.jface.viewers.CellEditor; import org.eclipse.jface.viewers.ICellModifier; import org.eclipse.jface.viewers.TextCellEditor; import org.eclipse.jface.viewers.TreePath; import org.eclipse.swt.widgets.Composite; public class RegisterGroupVMNode extends AbstractExpressionVMNode implements IElementEditor, IElementLabelProvider, IElementMementoProvider, IElementPropertiesProvider { /** * @since 2.0 */ private static final String PROP_REGISTER_GROUP_DESCRIPTION = "register_group_description"; //$NON-NLS-1$ protected class RegisterGroupVMC extends DMVMContext { private IExpression fExpression; public RegisterGroupVMC(IDMContext dmc) { super(dmc); } public void setExpression(IExpression expression) { fExpression = expression; } @Override @SuppressWarnings({ "rawtypes", "unchecked" }) public Object getAdapter(Class adapter) { if (fExpression != null && adapter.isAssignableFrom(fExpression.getClass())) { return fExpression; } else if (adapter.isAssignableFrom(IWatchExpressionFactoryAdapter2.class)) { return getWatchExpressionFactory(); } else { return super.getAdapter(adapter); } } @Override public boolean equals(Object other) { if (other instanceof RegisterGroupVMC && super.equals(other)) { RegisterGroupVMC otherGroup = (RegisterGroupVMC)other; return (otherGroup.fExpression == null && fExpression == null) || (otherGroup.fExpression != null && otherGroup.fExpression.equals(fExpression)); } return false; } @Override public int hashCode() { return super.hashCode() + (fExpression != null ? fExpression.hashCode() : 0); } } protected class RegisterGroupExpressionFactory implements IWatchExpressionFactoryAdapter2 { public boolean canCreateWatchExpression(Object element) { return element instanceof RegisterGroupVMC; } /** * Expected format: Group(GroupName) */ public String createWatchExpression(Object element) throws CoreException { IRegisterGroupDMData groupData = getSyncRegisterDataAccess().getRegisterGroupDMData(element); if (groupData != null) { StringBuffer exprBuf = new StringBuffer(); exprBuf.append("GRP( "); //$NON-NLS-1$ exprBuf.append(groupData.getName()); exprBuf.append(" )"); //$NON-NLS-1$ return exprBuf.toString(); } return null; } } final private SyncRegisterDataAccess fSyncRegisterDataAccess; private IWatchExpressionFactoryAdapter2 fRegisterGroupExpressionFactory = null; private WatchExpressionCellModifier fWatchExpressionCellModifier = new WatchExpressionCellModifier(); /** * The label provider delegate. This VM node will delegate label updates to this provider * which can be created by sub-classes. * * @since 2.0 */ private IElementLabelProvider fLabelProvider; public RegisterGroupVMNode(AbstractDMVMProvider provider, DsfSession session, SyncRegisterDataAccess syncDataAccess) { super(provider, session, IRegisterGroupDMContext.class); fLabelProvider = createLabelProvider(); fSyncRegisterDataAccess = syncDataAccess; } @Override public String toString() { return "RegisterGroupVMNode(" + getSession().getId() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ } public SyncRegisterDataAccess getSyncRegisterDataAccess() { return fSyncRegisterDataAccess; } /** * @since 1.1 */ public IWatchExpressionFactoryAdapter2 getWatchExpressionFactory() { if ( fRegisterGroupExpressionFactory == null ) { fRegisterGroupExpressionFactory = new RegisterGroupExpressionFactory(); } return fRegisterGroupExpressionFactory; } @Override protected void updateElementsInSessionThread(final IChildrenUpdate update) { IRegisters regService = getServicesTracker().getService(IRegisters.class); if ( regService == null ) { handleFailedUpdate(update); return; } regService.getRegisterGroups( createCompositeDMVMContext(update), new ViewerDataRequestMonitor<IRegisterGroupDMContext[]>(getSession().getExecutor(), update) { @Override public void handleCompleted() { if (!isSuccess()) { update.done(); return; } fillUpdateWithVMCs(update, getData()); update.done(); }}); } @Override protected IDMVMContext createVMContext(IDMContext dmc) { return new RegisterGroupVMC(dmc); } /** * Creates the label provider delegate. This VM node will delegate label * updates to this provider which can be created by sub-classes. * * @return Returns the label provider for this node. * * @since 2.0 */ protected IElementLabelProvider createLabelProvider() { PropertiesBasedLabelProvider provider = new PropertiesBasedLabelProvider(); // The name column consists of the group name. provider.setColumnInfo( IDebugVMConstants.COLUMN_ID__NAME, new LabelColumnInfo(new LabelAttribute[] { new LabelText( MessagesForRegisterVM.RegisterGroupVMNode_Name_column__text_format, new String[] { PROP_NAME }), new LabelImage(DebugUITools.getImageDescriptor(IDebugUIConstants.IMG_OBJS_REGISTER_GROUP)), new StaleDataLabelForeground(), new VariableLabelFont(), })); // The description column contains a brief description of the register group. provider.setColumnInfo( IDebugVMConstants.COLUMN_ID__DESCRIPTION, new LabelColumnInfo(new LabelAttribute[] { new LabelText(MessagesForRegisterVM.RegisterGroupVMNode_Description_column__text_format, new String[] { PROP_REGISTER_GROUP_DESCRIPTION }), new StaleDataLabelForeground(), new VariableLabelFont(), })); // Expression column is visible only in the expressions view. It shows the expression string that the user // entered. Expression column images are the same as for the name column. provider.setColumnInfo( IDebugVMConstants.COLUMN_ID__EXPRESSION, new LabelColumnInfo(new LabelAttribute[] { new LabelText( MessagesForRegisterVM.RegisterGroupVMNode_Expression_column__text_format, new String[] { PROP_ELEMENT_EXPRESSION }), new LabelImage(DebugUITools.getImageDescriptor(IDebugUIConstants.IMG_OBJS_REGISTER_GROUP)), new StaleDataLabelForeground(), new VariableLabelFont(), })); provider.setColumnInfo( PropertiesBasedLabelProvider.ID_COLUMN_NO_COLUMNS, new LabelColumnInfo(new LabelAttribute[] { new LabelText(MessagesForRegisterVM.RegisterGroupVMNode_No_columns__text_format, new String[] { PROP_NAME, PROP_REGISTER_GROUP_DESCRIPTION}), new LabelImage(DebugUITools.getImageDescriptor(IDebugUIConstants.IMG_OBJS_REGISTER_GROUP)), new StaleDataLabelBackground(), new VariableLabelFont(), })); return provider; } public void update(final ILabelUpdate[] updates) { fLabelProvider.update(updates); } /** * @see IElementPropertiesProvider#update(IPropertiesUpdate[]) * * @since 2.0 */ public void update(final IPropertiesUpdate[] updates) { try { getSession().getExecutor().execute(new DsfRunnable() { public void run() { updatePropertiesInSessionThread(updates); }}); } catch (RejectedExecutionException e) { for (IPropertiesUpdate update : updates) { handleFailedUpdate(update); } } } /** * @since 2.0 */ @ConfinedToDsfExecutor("getSession().getExecutor()") protected void updatePropertiesInSessionThread(IPropertiesUpdate[] updates) { IRegisters service = getServicesTracker().getService(IRegisters.class, null); for (final IPropertiesUpdate update : updates) { IExpression expression = (IExpression)DebugPlugin.getAdapter(update.getElement(), IExpression.class); if (expression != null) { update.setProperty(AbstractExpressionVMNode.PROP_ELEMENT_EXPRESSION, expression.getExpressionText()); } IRegisterGroupDMContext dmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), IRegisterGroupDMContext.class); IRegisters regService = getServicesTracker().getService(IRegisters.class); if ( dmc == null || regService == null) { handleFailedUpdate(update); return; } service.getRegisterGroupData( dmc, new ViewerDataRequestMonitor<IRegisterGroupDMData>(getSession().getExecutor(), update) { @Override protected void handleSuccess() { fillRegisterGroupDataProperties(update, getData()); update.done(); } }); } } /** * @since 2.0 */ @ConfinedToDsfExecutor("getSession().getExecutor()") protected void fillRegisterGroupDataProperties(IPropertiesUpdate update, IRegisterGroupDMData data) { update.setProperty(PROP_NAME, data.getName()); update.setProperty(PROP_REGISTER_GROUP_DESCRIPTION, data.getDescription()); /* * If this node has an expression then it has already been filled in by the higher * level logic. If not then we need to supply something. In the previous version * ( pre-property based ) we supplied the name. So we will do that here also. */ IExpression expression = (IExpression)DebugPlugin.getAdapter(update.getElement(), IExpression.class); if (expression == null) { update.setProperty(AbstractExpressionVMNode.PROP_ELEMENT_EXPRESSION, data.getName()); } } public int getDeltaFlags(Object e) { if (e instanceof ISuspendedDMEvent) { return IModelDelta.CONTENT; } else if (e instanceof IGroupsChangedDMEvent) { return IModelDelta.CONTENT; } else if (e instanceof IGroupChangedDMEvent) { return IModelDelta.STATE; } return IModelDelta.NO_CHANGE; } public void buildDelta(Object e, VMDelta parentDelta, int nodeOffset, RequestMonitor rm) { // Although the register groups themselves are not affected by the // suspended event, typically all the registers are. Add a CONTENT changed // flag to the parent to repaint all the groups and their registers. if (e instanceof ISuspendedDMEvent) { // Create a delta that indicates all groups have changed parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); } else if (e instanceof IGroupsChangedDMEvent) { // Create a delta that indicates all groups have changed parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); } else if (e instanceof IGroupChangedDMEvent) { // Create a delta that indicates that specific group changed parentDelta.addNode( createVMContext(((IGroupChangedDMEvent)e).getDMContext()), IModelDelta.STATE ); } rm.done(); } public boolean canParseExpression(IExpression expression) { return parseExpressionForGroupName(expression.getExpressionText()) != null; } /** * Expected format: Group(GroupName) */ private String parseExpressionForGroupName(String expression) { if (expression.startsWith("GRP(")) { //$NON-NLS-1$ /* * Extract the group name. */ int startIdx = "GRP(".length(); //$NON-NLS-1$ int endIdx = expression.indexOf(')', startIdx); if ( startIdx == -1 || endIdx == -1 ) { return null; } String groupName = expression.substring(startIdx, endIdx); return groupName.trim(); } return null; } /* * (non-Javadoc) * @see org.eclipse.cdt.dsf.debug.ui.viewmodel.expression.IExpressionVMNode#getDeltaFlagsForExpression(org.eclipse.debug.core.model.IExpression, java.lang.Object) */ public int getDeltaFlagsForExpression(IExpression expression, Object event) { if (event instanceof ISuspendedDMEvent || event instanceof IGroupsChangedDMEvent) { return IModelDelta.CONTENT; } if (event instanceof IGroupChangedDMEvent) { return IModelDelta.STATE; } return IModelDelta.NO_CHANGE; } /* * (non-Javadoc) * @see org.eclipse.cdt.dsf.debug.ui.viewmodel.expression.IExpressionVMNode#buildDeltaForExpression(org.eclipse.debug.core.model.IExpression, int, java.lang.Object, org.eclipse.cdt.dsf.ui.viewmodel.VMDelta, org.eclipse.jface.viewers.TreePath, org.eclipse.cdt.dsf.concurrent.RequestMonitor) */ public void buildDeltaForExpression(IExpression expression, int elementIdx, Object event, VMDelta parentDelta, TreePath path, RequestMonitor rm) { if (event instanceof ISuspendedDMEvent) { // Mark the parent delta indicating that elements were added and/or removed. parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); } // If the group definitions have changed, refresh the whole expressions // view contents since previously invalid expressions may now evaluate // to valid groups if (event instanceof IGroupsChangedDMEvent) { parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); } rm.done(); } /* * (non-Javadoc) * @see org.eclipse.cdt.dsf.debug.ui.viewmodel.expression.IExpressionVMNode#buildDeltaForExpressionElement(java.lang.Object, int, java.lang.Object, org.eclipse.cdt.dsf.ui.viewmodel.VMDelta, org.eclipse.cdt.dsf.concurrent.RequestMonitor) */ public void buildDeltaForExpressionElement(Object element, int elementIdx, Object event, VMDelta parentDelta, final RequestMonitor rm) { if (event instanceof IGroupChangedDMEvent) { parentDelta.addNode(element, IModelDelta.STATE); } rm.done(); } /* * (non-Javadoc) * @see org.eclipse.cdt.dsf.debug.ui.viewmodel.expression.AbstractExpressionVMNode#testElementForExpression(java.lang.Object, org.eclipse.debug.core.model.IExpression, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor) */ @Override protected void testElementForExpression(Object element, IExpression expression, final DataRequestMonitor<Boolean> rm) { if (!(element instanceof IDMVMContext)) { rm.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_HANDLE, "Invalid context", null)); //$NON-NLS-1$ rm.done(); return; } final IRegisterGroupDMContext dmc = DMContexts.getAncestorOfType(((IDMVMContext)element).getDMContext(), IRegisterGroupDMContext.class); if (dmc == null) { rm.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_HANDLE, "Invalid context", null)); //$NON-NLS-1$ rm.done(); return; } final String groupName = parseExpressionForGroupName(expression.getExpressionText()); try { getSession().getExecutor().execute(new DsfRunnable() { public void run() { IRegisters registersService = getServicesTracker().getService(IRegisters.class); if (registersService != null) { registersService.getRegisterGroupData( dmc, new DataRequestMonitor<IRegisterGroupDMData>(ImmediateExecutor.getInstance(), rm) { @Override protected void handleSuccess() { rm.setData( getData().getName().equals(groupName) ); rm.done(); } }); } else { rm.setStatus(new Status(IStatus.WARNING, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, "Register service not available", null)); //$NON-NLS-1$ rm.done(); } } }); } catch (RejectedExecutionException e) { rm.setStatus(new Status(IStatus.WARNING, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, "DSF session shut down", null)); //$NON-NLS-1$ rm.done(); } } /* * (non-Javadoc) * @see org.eclipse.cdt.dsf.debug.ui.viewmodel.expression.AbstractExpressionVMNode#associateExpression(java.lang.Object, org.eclipse.debug.core.model.IExpression) */ @Override protected void associateExpression(Object element, IExpression expression) { if (element instanceof RegisterGroupVMC) { ((RegisterGroupVMC)element).setExpression(expression); } } /* * (non-Javadoc) * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IElementEditor#getCellEditor(org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext, java.lang.String, java.lang.Object, org.eclipse.swt.widgets.Composite) */ public CellEditor getCellEditor(IPresentationContext context, String columnId, Object element, Composite parent) { if (IDebugVMConstants.COLUMN_ID__EXPRESSION.equals(columnId)) { return new TextCellEditor(parent); } return null; } /* * (non-Javadoc) * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IElementEditor#getCellModifier(org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext, java.lang.Object) */ public ICellModifier getCellModifier(IPresentationContext context, Object element) { return fWatchExpressionCellModifier; } /* * (non-Javadoc) * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoProvider#compareElements(org.eclipse.debug.internal.ui.viewers.model.provisional.IElementCompareRequest[]) */ private final String MEMENTO_NAME = "GROUP_MEMENTO_NAME"; //$NON-NLS-1$ public void compareElements(IElementCompareRequest[] requests) { for (final IElementCompareRequest request : requests ) { final IRegisterGroupDMContext regDmc = findDmcInPath(request.getViewerInput(), request.getElementPath(), IRegisterGroupDMContext.class); final String mementoName = request.getMemento().getString(MEMENTO_NAME); if (regDmc == null || mementoName == null) { request.done(); continue; } // Now go get the model data for the single register group found. try { getSession().getExecutor().execute(new DsfRunnable() { public void run() { final IRegisters regService = getServicesTracker().getService(IRegisters.class); if ( regService != null ) { regService.getRegisterGroupData( regDmc, new DataRequestMonitor<IRegisterGroupDMData>(regService.getExecutor(), null) { @Override protected void handleCompleted() { if ( getStatus().isOK() ) { // Now make sure the register group is the one we want. request.setEqual( mementoName.equals( "Group." + getData().getName()) ); //$NON-NLS-1$ } request.done(); } }); } else { request.done(); } } }); } catch (RejectedExecutionException e) { request.done(); } } } /* * (non-Javadoc) * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoProvider#encodeElements(org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoRequest[]) */ public void encodeElements(IElementMementoRequest[] requests) { for ( final IElementMementoRequest request : requests ) { final IRegisterGroupDMContext regDmc = findDmcInPath(request.getViewerInput(), request.getElementPath(), IRegisterGroupDMContext.class); if (regDmc == null) { request.done(); continue; } // Now go get the model data for the single register group found. try { getSession().getExecutor().execute(new DsfRunnable() { public void run() { final IRegisters regService = getServicesTracker().getService(IRegisters.class); if ( regService != null ) { regService.getRegisterGroupData( regDmc, new DataRequestMonitor<IRegisterGroupDMData>(regService.getExecutor(), null) { @Override protected void handleCompleted() { if ( getStatus().isOK() ) { // Now make sure the register group is the one we want. request.getMemento().putString(MEMENTO_NAME, "Group." + getData().getName()); //$NON-NLS-1$ } request.done(); } }); } else { request.done(); } } }); } catch (RejectedExecutionException e) { request.done(); } } } }