diff options
author | Pawel Piech | 2009-01-08 21:08:05 +0000 |
---|---|---|
committer | Pawel Piech | 2009-01-08 21:08:05 +0000 |
commit | 5b471873662a87a77cfd854c98fca9c9948aa878 (patch) | |
tree | 3da8dcf3820bf3c2d4ebd17e4e8f3043e9773676 /dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui | |
parent | 47093064424981f85335cfdaf25368c54cd840f8 (diff) | |
parent | c1e6da229b8ffcea160498f034bfa6bc8ff6f230 (diff) | |
download | org.eclipse.cdt-5b471873662a87a77cfd854c98fca9c9948aa878.tar.gz org.eclipse.cdt-5b471873662a87a77cfd854c98fca9c9948aa878.tar.xz org.eclipse.cdt-5b471873662a87a77cfd854c98fca9c9948aa878.zip |
Migrated DSF and DSF-GDB to the CDT project.
Diffstat (limited to 'dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui')
90 files changed, 15187 insertions, 0 deletions
diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/DsfDebugUITools.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/DsfDebugUITools.java new file mode 100644 index 00000000000..8ff52b7741d --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/DsfDebugUITools.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 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; + +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.jface.preference.IPreferenceStore; + +/** + * @since 1.1 + */ +public class DsfDebugUITools { + + public static IPreferenceStore getPreferenceStore() + { + return DsfUIPlugin.getDefault().getPreferenceStore(); + } + +} 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 new file mode 100644 index 00000000000..5e51d671a21 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/IDsfDebugUIConstants.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * 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, Inc. - initial implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.ui; + +import org.eclipse.debug.ui.IDebugUIConstants; + +/** + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface IDsfDebugUIConstants { + + /** + * Debug UI plug-in identifier (value <code>"org.eclipse.cdt.dsf.debug.ui"</code>). + */ + public static final String PLUGIN_ID = "org.eclipse.cdt.dsf.debug.ui"; //$NON-NLS-1$; + + /** Loaded shared library symbols image identifier. */ + public static final String IMG_OBJS_SHARED_LIBRARY_SYMBOLS_LOADED = "icons/library_syms_obj.gif"; //$NON-NLS-1$ + + /** Unloaded Shared library symbols image identifier. */ + public static final String IMG_OBJS_SHARED_LIBRARY_SYMBOLS_UNLOADED = "icons/library_obj.gif"; //$NON-NLS-1$ + + /* + * The orientation of the detail view in the VariablesView + * Note: these constants should be removed from API. See bug 246005. + */ + public static final String VARIABLES_DETAIL_PANE_ORIENTATION = "Variables.detail.orientation"; //$NON-NLS-1$ + public static final String EXPRESSIONS_DETAIL_PANE_ORIENTATION = "Expressions.detail.orientation"; //$NON-NLS-1$ + public static final String REGISTERS_DETAIL_PANE_ORIENTATION = "Registers.detail.orientation"; //$NON-NLS-1$ + public static final String MODULES_DETAIL_PANE_ORIENTATION = "Modules.detail.orientation"; //$NON-NLS-1$ + public static final String VARIABLES_DETAIL_PANE_RIGHT = "Variables.detail.orientation.right"; //$NON-NLS-1$ + public static final String VARIABLES_DETAIL_PANE_UNDERNEATH = "Variables.detail.orientation.underneath"; //$NON-NLS-1$ + public static final String VARIABLES_DETAIL_PANE_HIDDEN = "Variables.detail.orientation.hidden"; //$NON-NLS-1$ + + /** + * Boolean preference controlling whether the text in the detail panes is + * wrapped. When <code>true</code> the text in the detail panes will be + * wrapped in new variable view. + */ + public static final String PREF_DETAIL_PANE_WORD_WRAP = PLUGIN_ID + ".detail_pane_word_wrap"; //$NON-NLS-1$ + + /** + * Maximum number of characters to display in the details area of the variables + * view, or 0 if unlimited. + */ + public static final String PREF_MAX_DETAIL_LENGTH = PLUGIN_ID + ".max_detail_length"; //$NON-NLS-1$ + + /** + * The name of the font to use for detail panes. This font is managed via + * the workbench font preference page. + */ + public static final String DETAIL_PANE_FONT= PLUGIN_ID + "DetailPaneFont"; //$NON-NLS-1$ + + /** + * Integer preference to control the maximum amount of stack frames to + * retrieve from the backend. Default value is <code>10</code>. + * @see {@link #PREF_STACK_FRAME_LIMIT_ENABLE} + * + * @since 1.1 + */ + public static final String PREF_STACK_FRAME_LIMIT = "stackFrameLimit"; //$NON-NLS-1$ + + /** + * Boolean preference whether to apply the stack frame limit preference. Default is <code>true</code>. + * @see {@link #PREF_STACK_FRAME_LIMIT} + * + * @since 1.1 + */ + public static final String PREF_STACK_FRAME_LIMIT_ENABLE = "stackFrameLimitEnable"; //$NON-NLS-1$ + + /** + * Boolean preference whether to keep stepping speed in sync with UI updates. Default is <code>false</code>. + * + * @since 1.1 + */ + public static final String PREF_WAIT_FOR_VIEW_UPDATE_AFTER_STEP_ENABLE = "delaySteppingForViewUpdatesEnable"; //$NON-NLS-1$ + + /** + * Integer preference to enforce a minimum time interval between steps. Default is <code>100</code>. + * + * @since 1.1 + */ + public static final String PREF_MIN_STEP_INTERVAL= "minStepInterval"; //$NON-NLS-1$ + + /** + * Help prefixes. + */ + public static final String PREFIX = IDebugUIConstants.PLUGIN_ID + "."; //$NON-NLS-1$ + + public static final String DETAIL_PANE = PREFIX + "detail_pane_context"; //$NON-NLS-1$ + public static final String DETAIL_PANE_ASSIGN_VALUE_ACTION = PREFIX + "detail_pane_assign_value_action_context"; //$NON-NLS-1$ + public static final String DETAIL_PANE_CONTENT_ASSIST_ACTION = PREFIX + "detail_pane_content_assist_action_context"; //$NON-NLS-1$ + public static final String DETAIL_PANE_CUT_ACTION = PREFIX + "detail_pane_cut_action_context"; //$NON-NLS-1$ + public static final String DETAIL_PANE_COPY_ACTION = PREFIX + "detail_pane_copy_action_context"; //$NON-NLS-1$ + public static final String DETAIL_PANE_PASTE_ACTION = PREFIX + "detail_pane_paste_action_context"; //$NON-NLS-1$ + public static final String DETAIL_PANE_SELECT_ALL_ACTION = PREFIX + "detail_pane_select_all_action_context"; //$NON-NLS-1$ + public static final String DETAIL_PANE_FIND_REPLACE_ACTION = PREFIX + "detail_pane_find_replace_action_context"; //$NON-NLS-1$ + public static final String DETAIL_PANE_WORD_WRAP_ACTION = PREFIX + "detail_pane_word_wrap_action_context"; //$NON-NLS-1$ + public static final String DETAIL_PANE_MAX_LENGTH_ACTION = PREFIX + "detail_pane_max_length_action_context"; //$NON-NLS-1$ + + /** + * @since 1.1 + */ + public static final String PREFERENCE_PAGE= PREFIX + "preference_page_context"; //$NON-NLS-1$ +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/PreferenceInitializer.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/PreferenceInitializer.java new file mode 100644 index 00000000000..6c111eee564 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/PreferenceInitializer.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2004, 2008 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 + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.ui; + +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.preference.PreferenceConverter; +import org.eclipse.swt.graphics.RGB; + +// Note: this class should be removed from public API. See bug 246004 +public class PreferenceInitializer extends AbstractPreferenceInitializer { + + public PreferenceInitializer() { + super(); + } + + /* (non-Javadoc) + * @see org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer#initializeDefaultPreferences() + */ + @Override + public void initializeDefaultPreferences() { + + IPreferenceStore prefs = DsfUIPlugin.getDefault().getPreferenceStore(); + + /* + * Common to all views. + */ + PreferenceConverter.setDefault(prefs, IDebugUIConstants.PREF_CHANGED_DEBUG_ELEMENT_COLOR, new RGB(255, 0, 0)); + prefs.setDefault(IDsfDebugUIConstants.PREF_DETAIL_PANE_WORD_WRAP, false); + prefs.setDefault(IDsfDebugUIConstants.PREF_MAX_DETAIL_LENGTH, 10000); + + /* + * Variables view + */ + prefs.setDefault(IDsfDebugUIConstants.VARIABLES_DETAIL_PANE_ORIENTATION, IDsfDebugUIConstants.VARIABLES_DETAIL_PANE_UNDERNEATH); + + /* + * Registers View + */ + prefs.setDefault(IDsfDebugUIConstants.REGISTERS_DETAIL_PANE_ORIENTATION, IDsfDebugUIConstants.VARIABLES_DETAIL_PANE_UNDERNEATH); + + /* + * Expressions View + */ + prefs.setDefault(IDsfDebugUIConstants.EXPRESSIONS_DETAIL_PANE_ORIENTATION, IDsfDebugUIConstants.VARIABLES_DETAIL_PANE_UNDERNEATH); + + /* + * Debug View + */ + prefs.setDefault(IDsfDebugUIConstants.PREF_STACK_FRAME_LIMIT, 10); + prefs.setDefault(IDsfDebugUIConstants.PREF_STACK_FRAME_LIMIT_ENABLE, true); + prefs.setDefault(IDsfDebugUIConstants.PREF_WAIT_FOR_VIEW_UPDATE_AFTER_STEP_ENABLE, false); + prefs.setDefault(IDsfDebugUIConstants.PREF_MIN_STEP_INTERVAL, 100); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/actions/DsfCommandRunnable.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/actions/DsfCommandRunnable.java new file mode 100644 index 00000000000..f4bc09280fa --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/actions/DsfCommandRunnable.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * 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.actions; + +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.debug.service.IProcesses; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.IStepQueueManager; +import org.eclipse.cdt.dsf.debug.service.StepQueueManager; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.SteppingController; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.commands.IDebugCommandRequest; + +@Immutable +public abstract class DsfCommandRunnable extends DsfRunnable { + private final IExecutionDMContext fContext; + private final DsfServicesTracker fTracker; + private final IDebugCommandRequest fRequest; + + public IExecutionDMContext getContext() { return fContext; } + public IRunControl getRunControl() { + return fTracker.getService(IRunControl.class); + } + /** + * @deprecated Use {@link #getStepQueueManager()} instead. + */ + @Deprecated + public StepQueueManager getStepQueueMgr() { + return fTracker.getService(StepQueueManager.class); + } + + /** + * @since 1.1 + */ + public IStepQueueManager getStepQueueManager() { + // for backwards compatibility + IStepQueueManager mgr= getStepQueueMgr(); + if (mgr != null) { + return mgr; + } + return getSteppingController(); + } + + /** + * @since 1.1 + */ + public SteppingController getSteppingController() { + if (fContext != null) { + return (SteppingController) fContext.getAdapter(SteppingController.class); + } + return null; + } + + /** + * @since 1.1 + */ + public IProcesses getProcessService() { + return fTracker.getService(IProcesses.class); + } + + public DsfCommandRunnable(DsfServicesTracker servicesTracker, Object element, IDebugCommandRequest request) { + fTracker = servicesTracker; + if (element instanceof IDMVMContext) { + IDMVMContext vmc = (IDMVMContext)element; + fContext = DMContexts.getAncestorOfType(vmc.getDMContext(), IExecutionDMContext.class); + } else { + fContext = null; + } + + fRequest = request; + } + + public final void run() { + if (fRequest.isCanceled()) { + fRequest.done(); + return; + } + if (getContext() == null) { + fRequest.setStatus(makeError("Selected object does not support run control.", null)); //$NON-NLS-1$ + } else if (getRunControl() == null || getStepQueueManager() == null) { + fRequest.setStatus(makeError("Run Control not available", null)); //$NON-NLS-1$ + } else { + doExecute(); + } + fRequest.done(); + } + + /** + * Method to perform the actual work. It should not call monitor.done(), because + * it will be called in the super-class. + */ + protected abstract void doExecute(); + + protected IStatus makeError(String message, Throwable e) { + return new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, -1, message, e); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/actions/DsfResumeCommand.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/actions/DsfResumeCommand.java new file mode 100644 index 00000000000..d2d7cb77d1c --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/actions/DsfResumeCommand.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * 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.actions; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +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.debug.core.commands.IDebugCommandRequest; +import org.eclipse.debug.core.commands.IEnabledStateRequest; +import org.eclipse.debug.core.commands.IResumeHandler; + +@Immutable +public class DsfResumeCommand implements IResumeHandler { + + private final DsfExecutor fExecutor; + private final DsfServicesTracker fTracker; + + public DsfResumeCommand(DsfSession session) { + fExecutor = session.getExecutor(); + fTracker = new DsfServicesTracker(DsfUIPlugin.getBundleContext(), session.getId()); + } + + public void dispose() { + fTracker.dispose(); + } + + public void canExecute(final IEnabledStateRequest request) { + if (request.getElements().length != 1) { + request.setEnabled(false); + request.done(); + return; + } + + fExecutor.submit(new DsfCommandRunnable(fTracker, request.getElements()[0], request) { + @Override public void doExecute() { + getRunControl().canResume( + getContext(), + new DataRequestMonitor<Boolean>(ImmediateExecutor.getInstance(), null) { + @Override + protected void handleCompleted() { + request.setEnabled(isSuccess() && getData()); + request.done(); + } + }); + } + }); + } + + public boolean execute(final IDebugCommandRequest request) { + if (request.getElements().length != 1) { + request.done(); + return false; + } + + fExecutor.submit(new DsfCommandRunnable(fTracker, request.getElements()[0], request) { + @Override public void doExecute() { + getRunControl().resume(getContext(), new RequestMonitor(fExecutor, null)); + } + }); + return false; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/actions/DsfStepIntoCommand.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/actions/DsfStepIntoCommand.java new file mode 100644 index 00000000000..94375732dc8 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/actions/DsfStepIntoCommand.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * 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.actions; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.debug.service.IRunControl.StepType; +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.debug.core.commands.IDebugCommandRequest; +import org.eclipse.debug.core.commands.IEnabledStateRequest; +import org.eclipse.debug.core.commands.IStepIntoHandler; + +@Immutable +public class DsfStepIntoCommand implements IStepIntoHandler { + + private final DsfExecutor fExecutor; + private final DsfServicesTracker fTracker; + private final DsfSteppingModeTarget fSteppingMode; + + public DsfStepIntoCommand(DsfSession session, DsfSteppingModeTarget steppingMode) { + fExecutor = session.getExecutor(); + fTracker = new DsfServicesTracker(DsfUIPlugin.getBundleContext(), session.getId()); + fSteppingMode = steppingMode; + } + + public void dispose() { + fTracker.dispose(); + } + + public void canExecute(final IEnabledStateRequest request) { + if (request.getElements().length != 1) { + request.setEnabled(false); + request.done(); + return; + } + + final StepType stepType= getStepType(); + fExecutor.submit(new DsfCommandRunnable(fTracker, request.getElements()[0], request) { + @Override public void doExecute() { + getStepQueueManager().canEnqueueStep( + getContext(), stepType, + new DataRequestMonitor<Boolean>(ImmediateExecutor.getInstance(), null) { + @Override + protected void handleCompleted() { + request.setEnabled(isSuccess() && getData()); + request.done(); + } + }); + } + }); + } + + public boolean execute(final IDebugCommandRequest request) { + if (request.getElements().length != 1) { + request.done(); + return false; + } + + final StepType stepType= getStepType(); + fExecutor.submit(new DsfCommandRunnable(fTracker, request.getElements()[0], request) { + @Override public void doExecute() { + getStepQueueManager().enqueueStep(getContext(), stepType); + } + }); + return true; + } + + + /** + * @return the currently active step type + */ + protected final StepType getStepType() { + boolean instructionSteppingEnabled= fSteppingMode != null && fSteppingMode.isInstructionSteppingEnabled(); + return instructionSteppingEnabled ? StepType.INSTRUCTION_STEP_INTO : StepType.STEP_INTO; + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/actions/DsfStepOverCommand.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/actions/DsfStepOverCommand.java new file mode 100644 index 00000000000..00d8c35e7c0 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/actions/DsfStepOverCommand.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * 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.actions; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.debug.service.IRunControl.StepType; +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.debug.core.commands.IDebugCommandRequest; +import org.eclipse.debug.core.commands.IEnabledStateRequest; +import org.eclipse.debug.core.commands.IStepOverHandler; + +@Immutable +public class DsfStepOverCommand implements IStepOverHandler { + + private final DsfExecutor fExecutor; + private final DsfServicesTracker fTracker; + private final DsfSteppingModeTarget fSteppingMode; + + public DsfStepOverCommand(DsfSession session, DsfSteppingModeTarget steppingMode) { + fExecutor = session.getExecutor(); + fTracker = new DsfServicesTracker(DsfUIPlugin.getBundleContext(), session.getId()); + fSteppingMode = steppingMode; + } + + public void dispose() { + fTracker.dispose(); + } + + public void canExecute(final IEnabledStateRequest request) { + if (request.getElements().length != 1) { + request.setEnabled(false); + request.done(); + return; + } + + fExecutor.submit(new DsfCommandRunnable(fTracker, request.getElements()[0], request) { + final StepType stepType= getStepType(); + @Override public void doExecute() { + getStepQueueManager().canEnqueueStep( + getContext(), stepType, + new DataRequestMonitor<Boolean>(ImmediateExecutor.getInstance(), null) { + @Override + protected void handleCompleted() { + request.setEnabled(isSuccess() && getData()); + request.done(); + } + }); + } + }); + } + + public boolean execute(final IDebugCommandRequest request) { + if (request.getElements().length != 1) { + request.done(); + return false; + } + + final StepType stepType= getStepType(); + fExecutor.submit(new DsfCommandRunnable(fTracker, request.getElements()[0], request) { + @Override public void doExecute() { + getStepQueueManager().enqueueStep(getContext(), stepType); + } + }); + return true; + } + + /** + * @return the currently active step type + */ + protected final StepType getStepType() { + boolean instructionSteppingEnabled= fSteppingMode != null && fSteppingMode.isInstructionSteppingEnabled(); + return instructionSteppingEnabled ? StepType.INSTRUCTION_STEP_OVER : StepType.STEP_OVER; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/actions/DsfStepReturnCommand.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/actions/DsfStepReturnCommand.java new file mode 100644 index 00000000000..7127fe2f0cb --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/actions/DsfStepReturnCommand.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * 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.actions; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.debug.service.IRunControl.StepType; +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.debug.core.commands.IDebugCommandRequest; +import org.eclipse.debug.core.commands.IEnabledStateRequest; +import org.eclipse.debug.core.commands.IStepReturnHandler; + +@Immutable +public class DsfStepReturnCommand implements IStepReturnHandler { + + private final DsfExecutor fExecutor; + private final DsfServicesTracker fTracker; + + public DsfStepReturnCommand(DsfSession session) { + fExecutor = session.getExecutor(); + fTracker = new DsfServicesTracker(DsfUIPlugin.getBundleContext(), session.getId()); + } + + public void dispose() { + fTracker.dispose(); + } + + public void canExecute(final IEnabledStateRequest request) { + if (request.getElements().length != 1) { + request.setEnabled(false); + request.done(); + return; + } + + fExecutor.submit(new DsfCommandRunnable(fTracker, request.getElements()[0], request) { + @Override public void doExecute() { + getStepQueueManager().canEnqueueStep( + getContext(), StepType.STEP_RETURN, + new DataRequestMonitor<Boolean>(ImmediateExecutor.getInstance(), null) { + @Override + protected void handleCompleted() { + request.setEnabled(isSuccess() && getData()); + request.done(); + } + }); + } + }); + } + + public boolean execute(final IDebugCommandRequest request) { + if (request.getElements().length != 1) { + request.done(); + return false; + } + + fExecutor.submit(new DsfCommandRunnable(fTracker, request.getElements()[0], request) { + @Override public void doExecute() { + getStepQueueManager().enqueueStep(getContext(), StepType.STEP_RETURN); + } + }); + return true; + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/actions/DsfSteppingModeTarget.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/actions/DsfSteppingModeTarget.java new file mode 100644 index 00000000000..aeb4cf64a62 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/actions/DsfSteppingModeTarget.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2008 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.actions; + +import org.eclipse.cdt.debug.core.model.ISteppingModeTarget; +import org.eclipse.cdt.debug.core.model.ITargetProperties; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.core.runtime.Preferences; +import org.eclipse.core.runtime.Preferences.IPropertyChangeListener; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PlatformUI; + +/** + */ +public class DsfSteppingModeTarget implements ISteppingModeTarget, ITargetProperties { + + private static final String ID_DISASSEMBLY_VIEW= "org.eclipse.cdt.dsf.debug.ui.disassembly.view"; //$NON-NLS-1$ + + private final Preferences fPreferences; + + public DsfSteppingModeTarget() { + fPreferences= new Preferences(); + fPreferences.setDefault(PREF_INSTRUCTION_STEPPING_MODE, false); + } + + /* + * @see org.eclipse.cdt.debug.core.model.ISteppingModeTarget#enableInstructionStepping(boolean) + */ + public void enableInstructionStepping(boolean enabled) { + fPreferences.setValue(PREF_INSTRUCTION_STEPPING_MODE, enabled); + if (enabled) { + try { + final IWorkbenchWindow activeWorkbenchWindow= PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + if (activeWorkbenchWindow != null && activeWorkbenchWindow.getActivePage() != null) { + activeWorkbenchWindow.getActivePage().showView(ID_DISASSEMBLY_VIEW); + } + } catch (PartInitException exc) { + DsfUIPlugin.log(exc); + } + } + } + + /* + * @see org.eclipse.cdt.debug.core.model.ISteppingModeTarget#isInstructionSteppingEnabled() + */ + public boolean isInstructionSteppingEnabled() { + return fPreferences.getBoolean(PREF_INSTRUCTION_STEPPING_MODE); + } + + /* + * @see org.eclipse.cdt.debug.core.model.ISteppingModeTarget#supportsInstructionStepping() + */ + public boolean supportsInstructionStepping() { + return true; + } + + /* + * @see org.eclipse.cdt.debug.core.model.ITargetProperties#addPropertyChangeListener(org.eclipse.core.runtime.Preferences.IPropertyChangeListener) + */ + public void addPropertyChangeListener(IPropertyChangeListener listener) { + fPreferences.addPropertyChangeListener(listener); + } + + /* + * @see org.eclipse.cdt.debug.core.model.ITargetProperties#removePropertyChangeListener(org.eclipse.core.runtime.Preferences.IPropertyChangeListener) + */ + public void removePropertyChangeListener(IPropertyChangeListener listener) { + fPreferences.removePropertyChangeListener(listener); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/actions/DsfSuspendCommand.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/actions/DsfSuspendCommand.java new file mode 100644 index 00000000000..cee40fccf08 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/actions/DsfSuspendCommand.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * 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.actions; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +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.debug.core.commands.IDebugCommandRequest; +import org.eclipse.debug.core.commands.IEnabledStateRequest; +import org.eclipse.debug.core.commands.ISuspendHandler; + +@Immutable +public class DsfSuspendCommand implements ISuspendHandler { + private final DsfExecutor fExecutor; + private final DsfServicesTracker fTracker; + + public DsfSuspendCommand(DsfSession session) { + fExecutor = session.getExecutor(); + fTracker = new DsfServicesTracker(DsfUIPlugin.getBundleContext(), session.getId()); + } + + public void dispose() { + fTracker.dispose(); + } + + public void canExecute(final IEnabledStateRequest request) { + if (request.getElements().length != 1) { + request.setEnabled(false); + request.done(); + return; + } + + fExecutor.submit(new DsfCommandRunnable(fTracker, request.getElements()[0], request) { + @Override public void doExecute() { + getRunControl().canSuspend( + getContext(), + new DataRequestMonitor<Boolean>(ImmediateExecutor.getInstance(), null) { + @Override + protected void handleCompleted() { + request.setEnabled(isSuccess() && getData()); + request.done(); + } + }); + } + }); + } + + public boolean execute(final IDebugCommandRequest request) { + if (request.getElements().length != 1) { + request.done(); + return false; + } + + fExecutor.submit(new DsfCommandRunnable(fTracker, request.getElements()[0], request) { + @Override public void doExecute() { + getRunControl().suspend(getContext(), new RequestMonitor(fExecutor, null)); + } + }); + return false; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/contexts/DsfSuspendTrigger.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/contexts/DsfSuspendTrigger.java new file mode 100644 index 00000000000..c8ed34a9fa1 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/contexts/DsfSuspendTrigger.java @@ -0,0 +1,121 @@ +/******************************************************************************* + * Copyright (c) 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.contexts; + +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.ISafeRunnable; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.ListenerList; +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.SafeRunner; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.ui.contexts.ISuspendTrigger; +import org.eclipse.debug.ui.contexts.ISuspendTriggerListener; + +/** + * DSF implementation of the ISuspendTrigger interface. The suspend trigger + * is used by the IDE to trigger activation of the debug perspective when + * the debugger suspends. + * + * @see ISuspendTrigger + */ +@ConfinedToDsfExecutor("fSession.getExecutor()") +public class DsfSuspendTrigger implements ISuspendTrigger { + + private final DsfSession fSession; + private final ILaunch fLaunch; + private boolean fDisposed = false; + private boolean fEventListenerRegisterd = false; + + @ThreadSafe + private final ListenerList fListeners = new ListenerList(); + + @ThreadSafe + public DsfSuspendTrigger(DsfSession session, ILaunch launch) { + fSession = session; + fLaunch = launch; + try { + fSession.getExecutor().execute(new DsfRunnable() { + public void run() { + if (!fDisposed) { + fSession.addServiceEventListener(DsfSuspendTrigger.this, null); + fEventListenerRegisterd = true; + } + } + }); + } catch(RejectedExecutionException e) {} + } + + @ThreadSafe + public void addSuspendTriggerListener(ISuspendTriggerListener listener) { + if (fListeners != null) { + fListeners.add(listener); + } + } + + @ThreadSafe + public void removeSuspendTriggerListener(ISuspendTriggerListener listener) { + if (fListeners != null) { + fListeners.remove(listener); + } + } + + public void dispose() { + if (fEventListenerRegisterd) { + fSession.removeServiceEventListener(this); + } + fDisposed = true; + } + + @DsfServiceEventHandler + public void eventDispatched(IRunControl.ISuspendedDMEvent e) { + final Object[] listeners = fListeners.getListeners(); + if (listeners.length != 0) { + new Job("DSF Suspend Trigger Notify") { //$NON-NLS-1$ + { + setSystem(true); + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + final MultiStatus status = new MultiStatus(DsfUIPlugin.PLUGIN_ID, 0, "DSF Suspend Trigger Notify Job Status", null); //$NON-NLS-1$ + for (int i = 0; i < listeners.length; i++) { + final ISuspendTriggerListener listener = (ISuspendTriggerListener) listeners[i]; + SafeRunner.run(new ISafeRunnable() { + public void run() throws Exception { + listener.suspended(fLaunch, null); + } + + public void handleException(Throwable exception) { + status.add(new Status( + IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, "Exception while calling suspend trigger listeners", exception)); //$NON-NLS-1$ + } + + }); + } + return status; + } + }.schedule(); + } + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/memory/RefreshAction.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/memory/RefreshAction.java new file mode 100644 index 00000000000..bb86ca6d561 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/memory/RefreshAction.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 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 - Ted Williams - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.debug.ui.memory; + +import org.eclipse.cdt.dsf.debug.internal.provisional.model.IMemoryBlockUpdatePolicyProvider; +import org.eclipse.debug.core.DebugEvent; +import org.eclipse.debug.core.IDebugEventSetListener; +import org.eclipse.debug.core.model.IMemoryBlock; +import org.eclipse.debug.internal.ui.views.memory.MemoryView; +import org.eclipse.debug.ui.memory.IMemoryRendering; +import org.eclipse.debug.ui.memory.IMemoryRenderingContainer; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.ui.IViewActionDelegate; +import org.eclipse.ui.IViewPart; + +@SuppressWarnings("restriction") +public class RefreshAction implements IViewActionDelegate { + + private IMemoryBlock fMemoryBlock = null; + + private MemoryView fView; + + public void init(IViewPart view) { + fView = (MemoryView) view; + } + + public void run(IAction action) { + + if(fMemoryBlock instanceof IMemoryBlockUpdatePolicyProvider) + { + ((IMemoryBlockUpdatePolicyProvider) fMemoryBlock).clearCache(); + IMemoryRenderingContainer containers[] = fView.getMemoryRenderingContainers(); + for(int i = 0; i < containers.length; i++) + { + IMemoryRendering renderings[] = containers[i].getRenderings(); + for(int j = 0; j < renderings.length; j++) + { + if (renderings[j].getControl() instanceof IDebugEventSetListener) + { + ((IDebugEventSetListener) renderings[j].getControl()).handleDebugEvents( + new DebugEvent[] { new DebugEvent(fMemoryBlock, DebugEvent.CHANGE) } ); + } + } + } + } + } + + public void selectionChanged(IAction action, ISelection selection) { + fMemoryBlock = null; + action.setEnabled(false); + if(selection instanceof IStructuredSelection) + { + if(((IStructuredSelection) selection).getFirstElement() instanceof IMemoryBlock) + { + fMemoryBlock = (IMemoryBlock) ((IStructuredSelection) selection).getFirstElement(); + action.setEnabled(true); + } + else if(((IStructuredSelection) selection).getFirstElement() instanceof IMemoryRendering) + { + fMemoryBlock = ((IMemoryRendering) ((IStructuredSelection) selection).getFirstElement()).getMemoryBlock(); + action.setEnabled(true); + } + } + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/memory/SelectUpdatePolicyAction.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/memory/SelectUpdatePolicyAction.java new file mode 100644 index 00000000000..026146eb7ba --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/memory/SelectUpdatePolicyAction.java @@ -0,0 +1,153 @@ +/******************************************************************************* + * Copyright (c) 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 - Ted Williams - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.debug.ui.memory; + +import org.eclipse.cdt.dsf.debug.internal.provisional.model.IMemoryBlockUpdatePolicyProvider; +import org.eclipse.debug.core.model.IMemoryBlock; +import org.eclipse.debug.ui.contexts.DebugContextEvent; +import org.eclipse.debug.ui.contexts.IDebugContextListener; +import org.eclipse.debug.ui.memory.IMemoryRendering; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.ActionContributionItem; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.action.IMenuCreator; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.swt.events.MenuAdapter; +import org.eclipse.swt.events.MenuEvent; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.swt.widgets.MenuItem; +import org.eclipse.ui.IActionDelegate2; +import org.eclipse.ui.IViewActionDelegate; +import org.eclipse.ui.IViewPart; + +/** + * + */ +public class SelectUpdatePolicyAction implements IMenuCreator, IViewActionDelegate, IDebugContextListener, IActionDelegate2 { + + private IAction fAction = null; + private IMemoryBlock fMemoryBlock = null; + + public void dispose() { + // do nothing + + } + + public void runWithEvent(IAction action, Event event) { + // do nothing + } + + class SelectPolicy extends Action { + + String fID; + String fDescription; + + public SelectPolicy(String id, String description) { + fID = id; + fDescription = description; + } + + @Override + public String getText() { + return fDescription; + } + + @Override + public void run() { + ((IMemoryBlockUpdatePolicyProvider) fMemoryBlock).setUpdatePolicy(fID); + } + + } + + + public Menu getMenu(Control parent) { + // Never called + return null; + } + + protected IAction getAction() { return fAction; } + + public void init(IViewPart view) { + } + + public void init(IAction action) { + fAction = action; + action.setMenuCreator(this); + } + + public void run(IAction action) { + // Do nothing, this is a pull-down menu + } + + public void selectionChanged(IAction action, ISelection selection) { + fMemoryBlock = null; + action.setEnabled(false); + if(selection instanceof IStructuredSelection) + { + if(((IStructuredSelection) selection).getFirstElement() instanceof IMemoryBlock) + { + fMemoryBlock = (IMemoryBlock) ((IStructuredSelection) selection).getFirstElement(); + action.setMenuCreator(this); + action.setEnabled(true); + } + else if(((IStructuredSelection) selection).getFirstElement() instanceof IMemoryRendering) + { + fMemoryBlock = ((IMemoryRendering) ((IStructuredSelection) selection).getFirstElement()).getMemoryBlock(); + action.setMenuCreator(this); + action.setEnabled(true); + } + } + } + + public void debugContextChanged(DebugContextEvent event) { + } + + public Menu getMenu(Menu parent) { + Menu menu = new Menu(parent); + menu.addMenuListener(new MenuAdapter() { + @Override + public void menuShown(MenuEvent e) { + Menu m = (Menu)e.widget; + MenuItem[] items = m.getItems(); + for (int i=0; i < items.length; i++) { + items[i].dispose(); + } + fillMenu(m); + } + }); + return menu; + } + + private void fillMenu(Menu menu) { + if(fMemoryBlock instanceof IMemoryBlockUpdatePolicyProvider) + { + IMemoryBlockUpdatePolicyProvider blockPolicy = (IMemoryBlockUpdatePolicyProvider) fMemoryBlock; + + String currentPolicy = blockPolicy.getUpdatePolicy(); + + String policies[] = blockPolicy.getUpdatePolicies(); + for(int i = 0; i < policies.length; i++) + { + SelectPolicy action = new SelectPolicy(policies[i], blockPolicy.getUpdatePolicyDescription(policies[i])); + ActionContributionItem item = new ActionContributionItem(action); + action.setChecked(policies[i].equals(currentPolicy)); + + item.fill(menu, -1); + } + } + } + +} + diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/sourcelookup/DsfSourceDisplayAdapter.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/sourcelookup/DsfSourceDisplayAdapter.java new file mode 100644 index 00000000000..c9140fc99c9 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/sourcelookup/DsfSourceDisplayAdapter.java @@ -0,0 +1,855 @@ +/******************************************************************************* + * 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: + * IBM Corporation - initial API and implementation + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.ui.sourcelookup; + +import java.io.File; +import java.net.URI; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.Query; +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.IStack; +import org.eclipse.cdt.dsf.debug.service.StepQueueManager; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.StateChangeReason; +import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; +import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMData; +import org.eclipse.cdt.dsf.debug.sourcelookup.DsfSourceLookupParticipant; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.SteppingController; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.SteppingController.ISteppingControlParticipant; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.SteppingController.SteppingTimedOutEvent; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; +import org.eclipse.core.filesystem.EFS; +import org.eclipse.core.filesystem.IFileStore; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector; +import org.eclipse.debug.core.sourcelookup.ISourceLookupParticipant; +import org.eclipse.debug.core.sourcelookup.containers.LocalFileStorage; +import org.eclipse.debug.ui.DebugUITools; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.debug.ui.sourcelookup.CommonSourceNotFoundEditorInput; +import org.eclipse.debug.ui.sourcelookup.ISourceDisplay; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITextOperationTarget; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.ITextViewerExtension5; +import org.eclipse.jface.text.Position; +import org.eclipse.swt.custom.BusyIndicator; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.IEditorDescriptor; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.editors.text.EditorsUI; +import org.eclipse.ui.ide.FileStoreEditorInput; +import org.eclipse.ui.ide.IDE; +import org.eclipse.ui.part.FileEditorInput; +import org.eclipse.ui.progress.UIJob; +import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants; +import org.eclipse.ui.texteditor.IDocumentProvider; +import org.eclipse.ui.texteditor.ITextEditor; + +/** + * Source display adapter that performs the source lookup, opens the editor, + * and paints the IP for the given object. + * <p> + * The implementation relies on three types of jobs to perform the operations.<br> + * - The first kind, "lookup job" performs the source lookup operation. <br> + * - The second "display job" positions and annotates the editor. <br> + * - The third clears the old IP annotations when a thread or process has resumed + * or exited. + * <p> + * The the lookup jobs can run in parallel with the display or the clearing job, + * but the clearing job and the display job must not run at the same time. + * Hence there is some involved logic which ensures that the jobs are run in + * proper order. To avoid race conditions, this logic uses the session's + * dispatch thread to synchronize access to the state data of the running jobs. + */ +@ThreadSafe +public class DsfSourceDisplayAdapter implements ISourceDisplay, ISteppingControlParticipant +{ + private static final class FrameData { + IFrameDMContext fDmc; + int fLine; + int fLevel; + String fFile; + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + FrameData other = (FrameData) obj; + if (!fDmc.equals(other.fDmc)) + return false; + if (fFile == null) { + if (other.fFile != null) + return false; + } else if (!fFile.equals(other.fFile)) + return false; + return true; + } + + /** + * Test whether the given frame data instance refers to the very same location. + * + * @param frameData + * @return <code>true</code> if the frame data refers to the same location + */ + public boolean isIdentical(FrameData frameData) { + return equals(frameData) && fLine == frameData.fLine; + } + } + + /** + * A job to perform source lookup on the given DMC. + */ + class LookupJob extends Job { + + private final IWorkbenchPage fPage; + private final FrameData fFrameData; + private final boolean fEventTriggered; + + /** + * Constructs a new source lookup job. + */ + public LookupJob(FrameData frameData, IWorkbenchPage page, boolean eventTriggered) { + super("DSF Source Lookup"); //$NON-NLS-1$ + setPriority(Job.INTERACTIVE); + setSystem(true); + fFrameData = frameData; + fPage = page; + fEventTriggered = eventTriggered; + } + + IDMContext getDmc() { return fFrameData.fDmc; } + + @Override + protected IStatus run(final IProgressMonitor monitor) { + if (monitor.isCanceled()) { + return Status.CANCEL_STATUS; + } + + final SourceLookupResult result = performLookup(); + executeFromJob(new DsfRunnable() { public void run() { + if (!monitor.isCanceled()) { + fPrevResult = result; + fPrevFrameData = fFrameData; + fRunningLookupJob = null; + startDisplayJob(fPrevResult, fFrameData, fPage, fEventTriggered); + } + }}); + return Status.OK_STATUS; + } + + private SourceLookupResult performLookup() { + IDMContext dmc = fFrameData.fDmc; + SourceLookupResult result = new SourceLookupResult(dmc , null, null, null); + String editorId = null; + IEditorInput editorInput = null; + Object sourceElement = fSourceLookup.getSourceElement(dmc); + + if (sourceElement == null) { + editorInput = new CommonSourceNotFoundEditorInput(dmc); + editorId = IDebugUIConstants.ID_COMMON_SOURCE_NOT_FOUND_EDITOR; + } else if (sourceElement instanceof IFile) { + editorId = getEditorIdForFilename(((IFile)sourceElement).getName()); + editorInput = new FileEditorInput((IFile)sourceElement); + } else if (sourceElement instanceof ITranslationUnit) { + try { + URI uriLocation = ((ITranslationUnit)sourceElement).getLocationURI(); + IFileStore fileStore = EFS.getStore(uriLocation); + editorInput = new FileStoreEditorInput(fileStore); + editorId = getEditorIdForFilename(fileStore.getName()); + } catch (CoreException e) { + editorInput = new CommonSourceNotFoundEditorInput(dmc); + editorId = IDebugUIConstants.ID_COMMON_SOURCE_NOT_FOUND_EDITOR; + } + } else if (sourceElement instanceof LocalFileStorage) { + File file = ((LocalFileStorage)sourceElement).getFile(); + IFileStore fileStore = EFS.getLocalFileSystem().fromLocalFile(file); + editorInput = new FileStoreEditorInput(fileStore); + editorId = getEditorIdForFilename(file.getName()); + } + result.setEditorInput(editorInput); + result.setEditorId(editorId); + result.setSourceElement(sourceElement); + + return result; + } + + private String getEditorIdForFilename(String filename) { + try { + IEditorDescriptor descriptor= IDE.getEditorDescriptor(filename); + return descriptor.getId(); + } catch (PartInitException exc) { + DsfUIPlugin.log(exc); + } + return "org.eclipse.ui.DefaultTextEditor"; //$NON-NLS-1$ + } + } + + /** + * Job that positions the editor and paints the IP Annotation for given DMC. + */ + class DisplayJob extends UIJob { + private final SourceLookupResult fResult; + private final IWorkbenchPage fPage; + private final FrameData fFrameData; + + private final DsfRunnable fDisplayJobFinishedRunnable = new DsfRunnable() { + public void run() { + // If the current display job does not match up with "this", it means that this job got canceled + // after it already completed and after this runnable was queued into the dispatch thread. + if (fRunningDisplayJob == DisplayJob.this) { + fRunningDisplayJob = null; + if (fEventTriggered && !fDoneStepping.getAndSet(true)) { + doneStepping(fResult.getDmc()); + } + serviceDisplayAndClearingJobs(); + } + } + }; + + private final AtomicBoolean fDoneStepping = new AtomicBoolean(false); + private IRegion fRegion; + private ITextViewer fTextViewer; + private final boolean fEventTriggered; + + IDMContext getDmc() { return fResult.getDmc(); } + + /** + * Constructs a new source display job + */ + public DisplayJob(SourceLookupResult result, FrameData frameData, IWorkbenchPage page, boolean eventTriggered) { + super("Debug Source Display"); //$NON-NLS-1$ + setSystem(true); + setPriority(Job.INTERACTIVE); + fResult = result; + fFrameData = frameData; + fPage = page; + fEventTriggered = eventTriggered; + } + + @Override + public IStatus runInUIThread(final IProgressMonitor monitor) { + + if (monitor.isCanceled()) { + executeFromJob(fDisplayJobFinishedRunnable); + return Status.CANCEL_STATUS; + } + + if (fRegion != null && fTextViewer != null) { + if (fRunningDisplayJob == this) { + if (!shouldCancelSelectionChange()) { + enableLineBackgroundPainter(); + fTextViewer.setSelectedRange(fRegion.getOffset(), 0); + } + executeFromJob(fDisplayJobFinishedRunnable); + } + } else { + IEditorPart editor = openEditor(fResult, fPage); + if (editor == null) { + executeFromJob(fDisplayJobFinishedRunnable); + return Status.OK_STATUS; + } + + ITextEditor textEditor = null; + if (editor instanceof ITextEditor) { + textEditor = (ITextEditor)editor; + } else { + textEditor = (ITextEditor) editor.getAdapter(ITextEditor.class); + } + if (textEditor != null) { + if (positionEditor(textEditor, fFrameData)) { + return Status.OK_STATUS; + } + } + executeFromJob(fDisplayJobFinishedRunnable); + } + return Status.OK_STATUS; + } + + private boolean shouldCancelSelectionChange() { + Query<Boolean> delaySelectionChangeQuery = new Query<Boolean>() { + @Override + protected void execute(DataRequestMonitor<Boolean> rm) { + IExecutionDMContext execCtx = DMContexts.getAncestorOfType(fFrameData.fDmc, + IExecutionDMContext.class); + + IRunControl runControl = fServicesTracker.getService(IRunControl.class); + rm.setData(runControl != null && execCtx != null && + (fController.getPendingStepCount(execCtx) != 0 || runControl.isStepping(execCtx))); + rm.done(); + } + }; + + try { + fController.getExecutor().execute(delaySelectionChangeQuery); + } catch (RejectedExecutionException e) { + return false; + } + + try { + return delaySelectionChangeQuery.get(); + } catch (InterruptedException e) { + return false; + } catch (ExecutionException e) { + return false; + } + } + + /** + * Opens the editor used to display the source for an element selected in + * this view and returns the editor that was opened or <code>null</code> if + * no editor could be opened. + */ + private IEditorPart openEditor(SourceLookupResult result, IWorkbenchPage page) { + IEditorInput input= result.getEditorInput(); + String id= result.getEditorId(); + if (input == null || id == null) { + return null; + } + + return openEditor(page, input, id); + } + + /** + * Opens an editor in the workbench and returns the editor that was opened + * or <code>null</code> if an error occurred while attempting to open the + * editor. + */ + private IEditorPart openEditor(final IWorkbenchPage page, final IEditorInput input, final String id) { + final IEditorPart[] editor = new IEditorPart[] {null}; + Runnable r = new Runnable() { + public void run() { + if (!page.getWorkbenchWindow().getWorkbench().isClosing()) { + try { + editor[0] = page.openEditor(input, id, false); + } catch (PartInitException e) {} + } + } + }; + BusyIndicator.showWhile(Display.getDefault(), r); + return editor[0]; + } + + /** + * Positions the text editor for the given stack frame + */ + private boolean positionEditor(ITextEditor editor, final FrameData frameData) { + // Position and annotate the editor. + fRegion= getLineInformation(editor, frameData.fLine); + if (fRegion != null) { + // add annotation + fIPManager.addAnnotation( + editor, frameData.fDmc, new Position(fRegion.getOffset(), fRegion.getLength()), + frameData.fLevel == 0); + + // this is a dirty trick to get access to the ITextViewer of the editor + Object tot = editor.getAdapter(ITextOperationTarget.class); + if (tot instanceof ITextViewer) { + fTextViewer = (ITextViewer)tot; + int widgetLine = frameData.fLine; + if (tot instanceof ITextViewerExtension5) { + ITextViewerExtension5 ext5 = (ITextViewerExtension5) tot; + // expand region if collapsed + ext5.exposeModelRange(fRegion); + widgetLine = ext5.modelLine2WidgetLine(widgetLine); + } + revealLine(fTextViewer, widgetLine); + + if (fStepCount > 0 && fSelectionChangeDelay > 0) { + disableLineBackgroundPainter(); + // reschedule for selection change + schedule(fSelectionChangeDelay); + if (!fDoneStepping.getAndSet(true)) { + doneStepping(getDmc()); + } + return true; + } else { + enableLineBackgroundPainter(); + fTextViewer.setSelectedRange(fRegion.getOffset(), 0); + } + } else { + editor.selectAndReveal(fRegion.getOffset(), 0); + } + } + return false; + } + + /** + * Scroll the given line into the visible area if it is not yet visible. + * @param focusLine + * @see org.eclipse.jface.text.TextViewer#revealRange(int, int) + */ + private void revealLine(ITextViewer viewer, int focusLine) { + StyledText textWidget = viewer.getTextWidget(); + int top = textWidget.getTopIndex(); + if (top > -1) { + + // scroll vertically + int lines = getEstimatedVisibleLinesInViewport(textWidget); + int bottom = top + lines; + + int bottomBuffer = Math.max(1, lines / 3); + + if (focusLine >= top && focusLine <= bottom - bottomBuffer) { + // do not scroll at all as it is already visible + } else { + if (focusLine > bottom - bottomBuffer && focusLine <= bottom) { + // focusLine is already in bottom bufferZone + // scroll to top of bottom bufferzone - for smooth down-scrolling + int scrollDelta = focusLine - (bottom - bottomBuffer); + textWidget.setTopIndex(top + scrollDelta); + } else { + // scroll to top of visible area minus buffer zone + int topBuffer = lines / 3; + textWidget.setTopIndex(Math.max(0, focusLine - topBuffer)); + } + } + } + } + + /** + * @return the number of visible lines in the view port assuming a constant + * line height. + */ + private int getEstimatedVisibleLinesInViewport(StyledText textWidget) { + if (textWidget != null) { + Rectangle clArea= textWidget.getClientArea(); + if (!clArea.isEmpty()) + return clArea.height / textWidget.getLineHeight(); + } + return -1; + } + + /** + * Returns the line information for the given line in the given editor + */ + private IRegion getLineInformation(ITextEditor editor, int lineNumber) { + IDocumentProvider provider= editor.getDocumentProvider(); + IEditorInput input= editor.getEditorInput(); + try { + provider.connect(input); + } catch (CoreException e) { + return null; + } + try { + IDocument document= provider.getDocument(input); + if (document != null) + return document.getLineInformation(lineNumber); + } catch (BadLocationException e) { + } finally { + provider.disconnect(input); + } + return null; + } + + } + + /** + * Job that removes the old IP Annotations associated with given execution + * context. + */ + class ClearingJob extends UIJob { + Set<IRunControl.IExecutionDMContext> fDmcsToClear; + + public ClearingJob(Set<IRunControl.IExecutionDMContext> dmcs) { + super("Debug Source Display"); //$NON-NLS-1$ + setSystem(true); + setPriority(Job.INTERACTIVE); + fDmcsToClear = dmcs; + } + + /* (non-Javadoc) + * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor) + */ + @Override + public IStatus runInUIThread(IProgressMonitor monitor) { + DsfRunnable clearingJobFinishedRunnable = new DsfRunnable() { public void run() { + assert fRunningClearingJob == ClearingJob.this; + fRunningClearingJob = null; + serviceDisplayAndClearingJobs(); + }}; + + enableLineBackgroundPainter(); + + if (monitor.isCanceled()) { + executeFromJob(clearingJobFinishedRunnable); + return Status.CANCEL_STATUS; + } + + for (IRunControl.IExecutionDMContext dmc : fDmcsToClear) { + fIPManager.removeAnnotations(dmc); + } + + executeFromJob(clearingJobFinishedRunnable); + return Status.OK_STATUS; + } + } + + private static final boolean DEBUG = false; + + private DsfSession fSession; + private DsfExecutor fExecutor; + private DsfServicesTracker fServicesTracker; + private FrameData fPrevFrameData; + private SourceLookupResult fPrevResult; + private ISourceLookupDirector fSourceLookup; + private DsfSourceLookupParticipant fSourceLookupParticipant; + private InstructionPointerManager fIPManager; + + private LookupJob fRunningLookupJob; + private DisplayJob fRunningDisplayJob; + private DisplayJob fPendingDisplayJob; + private ClearingJob fRunningClearingJob; + private Set<IRunControl.IExecutionDMContext> fPendingExecDmcsToClear = new HashSet<IRunControl.IExecutionDMContext>(); + private SteppingController fController; + + /** Delay (in milliseconds) before the selection is changed to the IP location */ + private int fSelectionChangeDelay = 150; + + private long fStepStartTime = 0; + private long fLastStepTime = 0; + private long fStepCount; + + private boolean fEnableLineBackgroundPainter; + + public DsfSourceDisplayAdapter(DsfSession session, ISourceLookupDirector sourceLocator) { + this(session, sourceLocator, null); + } + + /** + * @since 1.1 + */ + public DsfSourceDisplayAdapter(DsfSession session, ISourceLookupDirector sourceLocator, SteppingController controller) { + fSession = session; + fExecutor = session.getExecutor(); + fServicesTracker = new DsfServicesTracker(DsfUIPlugin.getBundleContext(), session.getId()); + fSourceLookup = sourceLocator; + fSourceLookupParticipant = new DsfSourceLookupParticipant(session); + fSourceLookup.addParticipants(new ISourceLookupParticipant[] {fSourceLookupParticipant} ); + + fIPManager = new InstructionPointerManager(); + + fSession.addServiceEventListener(this, null); + + fController = controller; + if (fController != null) { + fController.addSteppingControlParticipant(this); + } + } + + /** + * Configure the delay (in milliseconds) before the selection in the editor + * is changed to the IP location. + * + * @param delay the delay in milliseconds, a non-negative integer + * + * @since 1.1 + */ + public void setSelectionChangeDelay(int delay) { + fSelectionChangeDelay = delay; + } + + public void dispose() { + if (fController != null) { + fController.removeSteppingControlParticipant(this); + fController = null; + } + fSession.removeServiceEventListener(this); + fServicesTracker.dispose(); + fSourceLookup.removeParticipants(new ISourceLookupParticipant[] {fSourceLookupParticipant}); + + // fSourceLookupParticipant is disposed by the source lookup director + + // Need to remove annotations in UI thread. + Display display = Display.getDefault(); + if (!display.isDisposed()) { + display.asyncExec(new Runnable() { + public void run() { + enableLineBackgroundPainter(); + fIPManager.removeAllAnnotations(); + }}); + } + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.contexts.ISourceDisplayAdapter#displaySource(java.lang.Object, org.eclipse.ui.IWorkbenchPage, boolean) + */ + public void displaySource(Object context, final IWorkbenchPage page, final boolean force) { + fStepCount = 0; + + if (!(context instanceof IDMVMContext)) return; + final IDMContext dmc = ((IDMVMContext)context).getDMContext(); + + // Quick test. DMC is checked again in source lookup participant, but + // it's much quicker to test here. + if (!(dmc instanceof IFrameDMContext)) return; + doDisplaySource((IFrameDMContext) dmc, page, force, false); + } + + private void doDisplaySource(final IFrameDMContext context, final IWorkbenchPage page, final boolean force, final boolean eventTriggered) { + if (context.getLevel() < 0) { + return; + } + // Re-dispatch to executor thread before accessing job lists. + fExecutor.execute(new DsfRunnable() { public void run() { + // We need to retrieve the frame level and line number from the service. + IStack stackService = fServicesTracker.getService(IStack.class); + if (stackService == null) { + return; + } + stackService.getFrameData( + context, + new DataRequestMonitor<IFrameDMData>(fExecutor, null) { + @Override + public void handleSuccess() { + FrameData frameData = new FrameData(); + frameData.fDmc = context; + frameData.fLevel = context.getLevel(); + // Document line numbers are 0-based. While debugger line numbers are 1-based. + IFrameDMData data = getData(); + frameData.fLine = data.getLine() - 1; + frameData.fFile = data.getFile(); + if (!force && frameData.equals(fPrevFrameData)) { + fPrevResult.updateArtifact(context); + startDisplayJob(fPrevResult, frameData, page, eventTriggered); + } else { + startLookupJob(frameData, page, eventTriggered); + } + } + }); + }}); + } + + private void executeFromJob(Runnable runnable) { + try { + fExecutor.execute(runnable); + } catch (RejectedExecutionException e) { + // Session disposed, ignore + } + } + + private void startLookupJob(final FrameData frameData, final IWorkbenchPage page, boolean eventTriggered) { + // If there is a previous lookup job running, cancel it. + if (fRunningLookupJob != null) { + fRunningLookupJob.cancel(); + } + + fRunningLookupJob = new LookupJob(frameData, page, eventTriggered); + fRunningLookupJob.schedule(); + } + + // To be called only on dispatch thread. + private void startDisplayJob(SourceLookupResult lookupResult, FrameData frameData, IWorkbenchPage page, boolean eventTriggered) { + DisplayJob nextDisplayJob = new DisplayJob(lookupResult, frameData, page, eventTriggered); + if (fRunningDisplayJob != null) { + fPendingDisplayJob = null; + IExecutionDMContext[] execCtxs = DMContexts.getAllAncestorsOfType(frameData.fDmc, IExecutionDMContext.class); + fPendingExecDmcsToClear.removeAll(Arrays.asList(execCtxs)); + + if (!eventTriggered && frameData.isIdentical(fRunningDisplayJob.fFrameData)) { + // identical location - we are done + return; + } + // cancel running display job + fRunningDisplayJob.cancel(); + } + if (fRunningClearingJob != null) { + // Wait for the clearing job to finish, instead, set the + // display job as pending. + fPendingDisplayJob = nextDisplayJob; + } else { + fRunningDisplayJob = nextDisplayJob; + fRunningDisplayJob.schedule(); + } + } + + private void doneStepping(IDMContext context) { + if (fController != null) { + // indicate completion of step + final IExecutionDMContext dmc = DMContexts.getAncestorOfType(context, IExecutionDMContext.class); + if (dmc != null) { + fController.getExecutor().execute(new DsfRunnable() { + public void run() { + fController.doneStepping(dmc, DsfSourceDisplayAdapter.this); + }; + }); + } + } + } + + private void serviceDisplayAndClearingJobs() { + if (!fPendingExecDmcsToClear.isEmpty()) { + // There are annotations to be cleared, run the job first + fRunningClearingJob = new ClearingJob(fPendingExecDmcsToClear); + fRunningClearingJob.schedule(); + fPendingExecDmcsToClear = new HashSet<IRunControl.IExecutionDMContext>(); + } else if (fPendingDisplayJob != null) { + fRunningDisplayJob = fPendingDisplayJob; + fRunningDisplayJob.schedule(); + fPendingDisplayJob = null; + } + } + + private void startAnnotationClearingJob(IRunControl.IExecutionDMContext execDmc) { + // Make sure to add the context to the list. + fPendingExecDmcsToClear.add(execDmc); + + // If lookup job is running, check it against the execution context, + // and cancel it if matches. + if (fRunningLookupJob != null) { + if (DMContexts.isAncestorOf(fRunningLookupJob.getDmc(), execDmc)) { + fRunningLookupJob.cancel(); + fRunningLookupJob = null; + } + } + // If there is a pending display job, make sure it doesn't get + // preempted by this event. If so, just cancel the pending + // display job. + if (fPendingDisplayJob != null) { + if (DMContexts.isAncestorOf(fPendingDisplayJob.getDmc(), execDmc)) { + fPendingDisplayJob = null; + } + } + + // If no display or clearing jobs are running, schedule the clearing job. + if (fRunningClearingJob == null && fRunningDisplayJob == null) { + fRunningClearingJob = new ClearingJob(fPendingExecDmcsToClear); + fRunningClearingJob.schedule(); + fPendingExecDmcsToClear = new HashSet<IRunControl.IExecutionDMContext>(); + } + } + + @DsfServiceEventHandler + public void eventDispatched(IRunControl.IResumedDMEvent e) { + if (e.getReason() != StateChangeReason.STEP) { + startAnnotationClearingJob(e.getDMContext()); + } + } + + @DsfServiceEventHandler + public void eventDispatched(IRunControl.IExitedDMEvent e) { + startAnnotationClearingJob(e.getDMContext()); + } + + /** + * @since 1.1 + */ + @DsfServiceEventHandler + public void eventDispatched(SteppingTimedOutEvent e) { + startAnnotationClearingJob(e.getDMContext()); + } + + @DsfServiceEventHandler + public void eventDispatched(StepQueueManager.ISteppingTimedOutEvent e) { + startAnnotationClearingJob(e.getDMContext()); + } + + @DsfServiceEventHandler + public void eventDispatched(final IRunControl.ISuspendedDMEvent e) { + updateStepTiming(); + if (e.getReason() == StateChangeReason.STEP || e.getReason() == StateChangeReason.BREAKPOINT) { + // trigger source display immediately (should be optional?) + Display.getDefault().asyncExec(new Runnable() { + public void run() { + Object context = DebugUITools.getDebugContext(); + if (context instanceof IDMVMContext) { + final IDMContext dmc = ((IDMVMContext)context).getDMContext(); + if (dmc instanceof IFrameDMContext && DMContexts.isAncestorOf(dmc, e.getDMContext())) { + IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); + doDisplaySource((IFrameDMContext) dmc, page, false, true); + } + } + }}); + } else { + doneStepping(e.getDMContext()); + } + } + + private void updateStepTiming() { + long now = System.currentTimeMillis(); + if (now - fLastStepTime > Math.max(fSelectionChangeDelay, 200)) { + fStepCount = 0; + fStepStartTime = fLastStepTime = now; + return; + } + fLastStepTime = now; + ++fStepCount; + if (DEBUG) { + long delta = now - fStepStartTime; + float meanTime = delta/(float)fStepCount/1000; + System.out.println("[DsfSourceDisplayAdapter] step speed = " + 1/meanTime); //$NON-NLS-1$ + } + } + + /** + * Disable editor line background painter if it is enabled. + * <p> + * <strong>Must be called on display thread.</strong> + * </p> + */ + private void disableLineBackgroundPainter() { + if (!fEnableLineBackgroundPainter) { + fEnableLineBackgroundPainter = EditorsUI.getPreferenceStore().getBoolean(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_CURRENT_LINE); + if (fEnableLineBackgroundPainter) { + EditorsUI.getPreferenceStore().setValue(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_CURRENT_LINE, false); + } + } + } + + /** + * Enable the editor line background painter if it was enabled before. + * <p> + * <strong>Must be called on display thread.</strong> + * </p> + */ + private void enableLineBackgroundPainter() { + if (fEnableLineBackgroundPainter) { + fEnableLineBackgroundPainter = false; + EditorsUI.getPreferenceStore().setValue(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_CURRENT_LINE, true); + } + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/sourcelookup/InstructionPointerImageProvider.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/sourcelookup/InstructionPointerImageProvider.java new file mode 100644 index 00000000000..7a0d2d2196e --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/sourcelookup/InstructionPointerImageProvider.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 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 + *******************************************************************************/ + +package org.eclipse.cdt.dsf.debug.ui.sourcelookup; + +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +import org.eclipse.cdt.dsf.debug.ui.sourcelookup.InstructionPointerManager.IPAnnotation; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.text.source.Annotation; +import org.eclipse.swt.graphics.Image; +import org.eclipse.ui.texteditor.IAnnotationImageProvider; + +@ThreadSafe +public class InstructionPointerImageProvider implements IAnnotationImageProvider { + + /* (non-Javadoc) + * @see org.eclipse.ui.texteditor.IAnnotationImageProvider#getManagedImage(org.eclipse.jface.text.source.Annotation) + */ + public Image getManagedImage(Annotation annotation) { + return ((IPAnnotation)annotation).getImage(); + } + + /* (non-Javadoc) + * @see org.eclipse.ui.texteditor.IAnnotationImageProvider#getImageDescriptorId(org.eclipse.jface.text.source.Annotation) + */ + public String getImageDescriptorId(Annotation annotation) { + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.ui.texteditor.IAnnotationImageProvider#getImageDescriptor(java.lang.String) + */ + public ImageDescriptor getImageDescriptor(String imageDescritporId) { + return null; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/sourcelookup/InstructionPointerManager.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/sourcelookup/InstructionPointerManager.java new file mode 100644 index 00000000000..825ec43daf3 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/sourcelookup/InstructionPointerManager.java @@ -0,0 +1,264 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 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 - Adapter to use with DSF + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.ui.sourcelookup; + + +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.IStack; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.debug.ui.DebugUITools; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.source.Annotation; +import org.eclipse.jface.text.source.IAnnotationModel; +import org.eclipse.swt.graphics.Image; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.texteditor.IDocumentProvider; +import org.eclipse.ui.texteditor.ITextEditor; + +/** + * This class tracks instruction pointer contexts for a single DSF session. + */ +@ThreadSafe +class InstructionPointerManager { + + /** + * Current instruction pointer annotation type. + */ + private static final String ID_CURRENT_IP= "org.eclipse.cdt.dsf.debug.currentIP"; //$NON-NLS-1$ + + /** + * Secondary instruction pointer annotation type. + */ + private static final String ID_SECONDARY_IP= "org.eclipse.cdt.dsf.debug.secondaryIP"; //$NON-NLS-1$ + + /** + * Editor annotation object for instruction pointers. + */ + static class IPAnnotation extends Annotation { + + /** The image for this annotation. */ + private Image fImage; + + /** Frame DMC that this IP is for **/ + private IStack.IFrameDMContext fFrame; + + /** + * Constructs an instruction pointer image. + * + * @param frame stack frame the instruction pointer is associated with + * @param annotationType the type of annotation to display (annotation identifier) + * @param text the message to display with the annotation as hover help + * @param image the image used to display the annotation + */ + IPAnnotation(IStack.IFrameDMContext frame, String annotationType, String text, Image image) { + super(annotationType, false, text); + fFrame = frame; + fImage = image; + } + + /** + * Returns this annotation's image. + * + * @return image + */ + protected Image getImage() { + return fImage; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object other) { + if (other instanceof IPAnnotation) { + return fFrame.equals(((IPAnnotation)other).fFrame); + } + return false; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return fFrame.hashCode(); + } + + } + + /** + * Represents the context for a single instruction pointer. This is a convenience class + * used to store the three objects that comprise an instruction pointer 'context' so it + * can be stored in collections. + */ + static class AnnotationWrapper { + + /** The text editor for this context. */ + private ITextEditor fTextEditor; + + /** Stack frame that this annotation is for */ + private IStack.IFrameDMContext fFrameDmc; + + /** The vertical ruler annotation for this context. */ + private Annotation fAnnotation; + + AnnotationWrapper(ITextEditor textEditor, Annotation annotation, IStack.IFrameDMContext frameDmc) { + fTextEditor = textEditor; + fAnnotation = annotation; + fFrameDmc = frameDmc; + } + + /** + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object other) { + if (other instanceof AnnotationWrapper) { + AnnotationWrapper otherContext = (AnnotationWrapper) other; + return getAnnotation().equals(otherContext.getAnnotation()); + } + return false; + } + + /** + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return getAnnotation().hashCode(); + } + + ITextEditor getTextEditor() { return fTextEditor; } + Annotation getAnnotation() { return fAnnotation; } + IStack.IFrameDMContext getFrameDMC() { return fFrameDmc; } + } + + /** + * Mapping of IDebugTarget objects to (mappings of IThread objects to lists of instruction + * pointer contexts). + */ + private List<AnnotationWrapper> fAnnotationWrappers; + + /** + * Clients must not instantiate this class. + */ + public InstructionPointerManager() { + fAnnotationWrappers = Collections.synchronizedList(new LinkedList<AnnotationWrapper>()); + } + + /** + * Add an instruction pointer annotation in the specified editor for the + * specified stack frame. + */ + public void addAnnotation(ITextEditor textEditor, IStack.IFrameDMContext frame, Position position, boolean isTopFrame) { + + IDocumentProvider docProvider = textEditor.getDocumentProvider(); + IEditorInput editorInput = textEditor.getEditorInput(); + // If there is no annotation model, there's nothing more to do + IAnnotationModel annModel = docProvider.getAnnotationModel(editorInput); + if (annModel == null) { + return; + } + + String id; + String text; + Image image; + if (isTopFrame) { + id = ID_CURRENT_IP; + text = "Debug Current Instruction Pointer"; //$NON-NLS-1$ + image = DebugUITools.getImage(IDebugUIConstants.IMG_OBJS_INSTRUCTION_POINTER_TOP); + } else { + id = ID_SECONDARY_IP; + text = "Debug Call Stack"; //$NON-NLS-1$ + image = DebugUITools.getImage(IDebugUIConstants.IMG_OBJS_INSTRUCTION_POINTER); + } + + if (isTopFrame) { + // remove other top-frame IP annotation(s) for this execution-context + removeAnnotations(DMContexts.getAncestorOfType(frame.getParents()[0], IExecutionDMContext.class)); + } + Annotation annotation = new IPAnnotation(frame, id, text, image); + + // Add the annotation at the position to the editor's annotation model. + annModel.removeAnnotation(annotation); + annModel.addAnnotation(annotation, position); + + // Add to list of existing wrappers + fAnnotationWrappers.add(new AnnotationWrapper(textEditor, annotation, frame)); + } + + /** + * Remove all annotations associated with the specified debug target that this class + * is tracking. + */ + public void removeAnnotations(IRunControl.IExecutionDMContext execDmc) { + // Retrieve the mapping of threads to context lists + synchronized(fAnnotationWrappers) { + for (Iterator<AnnotationWrapper> wrapperItr = fAnnotationWrappers.iterator(); wrapperItr.hasNext();) { + AnnotationWrapper wrapper = wrapperItr.next(); + if (DMContexts.isAncestorOf(wrapper.getFrameDMC(), execDmc)) { + removeAnnotation(wrapper.getTextEditor(), wrapper.getAnnotation()); + wrapperItr.remove(); + } + } + } + } + + /** + * Remove all top-frame annotations associated with the specified debug target that this class + * is tracking. + */ + public void removeTopFrameAnnotations(IRunControl.IExecutionDMContext execDmc) { + // Retrieve the mapping of threads to context lists + synchronized(fAnnotationWrappers) { + for (Iterator<AnnotationWrapper> wrapperItr = fAnnotationWrappers.iterator(); wrapperItr.hasNext();) { + AnnotationWrapper wrapper = wrapperItr.next(); + if (DMContexts.isAncestorOf(wrapper.getFrameDMC(), execDmc) + && ID_CURRENT_IP.equals(wrapper.getAnnotation().getType())) { + removeAnnotation(wrapper.getTextEditor(), wrapper.getAnnotation()); + wrapperItr.remove(); + } + } + } + } + + /** Removes all annotations tracked by this manager */ + public void removeAllAnnotations() { + synchronized(fAnnotationWrappers) { + for (AnnotationWrapper wrapper : fAnnotationWrappers) { + removeAnnotation(wrapper.getTextEditor(), wrapper.getAnnotation()); + } + fAnnotationWrappers.clear(); + } + } + + /** + * Remove the specified annotation from the specified text editor. + */ + private void removeAnnotation(ITextEditor textEditor, Annotation annotation) { + IDocumentProvider docProvider = textEditor.getDocumentProvider(); + if (docProvider != null) { + IAnnotationModel annotationModel = docProvider.getAnnotationModel(textEditor.getEditorInput()); + if (annotationModel != null) { + annotationModel.removeAnnotation(annotation); + } + } + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/sourcelookup/SourceLookupResult.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/sourcelookup/SourceLookupResult.java new file mode 100644 index 00000000000..707d3c4eb3e --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/sourcelookup/SourceLookupResult.java @@ -0,0 +1,129 @@ +/******************************************************************************* + * 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.sourcelookup; + +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.ui.IEditorInput; + +/** + * The result of a source lookup contains the source element, editor id, and + * editor input resolved for a debug artifact. + * + * @since 3.1 + */ +class SourceLookupResult { + + /** + * Element that source was resolved for. + */ + private IDMContext fDmc; + /** + * Corresponding source element, or <code>null</code> + * if unknown. + */ + private Object fSourceElement; + /** + * Associated editor id, used to display the source element, + * or <code>null</code> if unknown. + */ + private String fEditorId; + /** + * Associatd editor input, used to display the source element, + * or <code>null</code> if unknown. + */ + private IEditorInput fEditorInput; + + /** + * Creates a source lookup result on the given artifact, source element, + * editor id, and editor input. + */ + public SourceLookupResult(IDMContext dmc, Object sourceElement, String editorId, IEditorInput editorInput) { + fDmc = dmc; + setSourceElement(sourceElement); + setEditorId(editorId); + setEditorInput(editorInput); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.sourcelookup.ISourceLookupResult#getArtifact() + */ + public IDMContext getDmc() { + return fDmc; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.sourcelookup.ISourceLookupResult#getSourceElement() + */ + public Object getSourceElement() { + return fSourceElement; + } + + /** + * Sets the source element resolved for the artifact that source + * lookup was performed for, or <code>null</code> if a source element + * was not resolved. + * + * @param element resolved source element or <code>null</code> if unknown + */ + protected void setSourceElement(Object element) { + fSourceElement = element; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.sourcelookup.ISourceLookupResult#getEditorId() + */ + public String getEditorId() { + return fEditorId; + } + + /** + * Sets the identifier of the editor used to display this source + * lookup result's source element, or <code>null</code> if unknown. + * + * @param id the identifier of the editor used to display this source + * lookup result's source element, or <code>null</code> if unknown + */ + protected void setEditorId(String id) { + fEditorId = id; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.sourcelookup.ISourceLookupResult#getEditorInput() + */ + public IEditorInput getEditorInput() { + return fEditorInput; + } + + /** + * Sets the editor input used to display this source lookup + * result's source element, or <code>null</code> if unknown. + * + * @param input the editor input used to display this source lookup + * result's source element, or <code>null</code> if unknown + */ + protected void setEditorInput(IEditorInput input) { + fEditorInput = input; + } + + /** + * Updates the artifact to refer to the given artifact + * if equal. For example, when a source lookup result is resued + * for the same stack frame, we still need to update in case + * the stack frame is not identical. + * + * @param artifact new artifact state + */ + public void updateArtifact(IDMContext dmc) { + if (fDmc.equals(dmc)) { + fDmc = dmc; + } + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/AbstractDebugVMAdapter.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/AbstractDebugVMAdapter.java new file mode 100644 index 00000000000..6a6c8e7fc46 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/AbstractDebugVMAdapter.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 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; + +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.SteppingController.ISteppingControlParticipant; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMAdapter; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; + +/** + * Base class for VM adapters used for implementing a debugger integration. + * + * @since 1.1 + */ +@SuppressWarnings("restriction") +public class AbstractDebugVMAdapter extends AbstractDMVMAdapter + implements ISteppingControlParticipant +{ + + public AbstractDebugVMAdapter(DsfSession session, final SteppingController controller) { + super(session); + fController = controller; + try { + fController.getExecutor().execute(new DsfRunnable() { + public void run() { + fController.addSteppingControlParticipant(AbstractDebugVMAdapter.this); + } + }); + } catch (RejectedExecutionException e) {} // Do nothing if session is shut down. + } + + private final SteppingController fController; + + @Override + protected IVMProvider createViewModelProvider(IPresentationContext context) { + return null; + } + + @Override + public void doneHandleEvent(Object event) { + if (event instanceof IRunControl.ISuspendedDMEvent) { + final ISuspendedDMEvent suspendedEvent= (IRunControl.ISuspendedDMEvent) event; + fController.getExecutor().execute(new DsfRunnable() { + public void run() { + fController.doneStepping(suspendedEvent.getDMContext(), AbstractDebugVMAdapter.this); + }; + }); + } + } + + @Override + public void dispose() { + try { + fController.getExecutor().execute(new DsfRunnable() { + public void run() { + fController.removeSteppingControlParticipant(AbstractDebugVMAdapter.this); + } + }); + } catch (RejectedExecutionException e) {} // Do nothing if session is shut down. + super.dispose(); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/IDebugVMConstants.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/IDebugVMConstants.java new file mode 100644 index 00000000000..29647fc5f86 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/IDebugVMConstants.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 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 - Ted Williams - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.ui.viewmodel; + +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; + +public interface IDebugVMConstants { + /** + * Standard across the board column IDs. + */ + public static final String ID = DsfUIPlugin.PLUGIN_ID + ".VARIABLES_COLUMN_PRESENTATION_ID"; //$NON-NLS-1$ + public static final String COLUMN_ID__NAME = DsfUIPlugin.PLUGIN_ID + ".COLUMN_ID__NAME"; //$NON-NLS-1$ + public static final String COLUMN_ID__TYPE = DsfUIPlugin.PLUGIN_ID + ".COLUMN_ID__TYPE"; //$NON-NLS-1$ + public static final String COLUMN_ID__VALUE = DsfUIPlugin.PLUGIN_ID + ".COLUMN_ID__VALUE"; //$NON-NLS-1$ + public static final String COLUMN_ID__ADDRESS = DsfUIPlugin.PLUGIN_ID + ".COLUMN_ID__ADDRESS"; //$NON-NLS-1$ + public static final String COLUMN_ID__DESCRIPTION = DsfUIPlugin.PLUGIN_ID + ".COLUMN_ID__DESCRIPTION"; //$NON-NLS-1$ + public static final String COLUMN_ID__EXPRESSION = DsfUIPlugin.PLUGIN_ID + ".COLUMN_ID__EXPRESSION"; //$NON-NLS-1$ + + /** + * Location of the current format in the IPresentationContext data store. + */ + public final static String CURRENT_FORMAT_STORAGE = "CurrentNumericStyle" ; //$NON-NLS-1$ +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/SteppingController.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/SteppingController.java new file mode 100644 index 00000000000..1f7a2890fe8 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/SteppingController.java @@ -0,0 +1,572 @@ +/******************************************************************************* + * 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; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.IStepQueueManager; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IResumedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.StateChangeReason; +import org.eclipse.cdt.dsf.debug.service.IRunControl.StepType; +import org.eclipse.cdt.dsf.debug.ui.IDsfDebugUIConstants; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.runtime.Platform; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; + +/** + * This class builds on top of standard run control service to provide + * functionality for step queuing and delaying. Step queuing essentially allows + * user to press and hold the step key and achieve maximum stepping speed. If + * this class is used, other service implementations, such as stack and + * expressions, can use it to avoid requesting data from debugger back end if + * another step is about to be executed. + * + * @since 1.1 + */ +public final class SteppingController implements IStepQueueManager +{ + /** + * Amount of time in milliseconds, that it takes the SteppingTimedOutEvent + * event to be issued after a step is started. + * @see SteppingTimedOutEvent + */ + public final static int STEPPING_TIMEOUT = 500; + + /** + * The depth of the step queue. In other words, the maximum number of steps + * that are queued before the step queue manager is throwing them away. + */ + public final static int STEP_QUEUE_DEPTH = 2; + + /** + * The maximum delay (in milliseconds) between steps when synchronized + * stepping is enabled. This also serves as a safeguard in the case stepping + * control participants fail to indicate completion of event processing. + */ + public final static int MAX_STEP_DELAY= 5000; + + private final static boolean DEBUG = "true".equals(Platform.getDebugOption("org.eclipse.cdt.dsf.ui/debug/stepping")); //$NON-NLS-1$ //$NON-NLS-2$ + + /** + * Indicates that the given context has been stepping for some time, + * and the UI (views and actions) may need to be updated accordingly. + */ + public static final class SteppingTimedOutEvent extends AbstractDMEvent<IExecutionDMContext> { + private SteppingTimedOutEvent(IExecutionDMContext execCtx) { + super(execCtx); + } + } + + /** + * Interface for clients interested in stepping control. When a stepping + * control participant is registered with the stepping controller, it is + * expected to call + * {@link SteppingController#doneStepping(IExecutionDMContext, ISteppingControlParticipant) + * doneStepping} as soon as a "step", i.e. a suspended event has been + * processed. If synchronized stepping is enabled, further stepping is + * blocked until all stepping control participants have indicated completion + * of event processing or the maximum timeout + * {@link SteppingController#MAX_STEP_DELAY} has been reached. + * + * @see SteppingController#addSteppingControlParticipant(ISteppingControlParticipant) + * @see SteppingController#removeSteppingControlParticipant(ISteppingControlParticipant) + */ + public interface ISteppingControlParticipant { + } + + private static class StepRequest { + IExecutionDMContext fContext; + StepType fStepType; + boolean inProgress = false; + StepRequest(IExecutionDMContext execCtx, StepType type) { + fContext = execCtx; + fStepType = type; + } + } + + private final DsfSession fSession; + private final DsfServicesTracker fServicesTracker; + + private IRunControl fRunControl; + private int fQueueDepth = STEP_QUEUE_DEPTH; + + private final Map<IExecutionDMContext,List<StepRequest>> fStepQueues = new HashMap<IExecutionDMContext,List<StepRequest>>(); + private final Map<IExecutionDMContext,Boolean> fTimedOutFlags = new HashMap<IExecutionDMContext,Boolean>(); + private final Map<IExecutionDMContext,ScheduledFuture<?>> fTimedOutFutures = new HashMap<IExecutionDMContext,ScheduledFuture<?>>(); + + /** + * Records the time of the last step for an execution context. + */ + private final Map<IExecutionDMContext, Long> fLastStepTimes= new HashMap<IExecutionDMContext, Long>(); + + /** + * Minimum step interval in milliseconds. + */ + private int fMinStepInterval= 0; + + /** + * Map of execution contexts for which a step is in progress. + */ + private final Map<IExecutionDMContext, List<ISteppingControlParticipant>> fStepInProgress = new HashMap<IExecutionDMContext, List<ISteppingControlParticipant>>(); + + /** + * List of registered stepping control participants. + */ + private final List<ISteppingControlParticipant> fParticipants = Collections.synchronizedList(new ArrayList<ISteppingControlParticipant>()); + + /** + * Property change listener. It updates the stepping control settings. + */ + private IPropertyChangeListener fPreferencesListener; + + public SteppingController(DsfSession session) { + fSession = session; + fServicesTracker = new DsfServicesTracker(DsfUIPlugin.getBundleContext(), session.getId()); + + final IPreferenceStore store= DsfUIPlugin.getDefault().getPreferenceStore(); + + fPreferencesListener = new IPropertyChangeListener() { + public void propertyChange(final PropertyChangeEvent event) { + handlePropertyChanged(store, event); + }}; + store.addPropertyChangeListener(fPreferencesListener); + + setMinimumStepInterval(store.getInt(IDsfDebugUIConstants.PREF_MIN_STEP_INTERVAL)); + } + + public void dispose() { + if (fRunControl != null) { + getSession().removeServiceEventListener(this); + } + + IPreferenceStore store= DsfUIPlugin.getDefault().getPreferenceStore(); + store.removePropertyChangeListener(fPreferencesListener); + + fServicesTracker.dispose(); + } + + /** + * Configure the minimum time (in milliseconds) to wait between steps. + * + * @param interval + */ + public void setMinimumStepInterval(int interval) { + fMinStepInterval = interval; + } + + /** + * Register given stepping control participant. + * <p> + * Participants are obliged to call + * {@link #doneStepping(IExecutionDMContext, ISteppingControlParticipant)} + * when they have received and completed processing an + * {@link ISuspendedDMEvent}. If synchronized stepping is enabled, further + * stepping is disabled until all participants have indicated completion of + * processing the event. + * </p> + * + * @param participant + */ + public void addSteppingControlParticipant(ISteppingControlParticipant participant) { + fParticipants.add(participant); + } + + /** + * Unregister given stepping control participant. + * + * @param participant + */ + public void removeSteppingControlParticipant(final ISteppingControlParticipant participant) { + fParticipants.remove(participant); + } + + /** + * Indicate that participant has completed processing of the last step. + * + * @param execCtx + */ + public void doneStepping(final IExecutionDMContext execCtx, final ISteppingControlParticipant participant) { + if (DEBUG) System.out.println("[SteppingController] doneStepping participant=" + participant.getClass().getSimpleName()); //$NON-NLS-1$ + List<ISteppingControlParticipant> participants = fStepInProgress.get(execCtx); + if (participants != null) { + participants.remove(participant); + if (participants.isEmpty()) { + doneStepping(execCtx); + } + } else { + for (IExecutionDMContext disabledCtx : fStepInProgress.keySet()) { + if (DMContexts.isAncestorOf(disabledCtx, execCtx)) { + participants = fStepInProgress.get(disabledCtx); + if (participants != null) { + participants.remove(participant); + if (participants.isEmpty()) { + doneStepping(disabledCtx); + } + } + } + } + } + } + + public DsfSession getSession() { + return fSession; + } + + /** + * All access to this class should happen through this executor. + * @return the executor this class is confined to + */ + public DsfExecutor getExecutor() { + return getSession().getExecutor(); + } + + private DsfServicesTracker getServicesTracker() { + return fServicesTracker; + } + + private IRunControl getRunControl() { + if (fRunControl == null) { + fRunControl = getServicesTracker().getService(IRunControl.class); + getSession().addServiceEventListener(this, null); + } + return fRunControl; + } + + /** + * Checks whether a step command can be queued up for given context. + */ + public void canEnqueueStep(IExecutionDMContext execCtx, StepType stepType, DataRequestMonitor<Boolean> rm) { + if (doCanEnqueueStep(execCtx, stepType)) { + rm.setData(true); + rm.done(); + } else { + getRunControl().canStep(execCtx, stepType, rm); + } + } + + private boolean doCanEnqueueStep(IExecutionDMContext execCtx, StepType stepType) { + return getRunControl().isStepping(execCtx) && !isSteppingTimedOut(execCtx); + } + + /** + * Check whether the next step on the given execution context should be delayed + * based on the configured step delay. + * + * @param execCtx + * @return <code>true</code> if the step should be delayed + */ + private boolean shouldDelayStep(IExecutionDMContext execCtx) { + final int stepDelay= getStepDelay(execCtx); + if (DEBUG) System.out.println("[SteppingController] shouldDelayStep delay=" + stepDelay); //$NON-NLS-1$ + return stepDelay > 0; + } + + /** + * Compute the delay in milliseconds before the next step for the given context may be executed. + * + * @param execCtx + * @return the number of milliseconds before the next possible step + */ + private int getStepDelay(IExecutionDMContext execCtx) { + if (fMinStepInterval > 0) { + for (IExecutionDMContext lastStepCtx : fLastStepTimes.keySet()) { + if (execCtx.equals(lastStepCtx) || DMContexts.isAncestorOf(execCtx, lastStepCtx)) { + long now = System.currentTimeMillis(); + int delay= (int) (fLastStepTimes.get(lastStepCtx) + fMinStepInterval - now); + return Math.max(delay, 0); + } + } + } + return 0; + } + + private void updateLastStepTime(IExecutionDMContext execCtx) { + long now = System.currentTimeMillis(); + fLastStepTimes.put(execCtx, now); + for (IExecutionDMContext lastStepCtx : fLastStepTimes.keySet()) { + if (!execCtx.equals(lastStepCtx) && DMContexts.isAncestorOf(execCtx, lastStepCtx)) { + fLastStepTimes.put(lastStepCtx, now); + } + } + } + + private long getLastStepTime(IExecutionDMContext execCtx) { + if (fLastStepTimes.containsKey(execCtx)) { + return fLastStepTimes.get(execCtx); + } + for (IExecutionDMContext lastStepCtx : fLastStepTimes.keySet()) { + if (DMContexts.isAncestorOf(execCtx, lastStepCtx)) { + return fLastStepTimes.get(lastStepCtx); + } + } + return 0; + } + + /** + * Returns the number of step commands that are queued for given execution + * context. + */ + public int getPendingStepCount(IExecutionDMContext execCtx) { + List<StepRequest> stepQueue = getStepQueue(execCtx); + if (stepQueue == null) return 0; + return stepQueue.size(); + } + + /** + * Adds a step command to the execution queue for given context. + * @param execCtx Execution context that should perform the step. + * @param stepType Type of step to execute. + */ + public void enqueueStep(final IExecutionDMContext execCtx, final StepType stepType) { + if (DEBUG) System.out.println("[SteppingController] enqueueStep ctx=" + execCtx); //$NON-NLS-1$ + if (!shouldDelayStep(execCtx) || doCanEnqueueStep(execCtx, stepType)) { + doEnqueueStep(execCtx, stepType); + processStepQueue(execCtx); + } + } + + private void doStep(final IExecutionDMContext execCtx, final StepType stepType) { + if (DEBUG) System.out.println("[SteppingController] doStep ctx="+execCtx); //$NON-NLS-1$ + disableStepping(execCtx); + updateLastStepTime(execCtx); + + getRunControl().step(execCtx, stepType, new RequestMonitor(getExecutor(), null) { + @Override + protected void handleFailure() { + if (getStatus().getCode() == IDsfStatusConstants.INVALID_STATE) { + // Ignore errors. During fast stepping there can be expected race + // conditions leading to stepping errors. + return; + } + super.handleFailure(); + } + }); + } + + /** + * Enqueue the given step for later execution. + * + * @param execCtx + * @param stepType + */ + private void doEnqueueStep(final IExecutionDMContext execCtx, final StepType stepType) { + List<StepRequest> stepQueue = fStepQueues.get(execCtx); + if (stepQueue == null) { + stepQueue = new LinkedList<StepRequest>(); + fStepQueues.put(execCtx, stepQueue); + } + if (stepQueue.size() < fQueueDepth) { + stepQueue.add(new StepRequest(execCtx, stepType)); + } + } + + /** + * Returns whether the step instruction for the given context has timed out. + */ + public boolean isSteppingTimedOut(IExecutionDMContext execCtx) { + for (IExecutionDMContext timedOutCtx : fTimedOutFlags.keySet()) { + if (execCtx.equals(timedOutCtx) || DMContexts.isAncestorOf(execCtx, timedOutCtx)) { + return fTimedOutFlags.get(timedOutCtx); + } + } + return false; + } + + /** + * Process next step on queue if any. + * @param execCtx + */ + private void processStepQueue(final IExecutionDMContext execCtx) { + final List<StepRequest> queue = getStepQueue(execCtx); + if (queue != null) { + final int stepDelay = getStepDelay(execCtx); + if (stepDelay > 0) { + getExecutor().schedule(new DsfRunnable() { + public void run() { + processStepQueue(execCtx); + } + }, stepDelay, TimeUnit.MILLISECONDS); + return; + } + final StepRequest request = queue.get(0); + if (DEBUG) System.out.println("[SteppingController] processStepQueue request-in-progress="+request.inProgress); //$NON-NLS-1$ + if (!request.inProgress) { + if (isSteppingDisabled(request.fContext)) { + return; + } + request.inProgress = true; + getRunControl().canStep( + request.fContext, request.fStepType, + new DataRequestMonitor<Boolean>(getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess() && getData()) { + queue.remove(0); + if (queue.isEmpty()) fStepQueues.remove(request.fContext); + doStep(request.fContext, request.fStepType); + } else { + // For whatever reason we can't step anymore, so clear out + // the step queue. + fStepQueues.remove(request.fContext); + } + } + }); + } + } + } + + private List<StepRequest> getStepQueue(IExecutionDMContext execCtx) { + List<StepRequest> queue = fStepQueues.get(execCtx); + if (queue == null) { + for (IExecutionDMContext stepCtx : fStepQueues.keySet()) { + if (DMContexts.isAncestorOf(stepCtx, execCtx)) { + queue = fStepQueues.get(stepCtx); + break; + } + } + } + return queue; + } + + /** + * Disable stepping for the given execution context. + * + * @param execCtx + */ + private void disableStepping(IExecutionDMContext execCtx) { + fStepInProgress.put(execCtx, new ArrayList<ISteppingControlParticipant>(fParticipants)); + } + + /** + * Indicate that processing of the last step has completed and + * the next step can be issued. + * + * @param execCtx + */ + private void doneStepping(final IExecutionDMContext execCtx) { + if (DEBUG) System.out.println("[SteppingController] doneStepping ctx=" + execCtx); //$NON-NLS-1$ + enableStepping(execCtx); + processStepQueue(execCtx); + } + + /** + * Enable stepping for the given execution context. + * + * @param execCtx + */ + private void enableStepping(final IExecutionDMContext execCtx) { + fStepInProgress.remove(execCtx); + for (IExecutionDMContext disabledCtx : fStepInProgress.keySet()) { + if (DMContexts.isAncestorOf(disabledCtx, execCtx)) { + fStepInProgress.remove(disabledCtx); + } + } + } + + private boolean isSteppingDisabled(IExecutionDMContext execCtx) { + boolean disabled= fStepInProgress.containsKey(execCtx); + if (!disabled) { + for (IExecutionDMContext disabledCtx : fStepInProgress.keySet()) { + if (DMContexts.isAncestorOf(execCtx, disabledCtx)) { + disabled = true; + break; + } + } + } + if (disabled) { + long now = System.currentTimeMillis(); + long lastStepTime = getLastStepTime(execCtx); + if (now - lastStepTime > MAX_STEP_DELAY) { + if (DEBUG) System.out.println("[SteppingController] stepping control participant(s) timed out"); //$NON-NLS-1$ + enableStepping(execCtx); + disabled = false; + } + } + return disabled; + } + + protected void handlePropertyChanged(final IPreferenceStore store, final PropertyChangeEvent event) { + String property = event.getProperty(); + if (IDsfDebugUIConstants.PREF_MIN_STEP_INTERVAL.equals(property)) { + setMinimumStepInterval(store.getInt(property)); + } + } + + + /////////////////////////////////////////////////////////////////////////// + + @DsfServiceEventHandler + public void eventDispatched(final ISuspendedDMEvent e) { + // Take care of the stepping time out + fTimedOutFlags.remove(e.getDMContext()); + ScheduledFuture<?> future = fTimedOutFutures.remove(e.getDMContext()); + if (future != null) future.cancel(false); + + // Check if there's a step pending, if so execute it + processStepQueue(e.getDMContext()); + } + + @DsfServiceEventHandler + public void eventDispatched(final IResumedDMEvent e) { + if (e.getReason().equals(StateChangeReason.STEP)) { + fTimedOutFlags.put(e.getDMContext(), Boolean.FALSE); + // We shouldn't have a stepping timeout running unless we get two + // stepping events in a row without a suspended, which would be a + // protocol error. + assert !fTimedOutFutures.containsKey(e.getDMContext()); + fTimedOutFutures.put( + e.getDMContext(), + getExecutor().schedule( + new DsfRunnable() { public void run() { + fTimedOutFutures.remove(e.getDMContext()); + + if (getSession().isActive()) { + // Issue the stepping time-out event. + getSession().dispatchEvent( + new SteppingTimedOutEvent(e.getDMContext()), + null); + } + }}, + STEPPING_TIMEOUT, TimeUnit.MILLISECONDS) + ); + + } + } + + @DsfServiceEventHandler + public void eventDispatched(SteppingTimedOutEvent e) { + fTimedOutFlags.put(e.getDMContext(), Boolean.TRUE); + enableStepping(e.getDMContext()); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/actions/AbstractVMProviderActionDelegate.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/actions/AbstractVMProviderActionDelegate.java new file mode 100644 index 00000000000..8bbd0226f4d --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/actions/AbstractVMProviderActionDelegate.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 2007, 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: + * Ted R Williams (Wind River Systems, Inc.) - initial implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.debug.ui.viewmodel.actions; + +import org.eclipse.cdt.dsf.ui.viewmodel.IVMAdapter; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMProvider; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer; +import org.eclipse.debug.ui.AbstractDebugView; +import org.eclipse.debug.ui.DebugUITools; +import org.eclipse.debug.ui.contexts.DebugContextEvent; +import org.eclipse.debug.ui.contexts.IDebugContextListener; +import org.eclipse.debug.ui.contexts.IDebugContextService; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.swt.widgets.Event; +import org.eclipse.ui.IActionDelegate2; +import org.eclipse.ui.IViewActionDelegate; +import org.eclipse.ui.IViewPart; + +/** + * @since 1.1 + */ +@SuppressWarnings("restriction") +abstract public class AbstractVMProviderActionDelegate implements IViewActionDelegate, IDebugContextListener, IActionDelegate2 { + + private IViewPart fView = null; + private IAction fAction = null; + private ISelection fDebugContext; + + public void init(IViewPart view) { + fView = view; + + // Get the current selection from the DebugView so we can determine if we want this menu action to be live or not. + IDebugContextService debugContextService = DebugUITools.getDebugContextManager().getContextService(view.getSite().getWorkbenchWindow()); + debugContextService.addPostDebugContextListener(this); + fDebugContext = debugContextService.getActiveContext(); + } + + public void selectionChanged(IAction action, ISelection selection) { + if (fAction != action) { + fAction = action; + } + } + + public void runWithEvent(IAction action, Event event) { + run(action); + } + + public void init(IAction action) { + fAction = action; + } + + public void dispose() { + DebugUITools.getDebugContextManager().getContextService(getView().getSite().getWorkbenchWindow()).removePostDebugContextListener(this); + } + + public void debugContextChanged(DebugContextEvent event) { + fDebugContext = event.getContext(); + } + + protected IViewPart getView() { return fView; } + + protected IAction getAction() { return fAction; } + + protected Object getViewerInput() { + if (fDebugContext instanceof IStructuredSelection) { + return ((IStructuredSelection)fDebugContext).getFirstElement(); + } + return null; + } + + protected IVMProvider getVMProvider() { + Object viewerInput = getViewerInput(); + IPresentationContext presentationContext = getPresentationContext(); + + if (viewerInput instanceof IAdaptable && presentationContext != null) { + IVMAdapter adapter = (IVMAdapter) ((IAdaptable)viewerInput).getAdapter(IVMAdapter.class); + + if ( adapter != null ) { + return adapter.getVMProvider(presentationContext); + } + } + + return null; + } + + protected IPresentationContext getPresentationContext() { + if (fView instanceof AbstractDebugView && + ((AbstractDebugView) fView).getViewer() instanceof TreeModelViewer) + { + return ((TreeModelViewer) ((AbstractDebugView) fView).getViewer()).getPresentationContext(); + } + return null; + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/actions/DefaultRefreshAllTarget.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/actions/DefaultRefreshAllTarget.java new file mode 100644 index 00000000000..95bae0930de --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/actions/DefaultRefreshAllTarget.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 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.actions; + +import org.eclipse.cdt.dsf.ui.viewmodel.IVMAdapterExtension; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.update.ICachingVMProvider; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; + +/** + * The default implementation of the refresh all debug target which + * calls the active VM providers, to ask them to refresh. + * + * @since 1.1 + */ +public class DefaultRefreshAllTarget implements IRefreshAllTarget { + + public void refresh(ISelection debugContext) throws CoreException { + IVMAdapterExtension adapter = getActiveVMAdapter( debugContext ); + + if (adapter != null) { + for (IVMProvider provider : adapter.getActiveProviders()) { + if (provider instanceof ICachingVMProvider) { + ((ICachingVMProvider)provider).refresh(); + } + } + } + } + + protected IVMAdapterExtension getActiveVMAdapter(ISelection debugContext) { + + if (debugContext instanceof IStructuredSelection) { + Object activeElement = ((IStructuredSelection)debugContext).getFirstElement(); + if (activeElement instanceof IAdaptable) { + return (IVMAdapterExtension)((IAdaptable)activeElement).getAdapter(IVMAdapterExtension.class); + } + } + return null; + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/actions/IRefreshAllTarget.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/actions/IRefreshAllTarget.java new file mode 100644 index 00000000000..cf1f50ec876 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/actions/IRefreshAllTarget.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 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.actions; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jface.viewers.ISelection; + +/** + * A retargetable action target which allows a debugger to refresh all of its + * active views with fresh data from the debug target. + * + * @since 1.1 + */ +public interface IRefreshAllTarget { + + /** + * Refreshes the debugger data of the given debug context. + * @param debugContext The active window debug context. + * + * @throws CoreException + */ + public void refresh(ISelection debugContext) throws CoreException; +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/actions/VMHandlerUtils.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/actions/VMHandlerUtils.java new file mode 100644 index 00000000000..594c89290ce --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/actions/VMHandlerUtils.java @@ -0,0 +1,137 @@ +/******************************************************************************* + * Copyright (c) 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.actions; + +import org.eclipse.cdt.dsf.ui.viewmodel.IVMAdapter; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMContext; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMProvider; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer; +import org.eclipse.debug.ui.DebugUITools; +import org.eclipse.debug.ui.IDebugView; +import org.eclipse.debug.ui.contexts.IDebugContextService; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.ui.IPartService; +import org.eclipse.ui.ISelectionService; +import org.eclipse.ui.IViewPart; +import org.eclipse.ui.IViewSite; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.handlers.HandlerUtil; +import org.eclipse.ui.services.IServiceLocator; + +/** + * Static utility methods for use with View Model related + * commands and handlers. + * + * @since 1.1 + */ +@SuppressWarnings("restriction") +public class VMHandlerUtils { + + /** + * Retrieves the active VM provider based on the currently active + * selection and part. + * @param serviceLocator Service locator for access to active selection + * and part. + * + * @return The active VM provder. + */ + static public IVMProvider getActiveVMProvider(IServiceLocator serviceLocator) { + ISelection selection = null; + + ISelectionService selectionService = + (ISelectionService)serviceLocator.getService(ISelectionService.class); + if (selectionService != null) { + selection = selectionService.getSelection(); + } + + if (selection != null && !selection.isEmpty()) { + return getVMProviderForSelection(selection); + } + else { + IWorkbenchPart part = null; + IPartService partService = (IPartService)serviceLocator.getService(IPartService.class); + if (partService != null) { + part = partService.getActivePart(); + } + return getVMProviderForPart(part); + } + } + + /** + * Retrieves the active VM provider based on the given execution event. + * @param event The execution event which is usually given as an argument + * to the command handler execution call. + * + * @return The active VM provder. + */ + static public IVMProvider getActiveVMProvider(ExecutionEvent event) { + ISelection selection = HandlerUtil.getCurrentSelection(event); + if (selection != null && !selection.isEmpty()) { + return getVMProviderForSelection(selection); + } + else { + IWorkbenchPart part = HandlerUtil.getActivePart(event); + return getVMProviderForPart(part); + } + } + + public static IVMProvider getVMProviderForPart(IWorkbenchPart part) { + IDebugContextService contextService = + DebugUITools.getDebugContextManager().getContextService(part.getSite().getWorkbenchWindow()); + + ISelection debugContext = contextService.getActiveContext(getPartId(part)); + if (debugContext == null) { + debugContext = contextService.getActiveContext(); + } + + Object input = null; + if (debugContext instanceof IStructuredSelection) { + input = ((IStructuredSelection)debugContext).getFirstElement(); + } + + if (part instanceof IDebugView) { + Viewer viewer = ((IDebugView)part).getViewer(); + if (input instanceof IAdaptable && viewer instanceof TreeModelViewer) { + IPresentationContext presContext = ((TreeModelViewer)viewer).getPresentationContext(); + IVMAdapter vmAdapter = (IVMAdapter)((IAdaptable)input).getAdapter(IVMAdapter.class); + if (vmAdapter != null) { + return vmAdapter.getVMProvider(presContext); + } + } + } + return null; + } + + private static String getPartId(IWorkbenchPart part) { + if (part instanceof IViewPart) { + IViewSite site = (IViewSite)part.getSite(); + return site.getId() + (site.getSecondaryId() != null ? (":" + site.getSecondaryId()) : ""); //$NON-NLS-1$ //$NON-NLS-2$ + } else { + return part.getSite().getId(); + } + } + + public static IVMProvider getVMProviderForSelection(ISelection selection) { + if (selection instanceof IStructuredSelection) { + Object element = ((IStructuredSelection)selection).getFirstElement(); + if (element instanceof IVMContext) { + return ((IVMContext)element).getVMNode().getVMProvider(); + } + } + return null; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/AbstractExpressionVMNode.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/AbstractExpressionVMNode.java new file mode 100644 index 00000000000..6e2a34f3e4c --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/AbstractExpressionVMNode.java @@ -0,0 +1,178 @@ +/******************************************************************************* + * Copyright (c) 2007 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.List; + +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.MultiRequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +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.IVMContext; +import org.eclipse.cdt.dsf.ui.viewmodel.VMChildrenUpdate; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.model.IExpression; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; + +/** + * Base class for VM Nodes which can be used in the expressions view. + * <p> + * This base class uses the methods {@link #canParseExpression(IExpression)} and + * {@link #update(IChildrenUpdate[])} to implement the + * {@link IExpressionVMNode#update(IExpressionUpdate)} + * method. Two additional abstract protected methods need to be implemented + * by the sub-class as well. + * </p> + */ +@SuppressWarnings("restriction") +public abstract class AbstractExpressionVMNode extends AbstractDMVMNode + implements IExpressionVMNode +{ + public AbstractExpressionVMNode(AbstractDMVMProvider provider, DsfSession session, Class<? extends IDMContext> dmcClassType) { + super(provider, session, dmcClassType); + } + + public void update(final IExpressionUpdate update) { + if (!canParseExpression(update.getExpression())) { + // This method should not be called if canParseExpression() returns false. + // Return an internal error status. + update.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Cannot parse expression", null)); //$NON-NLS-1$ + update.done(); + return; + } + + // Retrieve the list of all elements from the sub-class. Then compare + // each returned element to the expression in the update, using + // testElementForExpression(). The element that matches the expression + // is returned to the client. + // If no matching element is found, the createInvalidExpressionVMContext() + // method is called to a special context. + update(new IChildrenUpdate[] { new VMChildrenUpdate( + update, -1, -1, + new ViewerDataRequestMonitor<List<Object>>(getExecutor(), update) { + @Override + protected void handleSuccess() { + if (getData().size() == 0) { + update.setExpressionElement(createInvalidExpressionVMContext(update.getExpression())); + update.done(); + } else { + final List<Object> elements = getData(); + + final MultiRequestMonitor<DataRequestMonitor<Boolean>> multiRm = new MultiRequestMonitor<DataRequestMonitor<Boolean>>(getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + boolean foundMatchingContext = false; + for (int i = 0; i < getRequestMonitors().size(); i++) { + if (getRequestMonitors().get(i).getData()) { + Object element = elements.get(i); + associateExpression(element, update.getExpression()); + update.setExpressionElement(element); + foundMatchingContext = true; + break; + } + } + if (!foundMatchingContext) { + update.setExpressionElement(createInvalidExpressionVMContext(update.getExpression())); + } + } else { + update.setStatus(getStatus()); + } + update.done(); + } + }; + + for (Object element : elements) { + testElementForExpression( + element, update.getExpression(), + multiRm.add( + new DataRequestMonitor<Boolean>(getExecutor(), null) { + @Override + protected void handleCompleted() { + multiRm.requestMonitorDone(this); + } + })); + } + } + } + + @Override + protected void handleFailure() { + update.setStatus(getStatus()); + update.done(); + } + })} + ); + + } + + + /** + * Tests whether the given element matches the given expression. + * + * @param element Element to test against the given expression. + * @param expression Expression to use to check if the element is matching. + * @param rm The request monitor for the result. + */ + @ConfinedToDsfExecutor("#getSession#getExecutor") + protected void testElementForExpression(Object element, IExpression expression, final DataRequestMonitor<Boolean> rm) { + rm.setData(false); + rm.done(); + } + + /** + * Sets the given expression as the expression belonging to the given + * element. + * <p> + * This base class creates VM context elements using the extending class's + * {@link #update(IChildrenUpdate[])} method. The element matching the + * expression is found using {@link #testElementForExpression(Object, IExpression, DataRequestMonitor)}. + * Once the matching element is found it needs to be linked to the expression + * so that it can be distinguished from other contexts created for identical + * but distinct expressions. This method accomplishes this task. Elements + * which are associated with expressions should use the expression object + * for implementation of {@link #equals(Object)} and {@link #hashCode()} + * methods. + * </p> + * + * @param element + * @param expression + */ + protected void associateExpression(Object element, IExpression expression) { + } + + /** + * Create a place holder for an invalid expression. If for a given expression, + * this VM node returns true from {@link #canParseExpression(IExpression)}, which + * indicates that the expression matches the node's expected format, but the node + * then is not able to find the element represented by the expression, then an + * "invalid" expression context needs to be created. + * <p> + * This method can be overriden to provide a node-specific invalid expression + * context. + * </p> + * + * @param expression Expression to create the context for. + * @return Returns a VM context object representing an invalid expression with + * + * @since 1.1 + */ + protected IVMContext createInvalidExpressionVMContext(IExpression expression) { + return new InvalidExpressionVMContext(this, expression); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/ExpressionColumnPresentation.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/ExpressionColumnPresentation.java new file mode 100644 index 00000000000..8c5b437d712 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/ExpressionColumnPresentation.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * 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 org.eclipse.cdt.dsf.debug.ui.viewmodel.IDebugVMConstants; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.jface.resource.ImageDescriptor; + +/** + * + */ +@SuppressWarnings("restriction") +public class ExpressionColumnPresentation implements IColumnPresentation { + + public static final String ID = DsfUIPlugin.PLUGIN_ID + ".EXPRESSION_COLUMN_PRESENTATION_ID"; //$NON-NLS-1$ + + public void init(IPresentationContext context) { + } + + public void dispose() { + } + + // @see org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentation#getAvailableColumns() + public String[] getAvailableColumns() { + return new String[] { IDebugVMConstants.COLUMN_ID__EXPRESSION, IDebugVMConstants.COLUMN_ID__NAME, IDebugVMConstants.COLUMN_ID__TYPE, IDebugVMConstants.COLUMN_ID__VALUE, IDebugVMConstants.COLUMN_ID__DESCRIPTION, IDebugVMConstants.COLUMN_ID__ADDRESS }; + } + + // @see org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentation#getHeader(java.lang.String) + public String getHeader(String id) { + if (IDebugVMConstants.COLUMN_ID__EXPRESSION.equals(id)) { + return MessagesForExpressionVM.ExpressionColumnPresentation_expression; + } else if (IDebugVMConstants.COLUMN_ID__NAME.equals(id)) { + return MessagesForExpressionVM.ExpressionColumnPresentation_name; + } else if (IDebugVMConstants.COLUMN_ID__TYPE.equals(id)) { + return MessagesForExpressionVM.ExpressionColumnPresentation_type; + } else if (IDebugVMConstants.COLUMN_ID__VALUE.equals(id)) { + return MessagesForExpressionVM.ExpressionColumnPresentation_value; + } else if (IDebugVMConstants.COLUMN_ID__DESCRIPTION.equals(id)) { + return MessagesForExpressionVM.ExpressionColumnPresentation_description; + } else if (IDebugVMConstants.COLUMN_ID__ADDRESS.equals(id)) { + return MessagesForExpressionVM.ExpressionColumnPresentation_address; + } + return null; + } + + // @see org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentation#getId() + public String getId() { + return ID; + } + + public ImageDescriptor getImageDescriptor(String id) { + return null; + } + + // @see org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentation#getInitialColumns() + public String[] getInitialColumns() { + return new String[] { IDebugVMConstants.COLUMN_ID__EXPRESSION, IDebugVMConstants.COLUMN_ID__TYPE, IDebugVMConstants.COLUMN_ID__VALUE }; + } + + // @see org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentation#isOptional() + public boolean isOptional() { + return true; + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/ExpressionManagerVMNode.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/ExpressionManagerVMNode.java new file mode 100644 index 00000000000..ae679c01bbb --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/ExpressionManagerVMNode.java @@ -0,0 +1,307 @@ +/******************************************************************************* + * 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.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.debug.ui.viewmodel.IDebugVMConstants; +import org.eclipse.cdt.dsf.ui.concurrent.ViewerCountingRequestMonitor; +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.debug.core.DebugPlugin; +import org.eclipse.debug.core.IExpressionManager; +import org.eclipse.debug.core.model.IExpression; +import org.eclipse.debug.internal.ui.IInternalDebugUIConstants; +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.IElementEditor; +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.IPresentationContext; +import org.eclipse.jface.resource.JFaceResources; +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; + +/** + * This is the top-level view model node in the expressions view. Its job is to: + * <li> + * <ol> retrieve the {@link IExpression} objects from the global {@link IExpressionManager},</ol> + * <ol> retrieve the expression string from the <code>IExpression</code> object,</ol> + * <ol> then to call the configured expression nodes to parse the expression string.</ol> + * </li> + * <p> + * This node is not intended to have any standard child nodes, therefore + * the implementation of {@link #setChildNodes(IVMNode[])} throws an exception. + * Instead users should call {@link #setExpressionNodes(IExpressionVMNode[])} + * to configure the nodes that this node will delegate to when processing expressions. + * </p> + */ +@SuppressWarnings("restriction") +public class ExpressionManagerVMNode extends AbstractVMNode + implements IElementLabelProvider, IElementEditor +{ + /** + * VMC for a new expression object to be added. When user clicks on this node to + * edit it, he will create a new expression. + */ + class NewExpressionVMC extends AbstractVMContext { + public NewExpressionVMC() { + super(ExpressionManagerVMNode.this); + } + + @Override + @SuppressWarnings("unchecked") + public Object getAdapter(Class adapter) { + return super.getAdapter(adapter); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof NewExpressionVMC; + } + + @Override + public int hashCode() { + return getClass().hashCode(); + } + } + + /** Local reference to the global expression manager */ + private IExpressionManager fManager = DebugPlugin.getDefault().getExpressionManager(); + + /** Cached reference to a cell modifier for editing expression strings of invalid expressions */ + private WatchExpressionCellModifier fWatchExpressionCellModifier = new WatchExpressionCellModifier(); + + public ExpressionManagerVMNode(ExpressionVMProvider provider) { + super(provider); + } + + @Override + public String toString() { + return "ExpressionManagerVMNode"; //$NON-NLS-1$ + } + + private ExpressionVMProvider getExpressionVMProvider() { + return (ExpressionVMProvider)getVMProvider(); + } + + 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(); + } + } + + 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 + 1); + update.done(); + } + } + + 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<Object>(getVMProvider().getExecutor(), multiRm) { + @Override + protected void handleSuccess() { + update.setChild(getData(), childIndex); + multiRm.done(); + } + + @Override + protected void handleError() { + update.setChild(new InvalidExpressionVMContext(ExpressionManagerVMNode.this, expression), childIndex); + multiRm.done(); + } + }) + ); + } else { + // Last element in the list of expressions is the "add new expression" + // dummy entry. + update.setChild(new NewExpressionVMC(), i); + } + } + + // 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) { + if (update.getElement() instanceof NewExpressionVMC) { + updateNewExpressionVMCLabel(update, (NewExpressionVMC) update.getElement()); + } else { + update.done(); + } + } + } + + /** + * Updates the label for the NewExpressionVMC. + */ + private void updateNewExpressionVMCLabel(ILabelUpdate update, NewExpressionVMC vmc) { + String[] columnIds = update.getColumnIds() != null ? + update.getColumnIds() : new String[] { IDebugVMConstants.COLUMN_ID__NAME }; + + for (int i = 0; i < columnIds.length; i++) { + if (IDebugVMConstants.COLUMN_ID__EXPRESSION.equals(columnIds[i])) { + update.setLabel(MessagesForExpressionVM.ExpressionManagerLayoutNode__newExpression_label, i); + update.setFontData(JFaceResources.getFontDescriptor(IInternalDebugUIConstants.VARIABLE_TEXT_FONT).getFontData()[0], i); + } else { + update.setLabel("", i); //$NON-NLS-1$ + } + } + + + 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<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 CellEditor getCellEditor(IPresentationContext context, String columnId, Object element, Composite parent) { + if (IDebugVMConstants.COLUMN_ID__EXPRESSION.equals(columnId)) { + return new TextCellEditor(parent); + } + return null; + } + + public ICellModifier getCellModifier(IPresentationContext context, Object element) { + return fWatchExpressionCellModifier; + } +} 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. + } + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/ExpressionVMProviderContentStragegy.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/ExpressionVMProviderContentStragegy.java new file mode 100644 index 00000000000..55535acc67c --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/ExpressionVMProviderContentStragegy.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright (c) 2007, 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 org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.cdt.dsf.ui.concurrent.ViewerDataRequestMonitor; +import org.eclipse.cdt.dsf.ui.viewmodel.DefaultVMContentProviderStrategy; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +/** + * The IElementContentProvider implementation to be used with an expression + * view model provider. + * + * @see ExpressionVMProvider + */ +@SuppressWarnings("restriction") +public class ExpressionVMProviderContentStragegy extends DefaultVMContentProviderStrategy { + public ExpressionVMProviderContentStragegy(ExpressionVMProvider provider) { + super(provider); + } + + private ExpressionVMProvider getExpressionVMProvider() { + return (ExpressionVMProvider)getVMProvider(); + } + + public void update(final IExpressionUpdate update) { + final IExpressionVMNode matchingNode = + getExpressionVMProvider().findNodeToParseExpression(null, update.getExpression()); + + if (matchingNode != null) { + updateExpressionWithNode(matchingNode, update); + } else { + update.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Cannot parse expression", null)); //$NON-NLS-1$ + update.done(); + } + } + + private void updateExpressionWithNode(final IExpressionVMNode node, final IExpressionUpdate update) { + // Call the expression node to parse the expression and fill in the value. + node.update( + new VMExpressionUpdate( + update, update.getExpression(), + new ViewerDataRequestMonitor<Object>(getVMProvider().getExecutor(), update) { + @Override + protected void handleSuccess() { + // Check if the evaluated node has child expression nodes. + // If it does, check if any of those nodes can evaluate the given + // expression further. If they can, call the child node to further + // process the expression. Otherwise we found our element and + // we're done. + final IExpressionVMNode matchingNode = getExpressionVMProvider(). + findNodeToParseExpression(node, update.getExpression()); + + if (matchingNode != null && !matchingNode.equals(node)) { + updateExpressionWithNode( + matchingNode, + new VMExpressionUpdate( + update.getElementPath().createChildPath(getData()), update.getViewerInput(), + update.getPresentationContext(), update.getExpression(), + new ViewerDataRequestMonitor<Object>(getVMProvider().getExecutor(), update) { + + @Override + protected void handleSuccess() { + update.setExpressionElement(getData()); + update.done(); + } + }) + ); + } else { + update.setExpressionElement(getData()); + update.done(); + } + } + }) + ); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/ExpressionVMProviderModelProxyStrategy.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/ExpressionVMProviderModelProxyStrategy.java new file mode 100644 index 00000000000..fb59a74e02e --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/ExpressionVMProviderModelProxyStrategy.java @@ -0,0 +1,179 @@ +/******************************************************************************* + * Copyright (c) 2007, 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.Map; + +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.ui.viewmodel.DefaultVMModelProxyStrategy; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.VMDelta; +import org.eclipse.debug.core.model.IExpression; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.jface.viewers.TreePath; + +/** + * The IModelProxy implementation to be used with an expression + * view model provider. + * + * @see ExpressionVMProvider + */ +@SuppressWarnings("restriction") +public class ExpressionVMProviderModelProxyStrategy extends DefaultVMModelProxyStrategy { + + public ExpressionVMProviderModelProxyStrategy(ExpressionVMProvider provider, Object rootElement) { + super(provider, rootElement); + } + + private ExpressionVMProvider getExpressionVMProvider() { + return (ExpressionVMProvider)getVMProvider(); + } + + public int getDeltaFlagsForExpression(IExpression expression, Object event) { + final IExpressionVMNode matchingNode = getExpressionVMProvider().findNodeToParseExpression(null, expression); + + if (matchingNode != null) { + return getNodeDeltaFlagsForExpression(matchingNode, expression, event); + } + return IModelDelta.NO_CHANGE; + } + + private int getNodeDeltaFlagsForExpression(IExpressionVMNode node, IExpression expression, Object event) { + int flags = node.getDeltaFlagsForExpression(expression, event); + + IExpressionVMNode matchingNode = getExpressionVMProvider().findNodeToParseExpression(node, expression); + if (matchingNode != null && !matchingNode.equals(node)) { + flags = flags | getNodeDeltaFlagsForExpression(matchingNode, expression, event); + } else { + // Check the child nodes of this expression node for additional + // delta flags. + for (IVMNode childNode : getVMProvider().getChildVMNodes(node)) { + if (!childNode.equals(node)) { + int childNodeDeltaFlags = getDeltaFlags(childNode, null, event); + if ((childNodeDeltaFlags & IModelDelta.CONTENT) != 0) { + childNodeDeltaFlags &= ~IModelDelta.CONTENT; + childNodeDeltaFlags |= IModelDelta.STATE; + } + flags |= childNodeDeltaFlags; + } + } + } + return flags; + } + + public void buildDeltaForExpression(IExpression expression, int expressionElementIdx, Object event, + VMDelta parentDelta, TreePath path, RequestMonitor rm) + { + final IExpressionVMNode matchingNode = getExpressionVMProvider().findNodeToParseExpression(null, expression); + + if (matchingNode != null) { + buildNodeDeltaForExpression(matchingNode, expression, expressionElementIdx, event, + parentDelta, path, rm); + } else { + rm.done(); + } + } + + private void buildNodeDeltaForExpression(final IExpressionVMNode node, final IExpression expression, + final int expressionElementIdx, final Object event, final VMDelta parentDelta, final TreePath path, + final RequestMonitor rm) + { + node.buildDeltaForExpression( + expression, expressionElementIdx, event, parentDelta, path, + new RequestMonitor(getVMProvider().getExecutor(), rm) { + @Override + protected void handleSuccess() { + final IExpressionVMNode matchingNode = + getExpressionVMProvider().findNodeToParseExpression(node, expression); + if (matchingNode != null && !matchingNode.equals(node)) { + buildNodeDeltaForExpression( + matchingNode, expression, expressionElementIdx, event, parentDelta, path, rm); + } else { + getExpressionVMProvider().update(new VMExpressionUpdate( + parentDelta, getVMProvider().getPresentationContext(), expression, + new DataRequestMonitor<Object>(getVMProvider().getExecutor(), rm) { + @Override + protected void handleSuccess() { + buildDeltaForExpressionElement( + node, expression, getData(), expressionElementIdx, event, parentDelta, path, rm); + } + + @Override + protected void handleErrorOrWarning() { + // Avoid propagating the error to avoid processing the delta by + // all nodes. + rm.done(); + } + })); + } + } + }); + } + + + private void buildDeltaForExpressionElement(IExpressionVMNode node, IExpression expression, Object expressionElement, + int expressionElementIdx, Object event, VMDelta parentDelta, TreePath path, RequestMonitor rm) + { + CountingRequestMonitor multiRm = new CountingRequestMonitor(getVMProvider().getExecutor(), rm); + int multiRmCount = 0; + + node.buildDeltaForExpressionElement(expressionElement, expressionElementIdx, event, parentDelta, multiRm); + multiRmCount++; + + // Find the child nodes that have deltas for the given event. + Map<IVMNode,Integer> childNodesWithDeltaFlags = getChildNodesWithDeltaFlags(node, parentDelta, event); + + // If no child layout nodes have deltas we can stop here. + if (childNodesWithDeltaFlags.size() != 0) { + callChildNodesToBuildDelta( + node, childNodesWithDeltaFlags, + parentDelta.addNode(expressionElement, expressionElementIdx, IModelDelta.NO_CHANGE), + event, multiRm); + multiRmCount++; + } + + if (event instanceof ExpressionsChangedEvent) { + buildDeltaForExpressionsChangedEvent(expressionElement, expressionElementIdx, + (ExpressionsChangedEvent)event, parentDelta, multiRm); + multiRmCount++; + } + + multiRm.setDoneCount(multiRmCount); + } + + private void buildDeltaForExpressionsChangedEvent(Object element, int elementIdx, ExpressionsChangedEvent event, + VMDelta parentDelta, final RequestMonitor rm) + { + switch (event.getType()) { + case ADDED: + parentDelta.addNode(element, -1, IModelDelta.ADDED); + break; + case CHANGED: + parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); + break; + case MOVED: + parentDelta.addNode(element, -1, IModelDelta.REMOVED); + parentDelta.addNode(element, elementIdx, IModelDelta.INSERTED); + break; + case REMOVED: + parentDelta.addNode(element, -1, IModelDelta.REMOVED); + break; + case INSERTED: + parentDelta.addNode(element, elementIdx, IModelDelta.INSERTED); + break; + default: + break; + } + rm.done(); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/ExpressionsBreakpointHitUpdatePolicy.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/ExpressionsBreakpointHitUpdatePolicy.java new file mode 100644 index 00000000000..63bdc7f58bc --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/ExpressionsBreakpointHitUpdatePolicy.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 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 org.eclipse.cdt.dsf.debug.ui.viewmodel.update.BreakpointHitUpdatePolicy; +import org.eclipse.cdt.dsf.ui.viewmodel.update.IElementUpdateTester; + +/** + * Manual update policy which selectively clears the cache when the expressions + * in the expression manager are modified. + */ +public class ExpressionsBreakpointHitUpdatePolicy extends BreakpointHitUpdatePolicy { + + @Override + public IElementUpdateTester getElementUpdateTester(Object event) { + if (event instanceof ExpressionsChangedEvent) { + return new ExpressionsChangedUpdateTester((ExpressionsChangedEvent)event); + } + return super.getElementUpdateTester(event); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/ExpressionsChangedEvent.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/ExpressionsChangedEvent.java new file mode 100644 index 00000000000..058a6f39e2a --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/ExpressionsChangedEvent.java @@ -0,0 +1,38 @@ +package org.eclipse.cdt.dsf.debug.ui.viewmodel.expression; + +import java.util.Arrays; +import java.util.Set; + +import org.eclipse.debug.core.model.IExpression; + +/** + * Object representing a change in configured expressions. This event is + * object is used when generating a model delta. + */ +public class ExpressionsChangedEvent { + enum Type {ADDED, CHANGED, REMOVED, MOVED, INSERTED} + + private final Set<Object> fExpressionManagerElements; + private final ExpressionsChangedEvent.Type fType; + private final IExpression[] fExpressions; + private final int fIndex; + + public ExpressionsChangedEvent(ExpressionsChangedEvent.Type type, Set<Object> expressionManagerElements, + IExpression[] expressions, int index) + { + fExpressionManagerElements = expressionManagerElements; + fType = type; + fExpressions = expressions; + fIndex = index; + } + + public Set<Object> getExpressionManagerElements() { return fExpressionManagerElements; } + public ExpressionsChangedEvent.Type getType() { return fType; } + public IExpression[] getExpressions() { return fExpressions; } + public int getIndex() { return fIndex; } + + @Override + public String toString() { + return Arrays.asList(fExpressions).toString() + " " + fType + "@" + fIndex; //$NON-NLS-1$ //$NON-NLS-2$ + } +}
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/ExpressionsChangedUpdateTester.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/ExpressionsChangedUpdateTester.java new file mode 100644 index 00000000000..9f29b3c466a --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/ExpressionsChangedUpdateTester.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 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 org.eclipse.cdt.dsf.ui.viewmodel.update.IElementUpdateTester; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.debug.core.model.IExpression; +import org.eclipse.jface.viewers.TreePath; + +class ExpressionsChangedUpdateTester implements IElementUpdateTester { + + private final ExpressionsChangedEvent fEvent; + + public ExpressionsChangedUpdateTester(ExpressionsChangedEvent event) { + fEvent = event; + } + + public int getUpdateFlags(Object viewerInput, TreePath path) { + // Check whether the element in the cache matches the expression manager element. + Object element = path.getSegmentCount() == 0 ? viewerInput : path.getLastSegment(); + if (fEvent.getExpressionManagerElements().contains(element)) { + return ExpressionsManualUpdatePolicy.FLUSH; + } + + // If the expressions were modified, flush the entries which are under the + // given expression. To do that, check whether the element path contains one + // of the changed expressions. + if (fEvent.getType().equals(ExpressionsChangedEvent.Type.CHANGED)) { + for (int i = 0; i < path.getSegmentCount(); i++) { + if (eventContainsElement(path.getSegment(i))) { + return ExpressionsManualUpdatePolicy.FLUSH; + } + } + } + return 0; + } + + private boolean eventContainsElement(Object element) { + if (element instanceof IAdaptable) { + IExpression expression = (IExpression)((IAdaptable)element).getAdapter(IExpression.class); + if (expression != null) { + for (int i = 0; i < fEvent.getExpressions().length; i++) { + if (expression.equals(fEvent.getExpressions()[i])) { + return true; + } + } + } + } + return false; + } + + public boolean includes(IElementUpdateTester tester) { + return tester instanceof ExpressionsChangedUpdateTester; + } + + @Override + public String toString() { + return "(" + fEvent + ") update tester"; //$NON-NLS-1$ //$NON-NLS-2$ + } +}
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/ExpressionsManualUpdatePolicy.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/ExpressionsManualUpdatePolicy.java new file mode 100644 index 00000000000..8ca367468a6 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/ExpressionsManualUpdatePolicy.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 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 org.eclipse.cdt.dsf.ui.viewmodel.update.IElementUpdateTester; +import org.eclipse.cdt.dsf.ui.viewmodel.update.ManualUpdatePolicy; + +/** + * Manual update policy which selectively clears the cache when the expressions + * in the expression manager are modified. + */ +public class ExpressionsManualUpdatePolicy extends ManualUpdatePolicy { + + @Override + public IElementUpdateTester getElementUpdateTester(Object event) { + if (event instanceof ExpressionsChangedEvent) { + return new ExpressionsChangedUpdateTester((ExpressionsChangedEvent)event); + } + return super.getElementUpdateTester(event); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/IExpressionUpdate.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/IExpressionUpdate.java new file mode 100644 index 00000000000..e3fd41fc4a7 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/IExpressionUpdate.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2007, 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 org.eclipse.debug.core.model.IExpression; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; + +/** + * An update for an element based on the given expression. The provider processing + * this update needs to create an expression element based on the tree path and the + * expression object in this update. + */ +@SuppressWarnings("restriction") +public interface IExpressionUpdate extends IViewerUpdate { + + /** + * Returns the expression object for this update. + */ + public IExpression getExpression(); + + /** + * Sets the element to the update. The element is to be calculated by the provider + * handling the update. + */ + public void setExpressionElement(Object element); +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/IExpressionVMNode.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/IExpressionVMNode.java new file mode 100644 index 00000000000..1403d73d25e --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/IExpressionVMNode.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2007, 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 org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.VMDelta; +import org.eclipse.debug.core.model.IExpression; +import org.eclipse.jface.viewers.TreePath; + +/** + * Interface for view model nodes that can be used within the expression view. + * The methods of this interface allow the {@link ExpressionManagerVMNode} + * to use this node to delegate expression parsing to this node, and to + * generate deltas for expressions that are owned by this node. + */ +public interface IExpressionVMNode extends IVMNode { + + /** + * Returns whether the given expression node recognizes and can parse the given + * expression. + * @param expression Expression that needs to be parsed. + * @return true if expression can be parsed + */ + public boolean canParseExpression(IExpression expression); + + /** + * Asynchronously fills in the given expression update. + * @param update Update to complete. + */ + public void update(IExpressionUpdate update); + + /** + * Returns the flags that this node can generate for the given expression and + * event. + */ + public int getDeltaFlagsForExpression(IExpression expression, Object event); + + /** + * Adds delta flags to the given parent delta based on the expression + * object given. The nodes add flags to the expression view's root + * delta using this method, regardless of whether the node is directly + * below the expression manager or not. + * + */ + public void buildDeltaForExpression(IExpression expression, int elementIdx, Object event, VMDelta parentDelta, + TreePath path, RequestMonitor rm); + + /** + * Adds delta to the given parent delta based on the given element that + * was created base on an expression parsed by this node. The VM nodes can + * add a new delta node to the parentDela by implementing this method. + */ + public void buildDeltaForExpressionElement(Object element, int elementIdx, Object event, VMDelta parentDelta, final RequestMonitor rm); +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/InvalidExpressionVMContext.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/InvalidExpressionVMContext.java new file mode 100644 index 00000000000..9731351bb98 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/InvalidExpressionVMContext.java @@ -0,0 +1,88 @@ +package org.eclipse.cdt.dsf.debug.ui.viewmodel.expression; + +import org.eclipse.cdt.dsf.debug.ui.viewmodel.IDebugVMConstants; +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMContext; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMNode; +import org.eclipse.debug.core.model.IExpression; +import org.eclipse.debug.internal.ui.IInternalDebugUIConstants; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.debug.ui.DebugUITools; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.jface.resource.JFaceResources; + +/** + * VMC of an expression object that failed to get parsed by any of the + * configured expression layout nodes. It is only used to display an + * error message in the view, and to allow the user to edit the + * expression. + * <p> + * Note: VM Nodes using this invalid expression VM context should + * provide a cell modifier to edit the expressions. The cell modifier + * should subclass {@link WatchExpressionCellModifier}. + * </p> + * + * @since 1.1 + */ +@SuppressWarnings("restriction") +public class InvalidExpressionVMContext extends AbstractVMContext implements IElementLabelProvider { + + final private IExpression fExpression; + + public InvalidExpressionVMContext(IVMNode node, IExpression expression) { + super(node); + fExpression = expression; + } + + @Override + @SuppressWarnings("unchecked") + public Object getAdapter(Class adapter) { + if (adapter.isAssignableFrom(fExpression.getClass())) { + return fExpression; + } else { + return super.getAdapter(adapter); + } + } + + public IExpression getExpression() { + return fExpression; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof InvalidExpressionVMContext && ((InvalidExpressionVMContext)obj).fExpression.equals(fExpression); + } + + @Override + public int hashCode() { + return fExpression.hashCode(); + } + + /** + * Updates the label for the InvalidExpressionVMC. + */ + public void update(ILabelUpdate[] updates) { + for (ILabelUpdate update : updates) { + String[] columnIds = update.getColumnIds() != null ? + update.getColumnIds() : new String[] { IDebugVMConstants.COLUMN_ID__NAME }; + + for (int i = 0; i < columnIds.length; i++) { + if (IDebugVMConstants.COLUMN_ID__EXPRESSION.equals(columnIds[i])) { + update.setLabel(getExpression().getExpressionText(), i); + update.setImageDescriptor(DebugUITools.getImageDescriptor( IDebugUIConstants.IMG_OBJS_EXPRESSION ), i); + } else if (IDebugVMConstants.COLUMN_ID__NAME.equals(columnIds[i])) { + update.setLabel(getExpression().getExpressionText(), i); + update.setImageDescriptor(DebugUITools.getImageDescriptor( IDebugUIConstants.IMG_OBJS_EXPRESSION ), i); + } else if (IDebugVMConstants.COLUMN_ID__VALUE.equals(columnIds[i])) { + update.setLabel(MessagesForExpressionVM.ExpressionManagerLayoutNode__invalidExpression_valueColumn_label, i); + } else { + update.setLabel("", i); //$NON-NLS-1$ + } + update.setFontData(JFaceResources.getFontDescriptor(IInternalDebugUIConstants.VARIABLE_TEXT_FONT).getFontData()[0], i); + } + + update.done(); + } + } + +}
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/MessagesForExpressionVM.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/MessagesForExpressionVM.java new file mode 100644 index 00000000000..02014605b57 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/MessagesForExpressionVM.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 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 org.eclipse.osgi.util.NLS; + +public class MessagesForExpressionVM extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.cdt.dsf.debug.ui.viewmodel.expression.messages"; //$NON-NLS-1$ + + public static String ExpressionColumnPresentation_expression; + public static String ExpressionColumnPresentation_name; + public static String ExpressionColumnPresentation_type; + public static String ExpressionColumnPresentation_value; + public static String ExpressionColumnPresentation_address; + public static String ExpressionColumnPresentation_description; + + public static String ExpressionManagerLayoutNode__invalidExpression_nameColumn_label; + public static String ExpressionManagerLayoutNode__invalidExpression_valueColumn_label; + + public static String ExpressionManagerLayoutNode__newExpression_label; + + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, MessagesForExpressionVM.class); + } + + private MessagesForExpressionVM() { + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/VMExpressionUpdate.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/VMExpressionUpdate.java new file mode 100644 index 00000000000..8f64c623b28 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/VMExpressionUpdate.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2007, 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 org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.cdt.dsf.ui.viewmodel.VMViewerUpdate; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.model.IExpression; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; +import org.eclipse.jface.viewers.TreePath; + +/** + * + */ +@SuppressWarnings("restriction") +class VMExpressionUpdate extends VMViewerUpdate implements IExpressionUpdate { + + private final IExpression fExpression; + private Object fExpressionElement; + + public VMExpressionUpdate(IViewerUpdate clientUpdate, IExpression expression, DataRequestMonitor<Object> rm) + { + super(clientUpdate, rm); + fExpression = expression; + } + + public VMExpressionUpdate(IModelDelta delta, IPresentationContext presentationContext, IExpression expression, DataRequestMonitor<Object> rm) + { + super(delta, presentationContext, rm); + fExpression = expression; + } + + public VMExpressionUpdate(TreePath elementPath, Object viewerInput, IPresentationContext presentationContext, IExpression expression, DataRequestMonitor<Object> rm) + { + super(elementPath, viewerInput, presentationContext, rm); + fExpression = expression; + } + + + public IExpression getExpression() { + return fExpression; + } + + + public void setExpressionElement(Object element) { + fExpressionElement = element; + } + + @Override + public String toString() { + return "VMExpressionUpdate for elements under parent = " + getElement() + ", in for expression " + getExpression().getExpressionText(); //$NON-NLS-1$ //$NON-NLS-2$ + } + + @Override + public void done() { + @SuppressWarnings("unchecked") + + DataRequestMonitor<Object> rm = (DataRequestMonitor<Object>)getRequestMonitor(); + if (fExpressionElement != null) { + rm.setData(fExpressionElement); + } else if (rm.isSuccess()) { + rm.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Incomplete elements of updates", null)); //$NON-NLS-1$ + } + super.done(); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/WatchExpressionCellModifier.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/WatchExpressionCellModifier.java new file mode 100644 index 00000000000..8f4413bab8a --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/WatchExpressionCellModifier.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * 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 org.eclipse.cdt.dsf.concurrent.ThreadSafeAndProhibitedFromDsfExecutor; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.IDebugVMConstants; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.expression.ExpressionManagerVMNode.NewExpressionVMC; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.IExpressionManager; +import org.eclipse.debug.core.model.IWatchExpression; +import org.eclipse.jface.viewers.ICellModifier; + +/** + * + */ +@ThreadSafeAndProhibitedFromDsfExecutor("") +public class WatchExpressionCellModifier implements ICellModifier { + + /** + * Constructor for the modifier requires a valid DSF session in order to + * initialize the service tracker. + * @param session DSF session this modifier will use. + */ + public WatchExpressionCellModifier() { + } + + public boolean canModify(Object element, String property) { + return IDebugVMConstants.COLUMN_ID__EXPRESSION.equals(property) && + (getWatchExpression(element) != null || element instanceof NewExpressionVMC); + } + + public Object getValue(Object element, String property) { + if (!IDebugVMConstants.COLUMN_ID__EXPRESSION.equals(property)) return ""; //$NON-NLS-1$ + + IWatchExpression expression = getWatchExpression(element); + + if (expression != null) { + return expression.getExpressionText(); + } + return ""; //$NON-NLS-1$ + } + + public void modify(Object element, String property, Object value) { + if (!IDebugVMConstants.COLUMN_ID__EXPRESSION.equals(property)) return; + if (!(value instanceof String)) return; + + String origStrValue = (String) value; + String strValue = origStrValue.trim(); + IWatchExpression expression = getWatchExpression(element); + IExpressionManager expressionManager = DebugPlugin.getDefault().getExpressionManager(); + if (expression != null) { + if (strValue.length() != 0) { + expression.setExpressionText(origStrValue); + } else { + // (bug 233111) If user entered a blank string, remove the expression. + expressionManager.removeExpression(expression); + } + } else if (element instanceof NewExpressionVMC && strValue.length() != 0) { + IWatchExpression watchExpression = expressionManager.newWatchExpression(origStrValue); + expressionManager.addExpression(watchExpression); + } + } + + private IWatchExpression getWatchExpression(Object element) { + if (element instanceof IAdaptable) { + return (IWatchExpression)((IAdaptable)element).getAdapter(IWatchExpression.class); + } + return null; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/WatchExpressionDelegate.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/WatchExpressionDelegate.java new file mode 100644 index 00000000000..91fa6f1492c --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/WatchExpressionDelegate.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2007, 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 org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.model.IDebugElement; +import org.eclipse.debug.core.model.IValue; +import org.eclipse.debug.core.model.IWatchExpressionDelegate; +import org.eclipse.debug.core.model.IWatchExpressionListener; +import org.eclipse.debug.core.model.IWatchExpressionResult; + +/** + * + */ +public class WatchExpressionDelegate implements IWatchExpressionDelegate { + public void evaluateExpression(final String expression, IDebugElement context, IWatchExpressionListener listener) { + listener.watchEvaluationFinished(new IWatchExpressionResult() { + public String[] getErrorMessages() { return new String[0]; } + public DebugException getException() { return null; } + public String getExpressionText() { return expression; } + public IValue getValue() { return null; } + public boolean hasErrors() { return false; } + }); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/messages.properties b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/messages.properties new file mode 100644 index 00000000000..9102d757a58 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/expression/messages.properties @@ -0,0 +1,21 @@ +############################################################################### +# Copyright (c) 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 +# Wind River Systems - added Address +############################################################################### + +ExpressionColumnPresentation_expression=Expression +ExpressionColumnPresentation_name=Name +ExpressionColumnPresentation_type=Type +ExpressionColumnPresentation_value=Value +ExpressionColumnPresentation_address=Address +ExpressionColumnPresentation_description=Description +ExpressionManagerLayoutNode__invalidExpression_nameColumn_label=Invalid expression +ExpressionManagerLayoutNode__invalidExpression_valueColumn_label=Invalid expression +ExpressionManagerLayoutNode__newExpression_label=Add new expression diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/AbstractContainerVMNode.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/AbstractContainerVMNode.java new file mode 100644 index 00000000000..26cfc2d5468 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/AbstractContainerVMNode.java @@ -0,0 +1,191 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Anton Leherbauer (Wind River Systems) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.ui.viewmodel.launch; + +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +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.datamodel.IDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerResumedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerSuspendedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExitedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IStartedDMEvent; +import org.eclipse.cdt.dsf.debug.service.StepQueueManager.ISteppingTimedOutEvent; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.SteppingController.SteppingTimedOutEvent; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMContext; +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.AbstractDMVMProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; + +/** + * Abstract implementation of a container view model node. + * Clients need to implement {@link #updateLabelInSessionThread(ILabelUpdate[])}. + * + * @since 1.1 + */ +@SuppressWarnings("restriction") +public abstract class AbstractContainerVMNode extends AbstractDMVMNode implements IElementLabelProvider { + + public AbstractContainerVMNode(AbstractDMVMProvider provider, DsfSession session) { + super(provider, session, IRunControl.IContainerDMContext.class); + } + + public void update(final ILabelUpdate[] updates) { + try { + getSession().getExecutor().execute(new DsfRunnable() { + public void run() { + for (final ILabelUpdate update : updates) { + updateLabelInSessionThread(update); + } + }}); + } catch (RejectedExecutionException e) { + for (ILabelUpdate update : updates) { + handleFailedUpdate(update); + } + } + } + + /** + * Perform the given label updates in the session executor thread. + * + * @param updates the pending label updates + * @see {@link #update(ILabelUpdate[]) + */ + protected abstract void updateLabelInSessionThread(ILabelUpdate update); + + @Override + public void getContextsForEvent(VMDelta parentDelta, Object e, final DataRequestMonitor<IVMContext[]> rm) { + super.getContextsForEvent(parentDelta, e, rm); + } + + public int getDeltaFlags(Object e) { + IDMContext dmc = e instanceof IDMEvent<?> ? ((IDMEvent<?>)e).getDMContext() : null; + + if (e instanceof IContainerResumedDMEvent) { + if (((IContainerResumedDMEvent)e).getReason() != IRunControl.StateChangeReason.STEP) + { + return IModelDelta.CONTENT; + } + } else if (e instanceof IContainerSuspendedDMEvent) { + return IModelDelta.NO_CHANGE; + } else if (e instanceof FullStackRefreshEvent) { + if (dmc instanceof IContainerDMContext) { + return IModelDelta.CONTENT; + } + } else if (e instanceof SteppingTimedOutEvent) { + if (dmc instanceof IContainerDMContext) + { + return IModelDelta.CONTENT; + } + } else if (e instanceof ISteppingTimedOutEvent) { + if (dmc instanceof IContainerDMContext) + { + return IModelDelta.CONTENT; + } + } else if (e instanceof IExitedDMEvent) { + return IModelDelta.CONTENT; + } else if (e instanceof IStartedDMEvent) { + if (dmc instanceof IContainerDMContext) { + return IModelDelta.EXPAND | IModelDelta.SELECT; + } else { + return IModelDelta.CONTENT; + } + } + return IModelDelta.NO_CHANGE; + } + + public void buildDelta(Object e, final VMDelta parentDelta, final int nodeOffset, final RequestMonitor requestMonitor) { + IDMContext dmc = e instanceof IDMEvent<?> ? ((IDMEvent<?>)e).getDMContext() : null; + + if(e instanceof IContainerResumedDMEvent) { + // Container resumed: + // - If not stepping, update the container and the execution + // contexts under it. + // - If stepping, do nothing to avoid too many updates. If a + // time-out is reached before the step completes, the + // ISteppingTimedOutEvent will trigger a full refresh. + if (((IContainerResumedDMEvent)e).getReason() != IRunControl.StateChangeReason.STEP) + { + parentDelta.addNode(createVMContext(((IDMEvent<?>)e).getDMContext()), IModelDelta.CONTENT); + } + } else if (e instanceof IContainerSuspendedDMEvent) { + // Container suspended. Do nothing here to give the stack the + // priority in updating. The container and threads will update as + // a result of FullStackRefreshEvent. + } else if (e instanceof FullStackRefreshEvent) { + // Full-stack refresh event is generated following a suspended event + // and a fixed delay. If the suspended event was generated for the + // container refresh the whole container. + if (dmc instanceof IContainerDMContext) { + parentDelta.addNode(createVMContext(dmc), IModelDelta.CONTENT); + } + } else if (e instanceof SteppingTimedOutEvent) { + // Stepping time-out indicates that a step operation is taking + // a long time, and the view needs to be refreshed to show + // the user that the program is running. + // If the step was issued for the whole container refresh + // the whole container. + if (dmc instanceof IContainerDMContext) { + parentDelta.addNode(createVMContext(dmc), IModelDelta.CONTENT); + } + } else if (e instanceof ISteppingTimedOutEvent) { + // Stepping time-out indicates that a step operation is taking + // a long time, and the view needs to be refreshed to show + // the user that the program is running. + // If the step was issued for the whole container refresh + // the whole container. + if (dmc instanceof IContainerDMContext) { + parentDelta.addNode(createVMContext(dmc), IModelDelta.CONTENT); + } + } else if (e instanceof IExitedDMEvent) { + // An exited event could either be for a thread within a container + // or for the container itself. + // If a container exited, refresh the parent element so that the + // container may be removed. + // If a thread exited within a container, refresh that container. + if (dmc instanceof IContainerDMContext) { + parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); + } else { + IContainerDMContext containerCtx = DMContexts.getAncestorOfType(dmc, IContainerDMContext.class); + if (containerCtx != null) { + parentDelta.addNode(createVMContext(containerCtx), IModelDelta.CONTENT); + } + } + } else if (e instanceof IStartedDMEvent) { + // A started event could either be for a thread within a container + // or for the container itself. + // If a container started, issue an expand and select event to + // show the threads in the new container. + // Note: the EXPAND flag implies refreshing the parent element. + if (dmc instanceof IContainerDMContext) { + parentDelta.addNode(createVMContext(dmc), IModelDelta.EXPAND | IModelDelta.SELECT); + } else { + IContainerDMContext containerCtx = DMContexts.getAncestorOfType(dmc, IContainerDMContext.class); + if (containerCtx != null) { + parentDelta.addNode(createVMContext(containerCtx), IModelDelta.CONTENT); + } + } + } + + requestMonitor.done(); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/AbstractLaunchVMProvider.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/AbstractLaunchVMProvider.java new file mode 100644 index 00000000000..28de36ba1d8 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/AbstractLaunchVMProvider.java @@ -0,0 +1,294 @@ +/******************************************************************************* + * 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 + * Ericsson - Modified for new functionality + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.ui.viewmodel.launch; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExitedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IStartedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent; +import org.eclipse.cdt.dsf.debug.ui.IDsfDebugUIConstants; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.launch.LaunchRootVMNode.LaunchesEvent; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.launch.StackFramesVMNode.IncompleteStackVMContext; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMAdapter; +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.datamodel.AbstractDMVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.update.AutomaticUpdatePolicy; +import org.eclipse.cdt.dsf.ui.viewmodel.update.IVMUpdatePolicy; +import org.eclipse.cdt.dsf.ui.viewmodel.update.ManualUpdatePolicy; +import org.eclipse.debug.core.DebugEvent; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.IDebugEventSetListener; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchesListener2; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; + + +/** + * @since 1.1 + */ +@SuppressWarnings("restriction") +public class AbstractLaunchVMProvider extends AbstractDMVMProvider + implements IDebugEventSetListener, ILaunchesListener2 +{ + /** + * Delay (in milliseconds) before a full stack trace will be requested. + */ + private static final int FRAME_UPDATE_DELAY= 200; + + private final Map<IExecutionDMContext,ScheduledFuture<?>> fRefreshStackFramesFutures = new HashMap<IExecutionDMContext,ScheduledFuture<?>>(); + + private IPropertyChangeListener fPreferencesListener; + + @ThreadSafe + public AbstractLaunchVMProvider(AbstractVMAdapter adapter, IPresentationContext presentationContext, DsfSession session) + { + super(adapter, presentationContext, session); + + final IPreferenceStore store= DsfUIPlugin.getDefault().getPreferenceStore(); + if (store.getBoolean(IDsfDebugUIConstants.PREF_STACK_FRAME_LIMIT_ENABLE)) { + getPresentationContext().setProperty(IDsfDebugUIConstants.PREF_STACK_FRAME_LIMIT, store.getInt(IDsfDebugUIConstants.PREF_STACK_FRAME_LIMIT)); + } + + fPreferencesListener = new IPropertyChangeListener() { + public void propertyChange(final PropertyChangeEvent event) { + handlePropertyChanged(store, event); + }}; + store.addPropertyChangeListener(fPreferencesListener); + } + + @Override + protected IVMUpdatePolicy[] createUpdateModes() { + return new IVMUpdatePolicy[] { + new DelayedStackRefreshUpdatePolicy(new AutomaticUpdatePolicy()), + new DelayedStackRefreshUpdatePolicy(new ManualUpdatePolicy()) + }; + } + + public void handleDebugEvents(final DebugEvent[] events) { + if (isDisposed()) return; + + // We're in session's executor thread. Re-dispach to VM Adapter + // executor thread and then call root layout node. + try { + getExecutor().execute(new Runnable() { + public void run() { + if (isDisposed()) return; + + for (final DebugEvent event : events) { + handleEvent(event); + } + }}); + } catch (RejectedExecutionException e) { + // Ignore. This exception could be thrown if the provider is being + // shut down. + } + } + + @Override + public void handleEvent(Object event, final RequestMonitor rm) { + if (event instanceof DoubleClickEvent && !isDisposed()) { + final ISelection selection= ((DoubleClickEvent) event).getSelection(); + if (selection instanceof IStructuredSelection) { + Object element= ((IStructuredSelection) selection).getFirstElement(); + if (element instanceof IncompleteStackVMContext) { + IncompleteStackVMContext incStackVmc = ((IncompleteStackVMContext) element); + IVMNode node = incStackVmc.getVMNode(); + if (node instanceof StackFramesVMNode && node.getVMProvider() == this) { + IExecutionDMContext exeCtx= incStackVmc.getExecutionDMContext(); + ((StackFramesVMNode) node).incrementStackFrameLimit(exeCtx); + // replace double click event with expand stack event + final ExpandStackEvent expandStackEvent = new ExpandStackEvent(exeCtx); + getExecutor().execute(new DsfRunnable() { + public void run() { + handleEvent(expandStackEvent, null); + } + }); + } + } + } + if (rm != null) { + rm.done(); + } + return; + } + super.handleEvent(event, rm); + } + + @Override + protected void handleEvent(IVMModelProxy proxyStrategy, final Object event, RequestMonitor rm) { + super.handleEvent(proxyStrategy, event, rm); + + if (event instanceof IRunControl.ISuspendedDMEvent) { + final IExecutionDMContext exeContext= ((IRunControl.ISuspendedDMEvent) event).getDMContext(); + ScheduledFuture<?> refreshStackFramesFuture = getRefreshFuture(exeContext); + // trigger delayed full stack frame update + if (refreshStackFramesFuture != null) { + // cancel previously scheduled frame update + refreshStackFramesFuture.cancel(false); + } + + refreshStackFramesFuture = getSession().getExecutor().schedule( + new DsfRunnable() { + public void run() { + if (getSession().isActive()) { + getExecutor().execute(new Runnable() { + public void run() { + // trigger full stack frame update + ScheduledFuture<?> future= fRefreshStackFramesFutures.get(exeContext); + if (future != null && !isDisposed()) { + fRefreshStackFramesFutures.remove(exeContext); + handleEvent(new FullStackRefreshEvent(exeContext), null); + } + }}); + } + } + }, + FRAME_UPDATE_DELAY, TimeUnit.MILLISECONDS); + fRefreshStackFramesFutures.put(exeContext, refreshStackFramesFuture); + } else if (event instanceof IRunControl.IResumedDMEvent) { + IExecutionDMContext exeContext= ((IRunControl.IResumedDMEvent) event).getDMContext(); + ScheduledFuture<?> refreshStackFramesFuture= fRefreshStackFramesFutures.get(exeContext); + if (refreshStackFramesFuture != null) { + // cancel previously scheduled frame update + refreshStackFramesFuture.cancel(false); + fRefreshStackFramesFutures.remove(exeContext); + } + } + + } + + /** + * Returns the future for the given execution context or for any child of the + * given execution context. + */ + private ScheduledFuture<?> getRefreshFuture(IExecutionDMContext execCtx) { + for (IExecutionDMContext refreshCtx : fRefreshStackFramesFutures.keySet()) { + if (refreshCtx.equals(execCtx) || DMContexts.isAncestorOf(refreshCtx, execCtx)) { + return fRefreshStackFramesFutures.remove(refreshCtx); + } + } + return null; + } + + @Override + public void dispose() { + DebugPlugin.getDefault().removeDebugEventListener(this); + DebugPlugin.getDefault().getLaunchManager().removeLaunchListener(this); + + final IPreferenceStore store= DsfUIPlugin.getDefault().getPreferenceStore(); + store.removePropertyChangeListener(fPreferencesListener); + + super.dispose(); + } + + public void launchesAdded(ILaunch[] launches) { + handleLaunchesEvent(new LaunchesEvent(launches, LaunchesEvent.Type.ADDED)); + } + + public void launchesRemoved(ILaunch[] launches) { + handleLaunchesEvent(new LaunchesEvent(launches, LaunchesEvent.Type.REMOVED)); + } + + public void launchesChanged(ILaunch[] launches) { + handleLaunchesEvent(new LaunchesEvent(launches, LaunchesEvent.Type.CHANGED)); + } + + public void launchesTerminated(ILaunch[] launches) { + handleLaunchesEvent(new LaunchesEvent(launches, LaunchesEvent.Type.TERMINATED)); + } + + private void handleLaunchesEvent(final LaunchesEvent event) { + if (isDisposed()) return; + + // We're in session's executor thread. Re-dispach to VM Adapter + // executor thread and then call root layout node. + try { + getExecutor().execute(new Runnable() { + public void run() { + if (isDisposed()) return; + + IRootVMNode rootLayoutNode = getRootVMNode(); + if (rootLayoutNode != null && rootLayoutNode.getDeltaFlags(event) != 0) { + handleEvent(event); + } + }}); + } catch (RejectedExecutionException e) { + // Ignore. This exception could be thrown if the provider is being + // shut down. + } + } + + @Override + protected boolean canSkipHandlingEvent(Object newEvent, Object eventToSkip) { + // To optimize view performance when stepping rapidly, skip events that came + // before the last suspended events. However, the debug view can get suspended + // events for different threads, so make sure to skip only the events if they + // were in the same hierarchy as the last suspended event. + // Note: Avoid skipping thread started/exited events which require a larger + // scope refresh than some suspended events. + if (newEvent instanceof IStartedDMEvent || newEvent instanceof IExitedDMEvent) { + return false; + } + + if (newEvent instanceof ISuspendedDMEvent && eventToSkip instanceof IDMEvent<?>) { + IDMContext newEventDmc = ((IDMEvent<?>)newEvent).getDMContext(); + IDMContext eventToSkipDmc = ((IDMEvent<?>)eventToSkip).getDMContext(); + + if (newEventDmc.equals(eventToSkipDmc) || DMContexts.isAncestorOf(eventToSkipDmc, newEventDmc)) { + return true; + } + } + + return false; + } + + protected void handlePropertyChanged(final IPreferenceStore store, final PropertyChangeEvent event) { + String property = event.getProperty(); + if (IDsfDebugUIConstants.PREF_STACK_FRAME_LIMIT_ENABLE.equals(property) + || IDsfDebugUIConstants.PREF_STACK_FRAME_LIMIT.equals(property)) { + if (store.getBoolean(IDsfDebugUIConstants.PREF_STACK_FRAME_LIMIT_ENABLE)) { + getPresentationContext().setProperty(IDsfDebugUIConstants.PREF_STACK_FRAME_LIMIT, store.getInt(IDsfDebugUIConstants.PREF_STACK_FRAME_LIMIT)); + } else { + getPresentationContext().setProperty(IDsfDebugUIConstants.PREF_STACK_FRAME_LIMIT, null); + } + getExecutor().execute(new DsfRunnable() { + public void run() { + handleEvent(event); + } + }); + } + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/AbstractThreadVMNode.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/AbstractThreadVMNode.java new file mode 100644 index 00000000000..51f28dc57f9 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/AbstractThreadVMNode.java @@ -0,0 +1,328 @@ +/******************************************************************************* + * 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 + * Ericsson - Modified for multi threaded functionality + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.ui.viewmodel.launch; + +import java.util.List; +import java.util.concurrent.RejectedExecutionException; + +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.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerResumedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerSuspendedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IResumedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent; +import org.eclipse.cdt.dsf.debug.service.StepQueueManager.ISteppingTimedOutEvent; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.SteppingController.SteppingTimedOutEvent; +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.IVMContext; +import org.eclipse.cdt.dsf.ui.viewmodel.ModelProxyInstalledEvent; +import org.eclipse.cdt.dsf.ui.viewmodel.VMChildrenUpdate; +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.AbstractDMVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +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.ILabelUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; + + +/** + * Abstract implementation of a thread view model node. + * Clients need to implement {@link #updateLabelInSessionThread(ILabelUpdate[])}. + * + * @since 1.1 + */ +@SuppressWarnings("restriction") +public abstract class AbstractThreadVMNode extends AbstractDMVMNode + implements IElementLabelProvider +{ + public AbstractThreadVMNode(AbstractDMVMProvider provider, DsfSession session) { + super(provider, session, IExecutionDMContext.class); + } + + @Override + protected void updateElementsInSessionThread(final IChildrenUpdate update) { + IRunControl runControl = getServicesTracker().getService(IRunControl.class); + final IContainerDMContext contDmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), IContainerDMContext.class); + if (runControl == null || contDmc == null) { + handleFailedUpdate(update); + return; + } + + runControl.getExecutionContexts(contDmc, + new ViewerDataRequestMonitor<IExecutionDMContext[]>(getSession().getExecutor(), update){ + @Override + public void handleCompleted() { + if (!isSuccess()) { + handleFailedUpdate(update); + return; + } + fillUpdateWithVMCs(update, getData()); + update.done(); + } + }); + } + + + public void update(final ILabelUpdate[] updates) { + try { + getSession().getExecutor().execute(new DsfRunnable() { + public void run() { + updateLabelInSessionThread(updates); + }}); + } catch (RejectedExecutionException e) { + for (ILabelUpdate update : updates) { + handleFailedUpdate(update); + } + } + } + + @Override + public void getContextsForEvent(VMDelta parentDelta, Object e, final DataRequestMonitor<IVMContext[]> rm) { + if(e instanceof IContainerResumedDMEvent) { + IExecutionDMContext[] triggerContexts = ((IContainerResumedDMEvent)e).getTriggeringContexts(); + if (triggerContexts.length != 0) { + rm.setData(new IVMContext[] { createVMContext(triggerContexts[0]) }); + rm.done(); + return; + } + } else if(e instanceof IContainerSuspendedDMEvent) { + IExecutionDMContext[] triggerContexts = ((IContainerSuspendedDMEvent)e).getTriggeringContexts(); + if (triggerContexts.length != 0) { + rm.setData(new IVMContext[] { createVMContext(triggerContexts[0]) }); + rm.done(); + return; + } + } else if (e instanceof SteppingTimedOutEvent && + ((SteppingTimedOutEvent)e).getDMContext() instanceof IContainerDMContext) + { + // The timed out event occured on a container and not on a thread. Do not + // return a context for this event, which will force the view model to generate + // a delta for all the threads. + rm.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED, "", null)); //$NON-NLS-1$ + rm.done(); + return; + } else if (e instanceof ISteppingTimedOutEvent && + ((ISteppingTimedOutEvent)e).getDMContext() instanceof IContainerDMContext) + { + // The timed out event occured on a container and not on a thread. Do not + // return a context for this event, which will force the view model to generate + // a delta for all the threads. + rm.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED, "", null)); //$NON-NLS-1$ + rm.done(); + return; + } else if (e instanceof FullStackRefreshEvent && + ((FullStackRefreshEvent)e).getDMContext() instanceof IContainerDMContext) + { + // The step sequence end event occured on a container and not on a thread. Do not + // return a context for this event, which will force the view model to generate + // a delta for all the threads. + rm.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED, "", null)); //$NON-NLS-1$ + rm.done(); + return; + } else if (e instanceof ModelProxyInstalledEvent) { + getThreadVMCForModelProxyInstallEvent( + parentDelta, + new DataRequestMonitor<VMContextInfo>(getExecutor(), rm) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + rm.setData(new IVMContext[] { getData().fVMContext }); + } else { + rm.setData(new IVMContext[0]); + } + rm.done(); + } + }); + return; + } + super.getContextsForEvent(parentDelta, e, rm); + } + + private static class VMContextInfo { + final IVMContext fVMContext; + final int fIndex; + final boolean fIsSuspended; + VMContextInfo(IVMContext vmContext, int index, boolean isSuspended) { + fVMContext = vmContext; + fIndex = index; + fIsSuspended = isSuspended; + } + } + + private void getThreadVMCForModelProxyInstallEvent(VMDelta parentDelta, final DataRequestMonitor<VMContextInfo> rm) { + getVMProvider().updateNode(this, new VMChildrenUpdate( + parentDelta, getVMProvider().getPresentationContext(), -1, -1, + new DataRequestMonitor<List<Object>>(getExecutor(), rm) { + @Override + protected void handleSuccess() { + try { + getSession().getExecutor().execute(new DsfRunnable() { + public void run() { + final IRunControl runControl = getServicesTracker().getService(IRunControl.class); + if (runControl != null) { + int vmcIdx = -1; + int suspendedVmcIdx = -1; + + for (int i = 0; i < getData().size(); i++) { + if (getData().get(i) instanceof IDMVMContext) { + IDMVMContext vmc = (IDMVMContext)getData().get(i); + IExecutionDMContext execDmc = DMContexts.getAncestorOfType( + vmc.getDMContext(), IExecutionDMContext.class); + if (execDmc != null) { + vmcIdx = vmcIdx < 0 ? i : vmcIdx; + if (runControl.isSuspended(execDmc)) { + suspendedVmcIdx = suspendedVmcIdx < 0 ? i : suspendedVmcIdx; + } + } + } + } + if (suspendedVmcIdx >= 0) { + rm.setData(new VMContextInfo( + (IVMContext)getData().get(suspendedVmcIdx), suspendedVmcIdx, true)); + } else if (vmcIdx >= 0) { + rm.setData(new VMContextInfo((IVMContext)getData().get(vmcIdx), vmcIdx, false)); + } else { + rm.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "No threads available", null)); //$NON-NLS-1$ + } + rm.done(); + } else { + rm.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "No threads available", null)); //$NON-NLS-1$ + rm.done(); + } + } + }); + } catch (RejectedExecutionException e) { + rm.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED, "", null)); //$NON-NLS-1$ + rm.done(); + } + } + })); + } + + /** + * Perform the given label updates in the session executor thread. + * + * @param updates the pending label updates + * @see {@link #update(ILabelUpdate[]) + */ + protected abstract void updateLabelInSessionThread(ILabelUpdate[] updates); + + + public int getDeltaFlags(Object e) { + IDMContext dmc = e instanceof IDMEvent<?> ? ((IDMEvent<?>)e).getDMContext() : null; + + if (dmc instanceof IContainerDMContext) { + return IModelDelta.NO_CHANGE; + } else if (e instanceof IResumedDMEvent && + ((IResumedDMEvent)e).getReason() != IRunControl.StateChangeReason.STEP) + { + return IModelDelta.CONTENT; + } else if (e instanceof ISuspendedDMEvent) { + return IModelDelta.NO_CHANGE; + } else if (e instanceof FullStackRefreshEvent) { + return IModelDelta.CONTENT; + } else if (e instanceof SteppingTimedOutEvent) { + return IModelDelta.CONTENT; + } else if (e instanceof ISteppingTimedOutEvent) { + return IModelDelta.CONTENT; + } else if (e instanceof ModelProxyInstalledEvent) { + return IModelDelta.SELECT | IModelDelta.EXPAND; + } + return IModelDelta.NO_CHANGE; + } + + public void buildDelta(Object e, final VMDelta parentDelta, final int nodeOffset, final RequestMonitor rm) { + IDMContext dmc = e instanceof IDMEvent<?> ? ((IDMEvent<?>)e).getDMContext() : null; + + if(dmc instanceof IContainerDMContext) { + // The IContainerDMContext sub-classes IExecutionDMContext. + // Also IContainerResumedDMEvent sub-classes IResumedDMEvent and + // IContainerSuspendedDMEvnet sub-classes ISuspendedEvent. + // Because of this relationship, the thread VM node can be called + // with data-model evnets for the containers. This statement + // filters out those event. + rm.done(); + } else if(e instanceof IResumedDMEvent) { + // Resumed: + // - If not stepping, update the thread and its content (its stack). + // - If stepping, do nothing to avoid too many updates. If a + // time-out is reached before the step completes, the + // ISteppingTimedOutEvent will trigger a refresh. + if (((IResumedDMEvent)e).getReason() != IRunControl.StateChangeReason.STEP) { + parentDelta.addNode(createVMContext(dmc), IModelDelta.CONTENT); + } + rm.done(); + } else if (e instanceof ISuspendedDMEvent) { + // Container suspended. Do nothing here to give the stack the + // priority in updating. The thread will update as a result of + // FullStackRefreshEvent. + rm.done(); + } else if (e instanceof FullStackRefreshEvent) { + // Full-stack refresh event is generated following a suspended event + // and a fixed delay. Refresh the whole thread upon this event. + parentDelta.addNode(createVMContext(dmc), IModelDelta.CONTENT); + rm.done(); + } else if (e instanceof SteppingTimedOutEvent) { + // Stepping time-out indicates that a step operation is taking + // a long time, and the view needs to be refreshed to show + // the user that the program is running. + parentDelta.addNode(createVMContext(dmc), IModelDelta.CONTENT); + rm.done(); + } else if (e instanceof ISteppingTimedOutEvent) { + // Stepping time-out indicates that a step operation is taking + // a long time, and the view needs to be refreshed to show + // the user that the program is running. + parentDelta.addNode(createVMContext(dmc), IModelDelta.CONTENT); + rm.done(); + } else if (e instanceof ModelProxyInstalledEvent) { + // Model Proxy install event is generated when the model is first + // populated into the view. This happens when a new debug session + // is started or when the view is first opened. + // In both cases, if there are already threads in the debug model, + // the desired user behavior is to show the threads and to select + // the first thread. + // If the thread is suspended, do not select the thread, instead, + // its top stack frame will be selected. + getThreadVMCForModelProxyInstallEvent( + parentDelta, + new DataRequestMonitor<VMContextInfo>(getExecutor(), rm) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + parentDelta.addNode( + getData().fVMContext, nodeOffset + getData().fIndex, + IModelDelta.EXPAND | (getData().fIsSuspended ? 0 : IModelDelta.SELECT)); + } + rm.done(); + } + }); + } else { + + rm.done(); + } + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/DefaultDsfModelSelectionPolicyFactory.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/DefaultDsfModelSelectionPolicyFactory.java new file mode 100644 index 00000000000..7a05904d4a4 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/DefaultDsfModelSelectionPolicyFactory.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Anton Leherbauer (Wind River Systems) - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.debug.ui.viewmodel.launch; + +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicy; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicyFactory; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.ui.IDebugUIConstants; + +/** + * Default model selection policy factory for DSF. + * @since 1.1 + */ +@SuppressWarnings("restriction") +public class DefaultDsfModelSelectionPolicyFactory implements IModelSelectionPolicyFactory { + + /* + * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicyFactory#createModelSelectionPolicyAdapter(java.lang.Object, org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext) + */ + public IModelSelectionPolicy createModelSelectionPolicyAdapter(Object element, IPresentationContext context) { + if (IDebugUIConstants.ID_DEBUG_VIEW.equals(context.getId())) { + if (element instanceof IDMVMContext) { + IDMVMContext dmvmContext= (IDMVMContext) element; + IDMContext dmContext= dmvmContext.getDMContext(); + if (dmContext != null) { + return new DefaultDsfSelectionPolicy(dmContext); + } + } + } + return null; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/DefaultDsfSelectionPolicy.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/DefaultDsfSelectionPolicy.java new file mode 100644 index 00000000000..cb1e8faffbc --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/DefaultDsfSelectionPolicy.java @@ -0,0 +1,154 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Anton Leherbauer (Wind River Systems) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.ui.viewmodel.launch; + +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicy; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.ITreeSelection; +import org.eclipse.jface.viewers.TreePath; +import org.eclipse.jface.viewers.TreeSelection; + +/** + * Default DSF selection policy implementation modelled after platform version + * (<code>DefaultSelectionPolicy</code>). + * @since 1.1 + */ +@SuppressWarnings("restriction") +public class DefaultDsfSelectionPolicy implements IModelSelectionPolicy { + + private IDMContext fDMContext; + + /** + * Create selection policy instance for the given data model context. + * + * @param dmContext + */ + public DefaultDsfSelectionPolicy(IDMContext dmContext) { + fDMContext= dmContext; + } + + /* + * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicy#contains(org.eclipse.jface.viewers.ISelection, org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext) + */ + public boolean contains(ISelection selection, IPresentationContext context) { + if (IDebugUIConstants.ID_DEBUG_VIEW.equals(context.getId())) { + if (selection instanceof IStructuredSelection) { + IStructuredSelection ss= (IStructuredSelection) selection; + Object element= ss.getFirstElement(); + if (element instanceof IDMVMContext) { + IDMVMContext dmvmContext= (IDMVMContext) element; + IDMContext dmContext= dmvmContext.getDMContext(); + if (dmContext != null) { + return fDMContext.getSessionId().equals(dmContext.getSessionId()); + } + } + } + } + return false; + } + + /* + * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicy#isSticky(org.eclipse.jface.viewers.ISelection, org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext) + */ + public boolean isSticky(ISelection selection, IPresentationContext context) { + if (IDebugUIConstants.ID_DEBUG_VIEW.equals(context.getId())) { + if (selection instanceof IStructuredSelection) { + IStructuredSelection ss= (IStructuredSelection) selection; + Object element= ss.getFirstElement(); + return isSticky(element); + } + } + return false; + } + + protected boolean isSticky(Object element) { + if (element instanceof IDMVMContext) { + IDMVMContext dmvmContext= (IDMVMContext) element; + IDMContext dmContext= dmvmContext.getDMContext(); + if (dmContext instanceof IFrameDMContext) { + IExecutionDMContext execContext= DMContexts.getAncestorOfType(dmContext, IExecutionDMContext.class); + if (execContext != null) { + DsfServicesTracker servicesTracker = new DsfServicesTracker(DsfUIPlugin.getBundleContext(), dmContext.getSessionId()); + try { + IRunControl runControl= servicesTracker.getService(IRunControl.class); + if (runControl != null) { + if (runControl.isSuspended(execContext)) { + return true; + } + } + } finally { + servicesTracker.dispose(); + } + } + } + } + return false; + } + + /* + * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicy#overrides(org.eclipse.jface.viewers.ISelection, org.eclipse.jface.viewers.ISelection, org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext) + */ + public boolean overrides(ISelection existing, ISelection candidate, IPresentationContext context) { + if (IDebugUIConstants.ID_DEBUG_VIEW.equals(context.getId())) { + if (existing instanceof IStructuredSelection && candidate instanceof IStructuredSelection) { + IStructuredSelection ssExisting = (IStructuredSelection) existing; + IStructuredSelection ssCandidate = (IStructuredSelection) candidate; + return overrides(ssExisting.getFirstElement(), ssCandidate.getFirstElement()); + } + } + return true; + } + + + protected boolean overrides(Object existing, Object candidate) { + if (existing == null || existing.equals(candidate)) { + return true; + } + if (existing instanceof IDMVMContext && candidate instanceof IDMVMContext) { + IDMContext curr = ((IDMVMContext) existing).getDMContext(); + IDMContext cand = ((IDMVMContext) candidate).getDMContext(); + if (curr instanceof IFrameDMContext && cand instanceof IFrameDMContext) { + IExecutionDMContext currExecContext= DMContexts.getAncestorOfType(curr, IExecutionDMContext.class); + if (currExecContext != null) { + IExecutionDMContext candExecContext= DMContexts.getAncestorOfType(cand, IExecutionDMContext.class); + return currExecContext.equals(candExecContext) || !isSticky(existing); + } + } + } + return !isSticky(existing); + } + + /* + * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicy#replaceInvalidSelection(org.eclipse.jface.viewers.ISelection, org.eclipse.jface.viewers.ISelection) + */ + public ISelection replaceInvalidSelection(ISelection invalidSelection, ISelection newSelection) { + if (invalidSelection instanceof ITreeSelection) { + ITreeSelection treeSelection = (ITreeSelection)invalidSelection; + if (treeSelection.getPaths().length == 1) { + TreePath path = treeSelection.getPaths()[0]; + return new TreeSelection(path.getParentPath()); + } + } + return newSelection; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/DelayedStackRefreshUpdatePolicy.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/DelayedStackRefreshUpdatePolicy.java new file mode 100644 index 00000000000..8cdfc04d8d7 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/DelayedStackRefreshUpdatePolicy.java @@ -0,0 +1,175 @@ +/******************************************************************************* + * Copyright (c) 2008 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.launch; + +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExitedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; +import org.eclipse.cdt.dsf.ui.viewmodel.update.IElementUpdateTester; +import org.eclipse.cdt.dsf.ui.viewmodel.update.IVMUpdatePolicy; +import org.eclipse.cdt.dsf.ui.viewmodel.update.UpdatePolicyDecorator; +import org.eclipse.jface.viewers.TreePath; + +/** + * An update strategy decorator specialized for delayed stack frame refresh. The + * strategy flushes only the cached top stack frame in case of an normal {@link ISuspendedDMEvent}, + * while in in case of a special {@link FullStackRefreshEvent} everything is invalidated. + * + * <p> + * The underlying base update policy is considered for container contexts only. + * In other cases the cache data is always flushed. + * </p> + * + * @since 1.1 + */ +public class DelayedStackRefreshUpdatePolicy extends UpdatePolicyDecorator { + + private static final class DelayedStackRefreshUpdateTester implements IElementUpdateTester { + + private final IElementUpdateTester fBaseTester; + + /** Indicates whether only the top stack frame should be updated */ + private final boolean fLazyStackFrameMode; + + DelayedStackRefreshUpdateTester(IElementUpdateTester baseTester, boolean lazyStackFrameMode) { + fBaseTester = baseTester; + fLazyStackFrameMode = lazyStackFrameMode; + } + public int getUpdateFlags(Object viewerInput, TreePath path) { + Object element = path.getSegmentCount() != 0 ? path.getLastSegment() : viewerInput; + if (element instanceof IDMVMContext) { + IDMContext dmc = ((IDMVMContext) element).getDMContext(); + if (fLazyStackFrameMode) { + if (dmc instanceof IFrameDMContext) { + if (((IFrameDMContext) dmc).getLevel() == 0) { + return FLUSH; + } + } else if (dmc instanceof IExecutionDMContext) { + return fBaseTester.getUpdateFlags(viewerInput, path); + } + return DIRTY; + } else if (dmc instanceof IContainerDMContext) { + return fBaseTester.getUpdateFlags(viewerInput, path); + } + } + return FLUSH; + } + + public boolean includes(IElementUpdateTester tester) { + // A non-lazy tester includes a lazy tester, but not vice versa. + // This allows entries that were marked as dirty by a flush with + // the lazy mode to be superseded by a non-lazy update which + // actually clears the entries that were marked as dirty. + if (tester instanceof DelayedStackRefreshUpdateTester) { + DelayedStackRefreshUpdateTester sfTester = (DelayedStackRefreshUpdateTester)tester; + if (fLazyStackFrameMode) { + if (sfTester.fLazyStackFrameMode) { + return fBaseTester.includes(sfTester.fBaseTester); + } + } else { + if (!sfTester.fLazyStackFrameMode) { + return fBaseTester.includes(sfTester.fBaseTester); + } + // non-lazy includes lazy + return true; + } + } + return false; + } + + @Override + public String toString() { + return "Delayed stack refresh (lazy = " + fLazyStackFrameMode + ", base = " + fBaseTester + ") update tester"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + } + + private static final class ThreadsUpdateTester implements IElementUpdateTester { + + private final IElementUpdateTester fBaseTester; + + private final boolean fRefreshAll; + + ThreadsUpdateTester(IElementUpdateTester baseTester, boolean refreshAll) { + fBaseTester = baseTester; + fRefreshAll = refreshAll; + } + + public int getUpdateFlags(Object viewerInput, TreePath path) { + Object element = path.getSegmentCount() != 0 ? path.getLastSegment() : viewerInput; + + if (!fRefreshAll && element instanceof IDMVMContext) { + IDMContext dmc = ((IDMVMContext) element).getDMContext(); + if (dmc instanceof IContainerDMContext) { + return fBaseTester.getUpdateFlags(viewerInput, path); + } + } + + // If the element is not a container or if the flush all flag is set, + // always flush it. + return FLUSH; + } + + public boolean includes(IElementUpdateTester tester) { + // A refresh-all tester includes a non-refresh-all tester, but not + // vice versa. This allows entries that were marked as dirty by + // a flush with + // the non-refresh-all to be superseded by a refresh-all update which + // actually clears the entries that were marked as dirty. + if (tester instanceof ThreadsUpdateTester) { + ThreadsUpdateTester threadsTester = (ThreadsUpdateTester)tester; + if (fRefreshAll) { + if (threadsTester.fRefreshAll) { + return fBaseTester.includes(threadsTester.fBaseTester); + } + // refresh-all includes the non-refresh-all + return true; + } else { + if (!threadsTester.fRefreshAll) { + return fBaseTester.includes(threadsTester.fBaseTester); + } + } + } + return false; + } + + @Override + public String toString() { + return "Threads update tester (base = " + fBaseTester + ") update tester"; //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + + public DelayedStackRefreshUpdatePolicy(IVMUpdatePolicy base) { + super(base); + } + + @Override + public IElementUpdateTester getElementUpdateTester(Object event) { + if (event instanceof ISuspendedDMEvent) { + return new DelayedStackRefreshUpdateTester(getBaseUpdatePolicy().getElementUpdateTester(event), true); + } else if (event instanceof FullStackRefreshEvent) { + return new DelayedStackRefreshUpdateTester(getBaseUpdatePolicy().getElementUpdateTester(event), false); + } else if (event instanceof IExitedDMEvent && + ((IExitedDMEvent)event).getDMContext() instanceof IContainerDMContext) + { + // container exit should always trigger a refresh + return new ThreadsUpdateTester(super.getElementUpdateTester(event), true); + } else { + return new ThreadsUpdateTester(super.getElementUpdateTester(event), false); + } + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/ExpandStackEvent.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/ExpandStackEvent.java new file mode 100644 index 00000000000..bed23000bb8 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/ExpandStackEvent.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2008 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.launch; + +import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; + +/** + * Event to increase the stack frame limit for an execution context. + * + * @since 1.1 + */ +public class ExpandStackEvent extends AbstractDMEvent<IExecutionDMContext> { + + public ExpandStackEvent(IExecutionDMContext execCtx) { + super(execCtx); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/FullStackRefreshEvent.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/FullStackRefreshEvent.java new file mode 100644 index 00000000000..ecca6de8eb6 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/FullStackRefreshEvent.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2008 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.launch; + +import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; + +/** + * Indicates the end of a sequence of steps. Should be handled like a suspended + * event to trigger a full refresh of stack frames. + * + * @since 1.1 + */ +public class FullStackRefreshEvent extends AbstractDMEvent<IExecutionDMContext> { + + public FullStackRefreshEvent(IExecutionDMContext execCtx) { + super(execCtx); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/LaunchRootVMNode.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/LaunchRootVMNode.java new file mode 100644 index 00000000000..faed099a305 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/LaunchRootVMNode.java @@ -0,0 +1,130 @@ +/******************************************************************************* + * 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.launch; + +import java.util.Arrays; +import java.util.List; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.IRootVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.RootVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.VMDelta; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.DebugEvent; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchManager; +import org.eclipse.debug.core.model.IDebugElement; +import org.eclipse.debug.core.model.IProcess; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; + +/** + * Layout node for the standard ILaunch object. This node can only be used at + * the root of a hierarchy. It does not implement the label provider + * functionality, so the default adapters should be used to retrieve the label. + */ +@SuppressWarnings("restriction") +public class LaunchRootVMNode extends RootVMNode + implements IRootVMNode +{ + public static class LaunchesEvent { + public enum Type { ADDED, REMOVED, CHANGED, TERMINATED } + public final ILaunch[] fLaunches; + public final Type fType; + + public LaunchesEvent(ILaunch[] launches, Type type) { + fLaunches = launches; + fType = type; + } + } + + + public LaunchRootVMNode(AbstractVMProvider provider) { + super(provider); + } + + @Override + public String toString() { + return "LaunchRootVMNode"; //$NON-NLS-1$ + } + + @Override + public boolean isDeltaEvent(Object rootObject, Object e) { + if (e instanceof DebugEvent) { + DebugEvent de = (DebugEvent)e; + if (de.getSource() instanceof IProcess && + !((IProcess)de.getSource()).getLaunch().equals(rootObject) ) + { + return false; + } + else if (de.getSource() instanceof IDebugElement && + !rootObject.equals(((IDebugElement)de.getSource()).getLaunch())) + { + return false; + } + } + return super.isDeltaEvent(rootObject, e); + } + + @Override + public int getDeltaFlags(Object e) { + int flags = 0; + if (e instanceof LaunchesEvent) { + LaunchesEvent le = (LaunchesEvent)e; + if (le.fType == LaunchesEvent.Type.CHANGED || le.fType == LaunchesEvent.Type.TERMINATED) { + flags = IModelDelta.STATE | IModelDelta.CONTENT; + } + } + + return flags; + } + + @Override + public void createRootDelta(Object rootObject, Object event, final DataRequestMonitor<VMDelta> rm) { + if (!(rootObject instanceof ILaunch)) { + rm.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, "Invalid root element configured with launch root node.", null)); //$NON-NLS-1$ + return; + } + + ILaunch rootLaunch = (ILaunch)rootObject; + + /* + * Create the root of the delta. Since the launch object is not at the + * root of the view, create the delta with the path to the launch. + */ + ILaunchManager manager = DebugPlugin.getDefault().getLaunchManager(); + List<ILaunch> launchList = Arrays.asList(manager.getLaunches()); + final VMDelta viewRootDelta = new VMDelta(manager, 0, IModelDelta.NO_CHANGE, launchList.size()); + final VMDelta rootDelta = viewRootDelta.addNode(rootLaunch, launchList.indexOf(rootLaunch), IModelDelta.NO_CHANGE); + + // Generate delta for launch node. + if (event instanceof LaunchesEvent) { + LaunchesEvent le = (LaunchesEvent)event; + for (ILaunch launch : le.fLaunches) { + if (rootLaunch == launch) { + if (le.fType == LaunchesEvent.Type.CHANGED) { + rootDelta.setFlags(rootDelta.getFlags() | IModelDelta.STATE | IModelDelta.CONTENT); + } else if (le.fType == LaunchesEvent.Type.TERMINATED) { + rootDelta.setFlags(rootDelta.getFlags() | IModelDelta.STATE | IModelDelta.CONTENT); + } + } + } + } + + rm.setData(rootDelta); + rm.done(); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/LaunchVMUpdateMessages.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/LaunchVMUpdateMessages.java new file mode 100644 index 00000000000..0a5db99385e --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/LaunchVMUpdateMessages.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 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.launch; + +import org.eclipse.osgi.util.NLS; + +/** + * @since 1.1 + */ +public class LaunchVMUpdateMessages extends NLS { + + private static final String BUNDLE_NAME = "org.eclipse.cdt.dsf.debug.ui.viewmodel.launch.LaunchVMUpdateMessages";//$NON-NLS-1$ + + public static String ThreadsAutomaticUpdatePolicy_name; + public static String ThreadsManualUpdatePolicy_name; + + static { + // load message values from bundle file + NLS.initializeMessages(BUNDLE_NAME, LaunchVMUpdateMessages.class); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/StackFramesVMNode.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/StackFramesVMNode.java new file mode 100644 index 00000000000..957f25598f1 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/StackFramesVMNode.java @@ -0,0 +1,724 @@ +/******************************************************************************* + * 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.launch; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.RejectedExecutionException; + +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.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.IStack; +import org.eclipse.cdt.dsf.debug.service.IStack2; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerSuspendedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExitedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; +import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMData; +import org.eclipse.cdt.dsf.debug.service.StepQueueManager.ISteppingTimedOutEvent; +import org.eclipse.cdt.dsf.debug.ui.IDsfDebugUIConstants; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.SteppingController; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.SteppingController.SteppingTimedOutEvent; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.ui.concurrent.ViewerDataRequestMonitor; +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMContext; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMContext; +import org.eclipse.cdt.dsf.ui.viewmodel.ModelProxyInstalledEvent; +import org.eclipse.cdt.dsf.ui.viewmodel.VMChildrenUpdate; +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.AbstractDMVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; +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.IElementCompareRequest; +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.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.ui.DebugUITools; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.ui.IMemento; + +@SuppressWarnings("restriction") +public class StackFramesVMNode extends AbstractDMVMNode + implements IElementLabelProvider, IElementMementoProvider +{ + + /** + * View model context representing the end of an incomplete stack. + * + * @since 1.1 + */ + public class IncompleteStackVMContext extends AbstractVMContext { + private final int fLevel; + private final IExecutionDMContext fDmc; + + public IncompleteStackVMContext(IExecutionDMContext dmc, int level) { + super(StackFramesVMNode.this); + fDmc = dmc; + fLevel = level; + } + public int getLevel() { + return fLevel; + } + public IExecutionDMContext getExecutionDMContext() { + return fDmc; + } + @Override + public boolean equals(Object obj) { + return obj instanceof IncompleteStackVMContext && + ((IncompleteStackVMContext)obj).fDmc.equals(fDmc); + } + + @Override + public int hashCode() { + return fDmc.hashCode(); + } + } + + /** + * Temporary stack frame limit to allow incremental stack updates. + */ + private Map<IExecutionDMContext, Integer> fTemporaryLimits = new HashMap<IExecutionDMContext, Integer>(); + + public StackFramesVMNode(AbstractDMVMProvider provider, DsfSession session) { + super(provider, session, IStack.IFrameDMContext.class); + } + + @Override + public String toString() { + return "StackFramesVMNode(" + getSession().getId() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMNode#updateHasElementsInSessionThread(org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate) + */ + @Override + protected void updateHasElementsInSessionThread(IHasChildrenUpdate update) { + IRunControl runControl = getServicesTracker().getService(IRunControl.class); + IExecutionDMContext execCtx = findDmcInPath(update.getViewerInput(), update.getElementPath(), IExecutionDMContext.class); + if (runControl == null || execCtx == null) { + handleFailedUpdate(update); + return; + } + + update.setHasChilren(runControl.isSuspended(execCtx) || runControl.isStepping(execCtx)); + update.done(); + } + + @Override + protected void updateElementCountInSessionThread(final IChildrenCountUpdate update) { + IStack stackService = getServicesTracker().getService(IStack.class); + final IExecutionDMContext execDmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), IExecutionDMContext.class); + if (stackService == null || execDmc == null) { + handleFailedUpdate(update); + return; + } + + final int stackFrameLimit= getStackFrameLimit(execDmc); + stackService.getStackDepth( + execDmc, stackFrameLimit == Integer.MAX_VALUE ? 0 : stackFrameLimit + 1, + new ViewerDataRequestMonitor<Integer>(getSession().getExecutor(), update) { + @Override + public void handleCompleted() { + if (!isSuccess()) { + handleFailedUpdate(update); + return; + } + int stackDepth= getData(); + if (stackFrameLimit < stackDepth) { + stackDepth = stackFrameLimit + 1; + } + update.setChildCount(stackDepth); + update.done(); + } + }); + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMNode#updateElementsInSessionThread(org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate) + */ + @Override + protected void updateElementsInSessionThread(final IChildrenUpdate update) { + IStack stackService = getServicesTracker().getService(IStack.class); + final IExecutionDMContext execDmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), IExecutionDMContext.class); + if (stackService == null || execDmc == null) { + handleFailedUpdate(update); + return; + } + + final int stackFrameLimit= getStackFrameLimit(execDmc); + final int startIndex= update.getOffset(); + + if (startIndex == 0 && update.getLength() == 1) { + // Requesting top stack frame only + stackService.getTopFrame( + execDmc, + new ViewerDataRequestMonitor<IFrameDMContext>(getSession().getExecutor(), update) { + @Override + public void handleCompleted() { + if (!isSuccess()) { + handleFailedUpdate(update); + return; + } + update.setChild(createVMContext(getData()), 0); + update.done(); + } + }); + + } else { + if (startIndex >= 0 && update.getLength() > 0 && stackService instanceof IStack2) { + // partial stack dump + IStack2 stackService2= (IStack2) stackService; + int endIndex= startIndex + update.getLength() - 1; + if (startIndex < stackFrameLimit && endIndex >= stackFrameLimit) { + endIndex = stackFrameLimit - 1; + } + stackService2.getFrames( + execDmc, + startIndex, + endIndex, + new ViewerDataRequestMonitor<IFrameDMContext[]>(getSession().getExecutor(), update) { + @Override + public void handleCompleted() { + if (!isSuccess()) { + handleFailedUpdate(update); + return; + } + IFrameDMContext[] frames = getData(); + fillUpdateWithVMCs(update, frames, startIndex); + if (startIndex + update.getLength() > stackFrameLimit) { + update.setChild(new IncompleteStackVMContext(execDmc, stackFrameLimit), stackFrameLimit); + } + update.done(); + } + }); + } else { + // full stack dump + stackService.getFrames( + execDmc, + new ViewerDataRequestMonitor<IFrameDMContext[]>(getSession().getExecutor(), update) { + @Override + public void handleCompleted() { + if (!isSuccess()) { + handleFailedUpdate(update); + return; + } + IFrameDMContext[] frames = getData(); + if (frames.length > stackFrameLimit) { + IFrameDMContext[] tmpFrames = new IFrameDMContext[stackFrameLimit]; + System.arraycopy(frames, 0, tmpFrames, 0, stackFrameLimit); + frames = tmpFrames; + update.setChild(new IncompleteStackVMContext(execDmc, stackFrameLimit), stackFrameLimit); + } + fillUpdateWithVMCs(update, frames); + update.done(); + } + }); + } + } + } + + /* + * (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider#update(org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate[]) + */ + public void update(final ILabelUpdate[] updates) { + try { + getSession().getExecutor().execute(new DsfRunnable() { + public void run() { + updateLabelInSessionThread(updates); + }}); + } catch (RejectedExecutionException e) { + for (ILabelUpdate update : updates) { + handleFailedUpdate(update); + } + } + } + + protected void updateLabelInSessionThread(ILabelUpdate[] updates) { + for (final ILabelUpdate update : updates) { + IStack stackService = getServicesTracker().getService(IStack.class); + + if (stackService == null) { + handleFailedUpdate(update); + continue; + } + + if (update.getElement() instanceof IncompleteStackVMContext) { + update.setLabel("<...more frames...>", 0); //$NON-NLS-1$ + update.setImageDescriptor(DebugUITools.getImageDescriptor(IDebugUIConstants.IMG_OBJS_STACKFRAME), 0); + update.done(); + continue; + } + + final IFrameDMContext dmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), IFrameDMContext.class); + if (dmc == null) { + handleFailedUpdate(update); + continue; + } + + getDMVMProvider().getModelData( + this, update, + getServicesTracker().getService(IStack.class, null), + dmc, + new ViewerDataRequestMonitor<IFrameDMData>(getSession().getExecutor(), update) { + @Override + protected void handleCompleted() { + /* + * Check that the request was evaluated and data is still + * valid. The request could fail if the state of the + * service changed during the request, but the view model + * has not been updated yet. + */ + if (!isSuccess()) { + assert getStatus().isOK() || + getStatus().getCode() != IDsfStatusConstants.INTERNAL_ERROR || + getStatus().getCode() != IDsfStatusConstants.NOT_SUPPORTED; + handleFailedUpdate(update); + return; + } + + /* + * If columns are configured, call the protected methods to + * fill in column values. + */ + String[] localColumns = update.getColumnIds(); + if (localColumns == null) localColumns = new String[] { null }; + + for (int i = 0; i < localColumns.length; i++) { + fillColumnLabel(dmc, getData(), localColumns[i], i, update); + } + update.done(); + } + }, + getExecutor()); + } + } + + protected void fillColumnLabel(IFrameDMContext dmContext, IFrameDMData dmData, String columnId, int idx, ILabelUpdate update) + { + if (idx != 0) return; + + final IExecutionDMContext execDmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), IExecutionDMContext.class); + if (execDmc == null) { + return; + } + IRunControl runControlService = getServicesTracker().getService(IRunControl.class); + SteppingController stepQueueMgr = (SteppingController) execDmc.getAdapter(SteppingController.class); + if (runControlService == null || stepQueueMgr == null) return; + + String imageKey = null; + if (runControlService.isSuspended(execDmc) || + (runControlService.isStepping(execDmc) && !stepQueueMgr.isSteppingTimedOut(execDmc))) + { + imageKey = IDebugUIConstants.IMG_OBJS_STACKFRAME; + } else { + imageKey = IDebugUIConstants.IMG_OBJS_STACKFRAME_RUNNING; + } + update.setImageDescriptor(DebugUITools.getImageDescriptor(imageKey), 0); + + // + // Finally, if all goes well, set the label. + // + StringBuilder label = new StringBuilder(); + + // Add the function name + if (dmData.getFunction() != null && dmData.getFunction().length() != 0) { + label.append(" "); //$NON-NLS-1$ + label.append(dmData.getFunction()); + label.append("()"); //$NON-NLS-1$ + } + + // Add full file name + if (dmData.getFile() != null && dmData.getFile().length() != 0) { + label.append(" at "); //$NON-NLS-1$ + label.append(dmData.getFile()); + } + + // Add line number + if (dmData.getLine() >= 0) { + label.append(":"); //$NON-NLS-1$ + label.append(dmData.getLine()); + label.append(" "); //$NON-NLS-1$ + } + + // Add the address + if (dmData.getAddress() != null) { + label.append("- 0x" + dmData.getAddress().toString(16)); //$NON-NLS-1$ + } + + // Set the label to the result listener + update.setLabel(label.toString(), 0); + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMNode#getContextsForEvent(org.eclipse.cdt.dsf.ui.viewmodel.VMDelta, java.lang.Object, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor) + */ + @Override + public void getContextsForEvent(final VMDelta parentDelta, Object e, final DataRequestMonitor<IVMContext[]> rm) { + if (e instanceof ModelProxyInstalledEvent) { + // Retrieve the list of stack frames, and mark the top frame to be selected. + getVMProvider().updateNode( + this, + new VMChildrenUpdate( + parentDelta, getVMProvider().getPresentationContext(), 0, 1, + new DataRequestMonitor<List<Object>>(getExecutor(), rm) { + @Override + public void handleCompleted() { + if (isSuccess() && getData().size() != 0) { + rm.setData(new IVMContext[] { (IVMContext)getData().get(0) }); + } else { + // In case of errors, return an empty set of frames. + rm.setData(new IVMContext[0]); + } + rm.done(); + } + }) + ); + return; + } + super.getContextsForEvent(parentDelta, e, rm); + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.ui.viewmodel.IVMNode#getDeltaFlags(java.lang.Object) + */ + public int getDeltaFlags(Object e) { + // This node generates delta if the timers have changed, or if the + // label has changed. + if (e instanceof ISuspendedDMEvent) { + return IModelDelta.CONTENT | IModelDelta.EXPAND | IModelDelta.SELECT; + } else if (e instanceof FullStackRefreshEvent) { + return IModelDelta.CONTENT | IModelDelta.EXPAND; + } else if (e instanceof SteppingTimedOutEvent) { + return IModelDelta.CONTENT; + } else if (e instanceof ISteppingTimedOutEvent) { + return IModelDelta.CONTENT; + } else if (e instanceof ModelProxyInstalledEvent) { + return IModelDelta.SELECT | IModelDelta.EXPAND; + } else if (e instanceof ExpandStackEvent) { + return IModelDelta.CONTENT; + } else if (e instanceof IExitedDMEvent) { + // Do not generate a delta for this event, but do clear the + // internal stack frame limit to avoid a memory leak. + clearStackFrameLimit( ((IExitedDMEvent)e).getDMContext() ); + return IModelDelta.NO_CHANGE; + } else if (e instanceof PropertyChangeEvent) { + String property = ((PropertyChangeEvent)e).getProperty(); + if (IDsfDebugUIConstants.PREF_STACK_FRAME_LIMIT_ENABLE.equals(property) + || IDsfDebugUIConstants.PREF_STACK_FRAME_LIMIT.equals(property)) + { + return IModelDelta.CONTENT; + } + } else { + } + + return IModelDelta.NO_CHANGE; + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.ui.viewmodel.IVMNode#buildDelta(java.lang.Object, org.eclipse.cdt.dsf.ui.viewmodel.VMDelta, int, org.eclipse.cdt.dsf.concurrent.RequestMonitor) + */ + public void buildDelta(final Object e, final VMDelta parent, final int nodeOffset, final RequestMonitor rm) { + if (e instanceof IContainerSuspendedDMEvent) { + // Clear the limit on the stack frames for all stack frames under a given container. + clearStackFrameLimit( ((IContainerSuspendedDMEvent)e).getDMContext() ); + + IContainerSuspendedDMEvent csEvent = (IContainerSuspendedDMEvent)e; + + IExecutionDMContext triggeringCtx = csEvent.getTriggeringContexts().length != 0 + ? csEvent.getTriggeringContexts()[0] : null; + + if (parent.getElement() instanceof IDMVMContext) { + IExecutionDMContext threadDmc = null; + threadDmc = DMContexts.getAncestorOfType( ((IDMVMContext)parent.getElement()).getDMContext(), IExecutionDMContext.class); + buildDeltaForSuspendedEvent(threadDmc, triggeringCtx, parent, nodeOffset, rm); + } else { + rm.done(); + } + } else if (e instanceof FullStackRefreshEvent) { + IExecutionDMContext execDmc = ((FullStackRefreshEvent)e).getDMContext(); + buildDeltaForFullStackRefreshEvent(execDmc, execDmc, parent, nodeOffset, rm); + } else if (e instanceof ISuspendedDMEvent) { + clearStackFrameLimit( ((ISuspendedDMEvent)e).getDMContext() ); + IExecutionDMContext execDmc = ((ISuspendedDMEvent)e).getDMContext(); + buildDeltaForSuspendedEvent(execDmc, execDmc, parent, nodeOffset, rm); + } else if (e instanceof SteppingTimedOutEvent) { + buildDeltaForSteppingTimedOutEvent((SteppingTimedOutEvent)e, parent, nodeOffset, rm); + } else if (e instanceof ISteppingTimedOutEvent) { + buildDeltaForSteppingTimedOutEvent((ISteppingTimedOutEvent)e, parent, nodeOffset, rm); + } else if (e instanceof ModelProxyInstalledEvent) { + buildDeltaForModelProxyInstalledEvent(parent, nodeOffset, rm); + } else if (e instanceof ExpandStackEvent) { + IExecutionDMContext execDmc = ((ExpandStackEvent)e).getDMContext(); + buildDeltaForExpandStackEvent(execDmc, parent, rm); + } else if (e instanceof PropertyChangeEvent) { + String property = ((PropertyChangeEvent)e).getProperty(); + if (IDsfDebugUIConstants.PREF_STACK_FRAME_LIMIT_ENABLE.equals(property) + || IDsfDebugUIConstants.PREF_STACK_FRAME_LIMIT.equals(property)) + { + buildDeltaForStackFrameLimitPreferenceChangedEvent(parent, rm); + } else { + rm.done(); + } + } else { + rm.done(); + } + } + + private void buildDeltaForSuspendedEvent(final IExecutionDMContext executionCtx, final IExecutionDMContext triggeringCtx, final VMDelta parentDelta, final int nodeOffset, final RequestMonitor rm) { + IRunControl runControlService = getServicesTracker().getService(IRunControl.class); + IStack stackService = getServicesTracker().getService(IStack.class); + if (stackService == null || runControlService == null) { + // Required services have not initialized yet. Ignore the event. + rm.done(); + return; + } + + // Check if we are building a delta for the thread that triggered the event. + // Only then expand the stack frames and select the top one. + if (executionCtx.equals(triggeringCtx)) { + // Always expand the thread node to show the stack frames. + parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.EXPAND); + + // Retrieve the list of stack frames, and mark the top frame to be selected. + getVMProvider().updateNode( + this, + new VMChildrenUpdate( + parentDelta, getVMProvider().getPresentationContext(), 0, 2, + new DataRequestMonitor<List<Object>>(getExecutor(), rm) { + @Override + public void handleCompleted() { + final List<Object> data= getData(); + if (data != null && data.size() != 0) { + parentDelta.addNode(data.get(0), 0, IModelDelta.SELECT | IModelDelta.STATE); + + // Refresh the whole list of stack frames unless the target is already stepping the next command. In + // which case, the refresh will occur when the stepping sequence slows down or stops. Trying to + // refresh the whole stack trace with every step would slow down stepping too much. + IRunControl runControlService = getServicesTracker().getService(IRunControl.class); + if (runControlService != null && + triggeringCtx != null && runControlService.isStepping(triggeringCtx) && + data.size() >= 2) + { + parentDelta.addNode( data.get(1), 1, IModelDelta.STATE); + } + } + // Even in case of errors, complete the request monitor. + rm.done(); + } + }) + ); + } else { + rm.done(); + } + } + + private void buildDeltaForFullStackRefreshEvent(final IExecutionDMContext executionCtx, final IExecutionDMContext triggeringCtx, final VMDelta parentDelta, final int nodeOffset, final RequestMonitor rm) { + IRunControl runControlService = getServicesTracker().getService(IRunControl.class); + IStack stackService = getServicesTracker().getService(IStack.class); + if (stackService == null || runControlService == null) { + // Required services have not initialized yet. Ignore the event. + rm.done(); + return; + } + + // Refresh the whole list of stack frames unless the target is already stepping the next command. In + // which case, the refresh will occur when the stepping sequence slows down or stops. Trying to + // refresh the whole stack trace with every step would slow down stepping too much. + if (triggeringCtx == null || !runControlService.isStepping(triggeringCtx)) { + parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); + } + + rm.done(); + } + + private void buildDeltaForSteppingTimedOutEvent(final SteppingTimedOutEvent e, final VMDelta parentDelta, final int nodeOffset, final RequestMonitor rm) { + // Repaint the stack frame images to have the running symbol. + //parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); + rm.done(); + } + + private void buildDeltaForSteppingTimedOutEvent(final ISteppingTimedOutEvent e, final VMDelta parentDelta, final int nodeOffset, final RequestMonitor rm) { + // Repaint the stack frame images to have the running symbol. + //parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); + rm.done(); + } + + private void buildDeltaForModelProxyInstalledEvent(final VMDelta parentDelta, final int nodeOffset, final RequestMonitor rm) { + // Retrieve the list of stack frames, and mark the top frame to be selected. + getVMProvider().updateNode( + this, + new VMChildrenUpdate( + parentDelta, getVMProvider().getPresentationContext(), -1, -1, + new DataRequestMonitor<List<Object>>(getExecutor(), rm) { + @Override + public void handleCompleted() { + if (isSuccess() && getData().size() != 0) { + parentDelta.addNode( getData().get(0), 0, IModelDelta.SELECT | IModelDelta.EXPAND); + } + rm.done(); + } + }) + ); + } + + private void buildDeltaForExpandStackEvent(IExecutionDMContext execDmc, final VMDelta parentDelta, final RequestMonitor rm) { + parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); + rm.done(); + } + + + private void buildDeltaForStackFrameLimitPreferenceChangedEvent(final VMDelta parentDelta, final RequestMonitor rm) { + parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); + rm.done(); + } + + private String produceFrameElementName( String viewName , IFrameDMContext frame ) { + /* + * We are addressing Bugzilla 211490 which wants the Register View to keep the same expanded + * state for registers for stack frames within the same thread. Different threads could have + * different register sets ( e.g. one thread may have floating point & another may not ). But + * within a thread we are enforcing the assumption that the register sets will be the same. + * So we make a more convenient work flow by keeping the same expansion when selecting amount + * stack frames within the same thread. We accomplish this by only differentiating by adding + * the level for the Expression/Variables view. Otherwise we do not delineate based on which + * view and this captures the Register View in its filter. + */ + if ( viewName.startsWith(IDebugUIConstants.ID_VARIABLE_VIEW) || + viewName.startsWith(IDebugUIConstants.ID_EXPRESSION_VIEW) ) + { + return "Frame." + frame.getLevel() + "." + frame.getSessionId(); //$NON-NLS-1$ //$NON-NLS-2$ + } + else { + return "Frame" + frame.getSessionId(); //$NON-NLS-1$ + } + } + + /* + * (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoProvider#compareElements(org.eclipse.debug.internal.ui.viewers.model.provisional.IElementCompareRequest[]) + */ + public void compareElements(IElementCompareRequest[] requests) { + + for ( IElementCompareRequest request : requests ) { + + Object element = request.getElement(); + IMemento memento = request.getMemento(); + String mementoName = memento.getString("STACK_FRAME_MEMENTO_NAME"); //$NON-NLS-1$ + + if (mementoName != null) { + if (element instanceof IDMVMContext) { + + IDMContext dmc = ((IDMVMContext)element).getDMContext(); + + if ( dmc instanceof IFrameDMContext) { + + String elementName = produceFrameElementName( request.getPresentationContext().getId(), (IFrameDMContext) dmc ); + request.setEqual( elementName.equals( mementoName ) ); + } + } + } + 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 ( IElementMementoRequest request : requests ) { + + Object element = request.getElement(); + IMemento memento = request.getMemento(); + + if (element instanceof IDMVMContext) { + + IDMContext dmc = ((IDMVMContext)element).getDMContext(); + + if ( dmc instanceof IFrameDMContext) { + + String elementName = produceFrameElementName( request.getPresentationContext().getId(), (IFrameDMContext) dmc ); + memento.putString("STACK_FRAME_MEMENTO_NAME", elementName); //$NON-NLS-1$ + } + } + request.done(); + } + } + + /** + * Get the current active stack frame limit. If no limit is applicable {@link Integer.MAX_VALUE} is returned. + * + * @return the current stack frame limit + * + * @since 1.1 + */ + public int getStackFrameLimit(IExecutionDMContext execCtx) { + if (fTemporaryLimits.containsKey(execCtx)) { + return fTemporaryLimits.get(execCtx); + } + Object stackDepthLimit= getVMProvider().getPresentationContext().getProperty(IDsfDebugUIConstants.PREF_STACK_FRAME_LIMIT); + if (stackDepthLimit instanceof Integer) { + return (Integer)stackDepthLimit; + } + return Integer.MAX_VALUE; + } + + private void clearStackFrameLimit(IExecutionDMContext execCtx) { + if (execCtx instanceof IContainerDMContext) { + for (Iterator<IExecutionDMContext> itr = fTemporaryLimits.keySet().iterator(); itr.hasNext();) { + IExecutionDMContext limitCtx = itr.next(); + if (limitCtx.equals(execCtx) || DMContexts.isAncestorOf(limitCtx, execCtx)) { + itr.remove(); + } + } + } else { + fTemporaryLimits.remove(execCtx); + } + } + + + /** + * Increment the stack frame limit by the default increment. + * This implementation doubles the current limit. + * + * @since 1.1 + */ + public void incrementStackFrameLimit(IExecutionDMContext execCtx) { + final int stackFrameLimit= getStackFrameLimit(execCtx); + if (stackFrameLimit < Integer.MAX_VALUE / 2) { + fTemporaryLimits.put(execCtx, stackFrameLimit * 2); + } else { + fTemporaryLimits.put(execCtx, Integer.MAX_VALUE); + } + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/StandardProcessVMNode.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/StandardProcessVMNode.java new file mode 100644 index 00000000000..5e0e7212e23 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/StandardProcessVMNode.java @@ -0,0 +1,223 @@ +/******************************************************************************* + * 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.launch; + +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMContext; +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMContext; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.VMDelta; +import org.eclipse.debug.core.DebugEvent; +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.model.IProcess; +import org.eclipse.debug.core.model.IStreamsProxy; +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.IHasChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; +import org.eclipse.debug.internal.ui.viewers.provisional.ILabelRequestMonitor; +import org.eclipse.jface.viewers.TreePath; + +/** + * Layout node for the standard platform debug model IProcess object. This + * node requires that an ILaunch object be found as an ancestor of this node. + * It does not implement the label provider functionality, so the default + * adapters should be used to retrieve the label. + */ +@SuppressWarnings("restriction") +public class StandardProcessVMNode extends AbstractVMNode { + + /** + * VMC element implementation, it is a proxy for the IProcess class, to + * allow the standard label adapter to be used with this object. + */ + private class VMC extends AbstractVMContext + implements IProcess + { + private final IProcess fProcess; + + VMC(IProcess process) { + super(StandardProcessVMNode.this); + fProcess = process; + } + + @Override + public IVMNode getVMNode() { return StandardProcessVMNode.this; } + @Override + @SuppressWarnings("unchecked") + public Object getAdapter(Class adapter) { + Object vmcAdapter = super.getAdapter(adapter); + if (vmcAdapter != null) { + return vmcAdapter; + } + return fProcess.getAdapter(adapter); + } + @Override + public String toString() { return "IProcess " + fProcess.toString(); } //$NON-NLS-1$ + + public String getAttribute(String key) { return fProcess.getAttribute(key); } + public int getExitValue() throws DebugException { return fProcess.getExitValue(); } + public String getLabel() { return fProcess.getLabel(); } + public ILaunch getLaunch() { return fProcess.getLaunch(); } + public IStreamsProxy getStreamsProxy() { return fProcess.getStreamsProxy(); } + public void setAttribute(String key, String value) { fProcess.setAttribute(key, value); } + public boolean canTerminate() { return fProcess.canTerminate(); } + public boolean isTerminated() { return fProcess.isTerminated(); } + public void terminate() throws DebugException { fProcess.terminate(); } + + @Override + public boolean equals(Object other) { + return other instanceof VMC && fProcess.equals(((VMC)other).fProcess); + } + @Override + public int hashCode() { return fProcess.hashCode(); } + } + + public StandardProcessVMNode(AbstractVMProvider provider) { + super(provider); + } + + @Override + public String toString() { + return "StandardProcessVMNode"; //$NON-NLS-1$ + } + + public void update(IChildrenUpdate[] updates) { + for (IChildrenUpdate update : updates) { + ILaunch launch = findLaunch(update.getElementPath()); + if (launch == null) { + // There is no launch in the parent of this node. This means that the + // layout is misconfigured. + assert false; + update.done(); + continue; + } + + /* + * Assume that the process objects are stored within the launch, and + * retrieve them on dispatch thread. + */ + IProcess[] processes = launch.getProcesses(); + for (int i = 0; i < processes.length; i++) { + update.setChild(new VMC(processes[i]), i); + } + update.done(); + } + } + + public void update(final IChildrenCountUpdate[] updates) { + for (IChildrenCountUpdate update : updates) { + if (!checkUpdate(update)) continue; + ILaunch launch = findLaunch(update.getElementPath()); + if (launch == null) { + assert false; + update.setChildCount(0); + update.done(); + return; + } + + update.setChildCount(launch.getProcesses().length); + update.done(); + } + } + + // @see org.eclipse.cdt.dsf.ui.viewmodel.IViewModelLayoutNode#hasElements(org.eclipse.cdt.dsf.ui.viewmodel.IVMContext, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor) + public void update(IHasChildrenUpdate[] updates) { + for (IHasChildrenUpdate update : updates) { + ILaunch launch = findLaunch(update.getElementPath()); + if (launch == null) { + assert false; + update.setHasChilren(false); + update.done(); + return; + } + + update.setHasChilren(launch.getProcesses().length != 0); + update.done(); + } + } + + // @see org.eclipse.cdt.dsf.ui.viewmodel.IViewModelLayoutNode#retrieveLabel(org.eclipse.cdt.dsf.ui.viewmodel.IVMContext, org.eclipse.debug.internal.ui.viewers.provisional.ILabelRequestMonitor) + public void updateLabel(IVMContext vmc, ILabelRequestMonitor result, String[] columns) { + + /* + * The implementation of IAdapterFactory that uses this node should not + * register a label adapter for IProcess. This will cause the default + * label provider to be used instead, and this method should then never + * be called. + */ + assert false; + result.done(); + } + + /** + * Recursively searches the VMC for Launch VMC, and returns its ILaunch. + * Returns null if an ILaunch is not found. + */ + private ILaunch findLaunch(TreePath path) { + for (int i = path.getSegmentCount() - 1; i >= 0; i--) { + if (path.getSegment(i) instanceof ILaunch) { + return (ILaunch)path.getSegment(i); + } + } + return null; + } + + public int getDeltaFlags(Object e) { + int myFlags = 0; + if (e instanceof DebugEvent) { + DebugEvent de = (DebugEvent)e; + if ( de.getSource() instanceof IProcess && + (de.getKind() == DebugEvent.CHANGE || + de.getKind() == DebugEvent.CREATE || + de.getKind() == DebugEvent.TERMINATE) ) + { + myFlags = IModelDelta.STATE; + } + } + return myFlags; + } + + public void buildDelta(Object e, VMDelta parent, int nodeOffset, RequestMonitor requestMonitor) { + if (e instanceof DebugEvent && ((DebugEvent)e).getSource() instanceof IProcess) { + DebugEvent de = (DebugEvent)e; + if (de.getKind() == DebugEvent.CHANGE) { + handleChange(de, parent); + } else if (de.getKind() == DebugEvent.CREATE) { + handleCreate(de, parent); + } else if (de.getKind() == DebugEvent.TERMINATE) { + handleTerminate(de, parent); + } + /* + * No other node should need to process events related to process. + * Therefore, just invoke the request monitor without calling super.buildDelta(). + */ + } + requestMonitor.done(); + } + + protected void handleChange(DebugEvent event, ModelDelta parent) { + parent.addNode(new VMC((IProcess)event.getSource()), IModelDelta.STATE); + } + + protected void handleCreate(DebugEvent event, ModelDelta parent) { + parent.setFlags(parent.getFlags() | IModelDelta.CONTENT); + } + + protected void handleTerminate(DebugEvent event, ModelDelta parent) { + handleChange(event, parent); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/actions/ExpandStackAction.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/actions/ExpandStackAction.java new file mode 100644 index 00000000000..665d6e9f109 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/launch/actions/ExpandStackAction.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright (c) 2008 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.launch.actions; + +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.actions.AbstractVMProviderActionDelegate; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.launch.AbstractLaunchVMProvider; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.launch.ExpandStackEvent; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.launch.StackFramesVMNode; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.launch.StackFramesVMNode.IncompleteStackVMContext; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMNode; +import org.eclipse.debug.ui.contexts.DebugContextEvent; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.ui.IObjectActionDelegate; +import org.eclipse.ui.IViewPart; +import org.eclipse.ui.IWorkbenchPart; + +/** + * Increment the (temporary) stack limit for the selected stack. + */ +public class ExpandStackAction extends AbstractVMProviderActionDelegate implements IObjectActionDelegate { + + /* + * @see org.eclipse.ui.IActionDelegate#run(org.eclipse.jface.action.IAction) + */ + public void run(IAction action) { + Object element = getViewerInput(); + if (element instanceof IncompleteStackVMContext) { + IncompleteStackVMContext incStackVmc = ((IncompleteStackVMContext) element); + IVMNode node = incStackVmc.getVMNode(); + if (incStackVmc.getVMNode() instanceof StackFramesVMNode) { + final IExecutionDMContext exeCtx= incStackVmc.getExecutionDMContext(); + ((StackFramesVMNode) node).incrementStackFrameLimit(exeCtx); + final ExpandStackEvent event = new ExpandStackEvent(exeCtx); + final AbstractLaunchVMProvider vmProvider = (AbstractLaunchVMProvider) getVMProvider(); + vmProvider.getExecutor().execute(new DsfRunnable() { + public void run() { + vmProvider.handleEvent(event); + } + }); + } + } + } + + @Override + public void init(IViewPart view) { + super.init(view); + updateEnablement(); + } + + @Override + public void debugContextChanged(DebugContextEvent event) { + super.debugContextChanged(event); + updateEnablement(); + } + + @Override + public void selectionChanged(IAction action, ISelection selection) { + super.selectionChanged(action, selection); + updateEnablement(); + } + + private void updateEnablement() { + boolean enabled = false; + if (getVMProvider() instanceof AbstractLaunchVMProvider) { + Object element = getViewerInput(); + enabled = element instanceof IncompleteStackVMContext; + } + getAction().setEnabled(enabled); + } + + public void setActivePart(IAction action, IWorkbenchPart targetPart) { + if (targetPart instanceof IViewPart) { + init((IViewPart) targetPart); + } + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/modules/ModulesVMNode.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/modules/ModulesVMNode.java new file mode 100644 index 00000000000..83c9bbe2e4c --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/modules/ModulesVMNode.java @@ -0,0 +1,180 @@ +/******************************************************************************* + * 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 + * Ericsson AB - Modules view for DSF implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.ui.viewmodel.modules; + +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IModules; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.IModules.IModuleDMContext; +import org.eclipse.cdt.dsf.debug.service.IModules.IModuleDMData; +import org.eclipse.cdt.dsf.debug.service.IModules.ISymbolDMContext; +import org.eclipse.cdt.dsf.debug.ui.IDsfDebugUIConstants; +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.AbstractDMVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; +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.ILabelUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; + +@SuppressWarnings("restriction") +public class ModulesVMNode extends AbstractDMVMNode + implements IElementLabelProvider +{ + /** + * Marker type for the modules VM context. It allows action enablement + * expressions to check for module context type. + */ + public class ModuleVMContext extends DMVMContext { + protected ModuleVMContext(IDMContext dmc) { + super(dmc); + } + } + + + public ModulesVMNode(AbstractDMVMProvider provider, DsfSession session) { + super(provider, session, IModuleDMContext.class); + } + + @Override + public String toString() { + return "ModulesVMNode(" + getSession().getId() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + } + + @Override + protected void updateElementsInSessionThread(final IChildrenUpdate update) { + IModules modulesService = getServicesTracker().getService(IModules.class); + final ISymbolDMContext symDmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), ISymbolDMContext.class) ; + + if (modulesService == null || symDmc == null) { + handleFailedUpdate(update); + return; + } + + modulesService.getModules( + symDmc, + new ViewerDataRequestMonitor<IModuleDMContext[]>(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 ModuleVMContext(dmc); + } + + public void update(final ILabelUpdate[] updates) { + try { + getSession().getExecutor().execute(new DsfRunnable() { + public void run() { + updateLabelInSessionThread(updates); + }}); + } catch (RejectedExecutionException e) { + for (ILabelUpdate update : updates) { + handleFailedUpdate(update); + } + } + } + + + protected void updateLabelInSessionThread(ILabelUpdate[] updates) { + for (final ILabelUpdate update : updates) { + IModules modulesService = getServicesTracker().getService(IModules.class); + final IModuleDMContext dmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), IModuleDMContext.class); + // If either update or service are not valid, fail the update and exit. + if ( modulesService == null || dmc == null ) { + handleFailedUpdate(update); + continue; + } + + // Use different image for loaded and unloaded symbols when event to report loading of symbols is implemented. + update.setImageDescriptor(DsfUIPlugin.getImageDescriptor(IDsfDebugUIConstants.IMG_OBJS_SHARED_LIBRARY_SYMBOLS_LOADED), 0); + + modulesService.getModuleData( + dmc, + new ViewerDataRequestMonitor<IModuleDMData>(getSession().getExecutor(), update) { + @Override + protected void handleCompleted() { + /* + * The request could fail if the state of the service + * changed during the request, but the view model + * has not been updated yet. + */ + if (!isSuccess()) { + assert getStatus().isOK() || + getStatus().getCode() != IDsfStatusConstants.INTERNAL_ERROR || + getStatus().getCode() != IDsfStatusConstants.NOT_SUPPORTED; + handleFailedUpdate(update); + return; + } + + /* + * If columns are configured, call the protected methods to + * fill in column values. + */ + String[] localColumns = update.getColumnIds(); + if (localColumns == null) localColumns = new String[] { null }; + + for (int i = 0; i < localColumns.length; i++) { + fillColumnLabel(dmc, getData(), localColumns[i], i, update); + } + update.done(); + } + }); + } + } + + protected void fillColumnLabel(IModuleDMContext dmContext, IModuleDMData dmData, + String columnId, int idx, ILabelUpdate update) + { + if ( columnId == null ) { + /* + * If the Column ID comes in as "null" then this is the case where the user has decided + * to not have any columns. So we need a default action which makes the most sense and + * is doable. In this case we elect to simply display the name. + */ + update.setLabel(dmData.getName(), idx); + } + } + + public int getDeltaFlags(Object e) { + if (e instanceof IRunControl.ISuspendedDMEvent) { + return IModelDelta.CONTENT; + } + return IModelDelta.NO_CHANGE; + } + + public void buildDelta(Object e, VMDelta parentDelta, int nodeOffset, RequestMonitor rm) { + if (e instanceof IRunControl.ISuspendedDMEvent) { + // Create a delta that indicates all groups have changed + parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); + } + + rm.done(); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/modules/ModulesVMProvider.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/modules/ModulesVMProvider.java new file mode 100644 index 00000000000..fc713683cdc --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/modules/ModulesVMProvider.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2007, 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 + * Ericsson AB - Modules view for DSF implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.ui.viewmodel.modules; + +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.debug.service.ICachingService; +import org.eclipse.cdt.dsf.debug.service.IModules; +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.IRootVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.RootDMVMNode; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; + +/** + * + */ +@SuppressWarnings("restriction") +public class ModulesVMProvider extends AbstractDMVMProvider { + /* + * Current default for register formatting. + */ + public ModulesVMProvider(AbstractVMAdapter adapter, IPresentationContext context, DsfSession session) { + super(adapter, context, session); + + /* + * Create the top level node to deal with the root selection. + */ + IRootVMNode rootNode = new RootDMVMNode(this); + + /* + * Create the Group nodes next. They represent the first level shown in the view. + */ + IVMNode modulesNode = new ModulesVMNode(this, getSession()); + addChildNodes(rootNode, new IVMNode[] { modulesNode }); + + /* + * Now set this schema set as the layout set. + */ + setRootNode(rootNode); + } + + @Override + public void refresh() { + super.refresh(); + try { + getSession().getExecutor().execute(new DsfRunnable() { + public void run() { + DsfServicesTracker tracker = new DsfServicesTracker(DsfUIPlugin.getBundleContext(), getSession().getId()); + IModules modulesService = tracker.getService(IModules.class); + if (modulesService instanceof ICachingService) { + ((ICachingService)modulesService).flushCache(null); + } + tracker.dispose(); + } + }); + } catch (RejectedExecutionException e) { + // Session disposed, ignore. + } + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/modules/detail/ModuleDetailPane.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/modules/detail/ModuleDetailPane.java new file mode 100644 index 00000000000..f014c8f4c7b --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/modules/detail/ModuleDetailPane.java @@ -0,0 +1,525 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 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 + * QNX Software Systems - Mikhail Khodjaiants - Registers View (Bug 53640) + * Wind River Systems - adopted to use with Modules view + * Ericsson AB - Modules view for DSF implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.ui.viewmodel.modules.detail; + + +import java.util.concurrent.ExecutionException; + +import org.eclipse.cdt.debug.ui.ICDebugUIConstants; +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.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.detailsupport.MessagesForDetailPane; +import org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.detailsupport.TextViewerAction; +import org.eclipse.cdt.dsf.debug.service.IModules; +import org.eclipse.cdt.dsf.debug.service.IModules.IModuleDMContext; +import org.eclipse.cdt.dsf.debug.service.IModules.IModuleDMData; +import org.eclipse.cdt.dsf.debug.ui.IDsfDebugUIConstants; +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.IDMVMContext; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.debug.ui.IDebugView; +import org.eclipse.jface.action.IMenuListener; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.action.Separator; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.text.Document; +import org.eclipse.jface.text.DocumentEvent; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IDocumentListener; +import org.eclipse.jface.text.ITextOperationTarget; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.source.SourceViewer; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.FocusAdapter; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.ui.IWorkbenchActionConstants; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.actions.ActionFactory; +import org.eclipse.ui.progress.WorkbenchJob; +import org.eclipse.ui.texteditor.IWorkbenchActionDefinitionIds; + + +/** + * + */ +public class ModuleDetailPane extends ModulesAbstractDetailPane implements IAdaptable, IPropertyChangeListener { + + /** + * These are the IDs for the actions in the context menu + */ + protected static final String DETAIL_COPY_ACTION = ActionFactory.COPY.getId() + ".SourceDetailPane"; //$NON-NLS-1$ + protected static final String DETAIL_SELECT_ALL_ACTION = IDebugView.SELECT_ALL_ACTION + ".SourceDetailPane"; //$NON-NLS-1$ + + /** + * The ID, name and description of this pane are stored in constants so that the class + * does not have to be instantiated to access them. + */ + public static final String ID = "ModuleDetailPane"; //$NON-NLS-1$ + public static final String NAME = "Module Viewer"; //$NON-NLS-1$ + public static final String DESCRIPTION = "A detail pane that is based on a source viewer. Displays as text and has actions for assigning values, content assist and text modifications."; //$NON-NLS-1$ + + + /** + * The source viewer in which the computed string detail + * of selected modules will be displayed. + */ + private SourceViewer fSourceViewer; + public Control createControl(Composite parent) { + createSourceViewer(parent); + + if (isInView()){ + createViewSpecificComponents(); + createActions(); + DsfUIPlugin.getDefault().getPreferenceStore().addPropertyChangeListener(this); + JFaceResources.getFontRegistry().addListener(this); + } + return fSourceViewer.getControl(); + } + + private DetailJob fDetailJob = null; + public void display(IStructuredSelection selection) { + if (selection == null){ + clearSourceViewer(); + return; + } + + if (isInView()){ + fSourceViewer.setEditable(true); + } + + if (selection.isEmpty()){ + clearSourceViewer(); + return; + } + + synchronized (this) { + if (fDetailJob != null) { + fDetailJob.cancel(); + } + fDetailJob = new DetailJob(selection.getFirstElement()); + fDetailJob.schedule(); + } + + } + + /** + * Clears the source viewer, removes all text. + */ + protected void clearSourceViewer(){ + if (fDetailJob != null) { + fDetailJob.cancel(); + } + fDetailDocument.set(""); //$NON-NLS-1$ + fSourceViewer.setEditable(false); + } + + @Override + public void dispose() { + super.dispose(); + if (fDetailJob != null) fDetailJob.cancel(); + if (fSourceViewer != null && fSourceViewer.getControl() != null) fSourceViewer.getControl().dispose(); + + if (isInView()){ + DsfUIPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(this); + JFaceResources.getFontRegistry().removeListener(this); + } + + } + public String getDescription() { + return DESCRIPTION; + } + public String getID() { + return ID; + } + public String getName() { + return NAME; + } + + public boolean setFocus() { + if (fSourceViewer != null){ + fSourceViewer.getTextWidget().setFocus(); + return true; + } + return false; + } + + @SuppressWarnings("unchecked") + public Object getAdapter(Class adapter) { + if (ITextViewer.class.equals(adapter)) { + return fSourceViewer; + } + return null; + } + + public void propertyChange(PropertyChangeEvent event) { + String propertyName= event.getProperty(); + if (propertyName.equals(IDsfDebugUIConstants.DETAIL_PANE_FONT)) { + fSourceViewer.getTextWidget().setFont(JFaceResources.getFont(IDsfDebugUIConstants.DETAIL_PANE_FONT)); + } + } + + + /** + * Creates the source viewer in the given parent composite + * + * @param parent Parent composite to create the source viewer in + */ + private void createSourceViewer(Composite parent) { + + // Create & configure a SourceViewer + fSourceViewer = new SourceViewer(parent, null, SWT.V_SCROLL | SWT.H_SCROLL); + fSourceViewer.setDocument(getDetailDocument()); + fSourceViewer.getTextWidget().setFont(JFaceResources.getFont(IDsfDebugUIConstants.DETAIL_PANE_FONT)); + fSourceViewer.getTextWidget().setWordWrap(DsfUIPlugin.getDefault().getPreferenceStore().getBoolean(IDsfDebugUIConstants.PREF_DETAIL_PANE_WORD_WRAP)); + fSourceViewer.setEditable(false); + PlatformUI.getWorkbench().getHelpSystem().setHelp(fSourceViewer.getTextWidget(), IDsfDebugUIConstants.DETAIL_PANE); + Control control = fSourceViewer.getControl(); + GridData gd = new GridData(GridData.FILL_BOTH); + control.setLayoutData(gd); + } + + /** + * Variables used to create the detailed information for a selection + */ + private IDocument fDetailDocument; + + /** + * Lazily instantiate and return a Document for the detail pane text viewer. + */ + protected IDocument getDetailDocument() { + if (fDetailDocument == null) { + fDetailDocument = new Document(); + } + return fDetailDocument; + } + + /** + * Creates listeners and other components that should only be added to the + * source viewer when this detail pane is inside a view. + */ + private void createViewSpecificComponents(){ + + // Add a document listener so actions get updated when the document changes + getDetailDocument().addDocumentListener(new IDocumentListener() { + public void documentAboutToBeChanged(DocumentEvent event) {} + public void documentChanged(DocumentEvent event) { + updateSelectionDependentActions(); + } + }); + + // Add the selection listener so selection dependent actions get updated. + fSourceViewer.getSelectionProvider().addSelectionChangedListener(new ISelectionChangedListener() { + public void selectionChanged(SelectionChangedEvent event) { + updateSelectionDependentActions(); + } + }); + + // Add a focus listener to update actions when details area gains focus + fSourceViewer.getControl().addFocusListener(new FocusAdapter() { + @Override + public void focusGained(FocusEvent e) { + + getViewSite().setSelectionProvider(fSourceViewer.getSelectionProvider()); + + setGlobalAction(IDebugView.SELECT_ALL_ACTION, getAction(DETAIL_SELECT_ALL_ACTION)); + setGlobalAction(IDebugView.COPY_ACTION, getAction(DETAIL_COPY_ACTION)); + + getViewSite().getActionBars().updateActionBars(); + } + + @Override + public void focusLost(FocusEvent e) { + + getViewSite().setSelectionProvider(null); + + setGlobalAction(IDebugView.SELECT_ALL_ACTION, null); + setGlobalAction(IDebugView.COPY_ACTION, null); + getViewSite().getActionBars().updateActionBars(); + + } + }); + + // Add a context menu to the detail area + createDetailContextMenu(fSourceViewer.getTextWidget()); + } + + /** + * Create the context menu particular to the detail pane. Note that anyone + * wishing to contribute an action to this menu must use + * <code>ICDebugUIConstants.MODULES_VIEW_DETAIL_ID</code> as the + * <code>targetID</code> in the extension XML. + */ + protected void createDetailContextMenu(Control menuControl) { + MenuManager menuMgr= new MenuManager(); + menuMgr.setRemoveAllWhenShown(true); + menuMgr.addMenuListener(new IMenuListener() { + public void menuAboutToShow(IMenuManager mgr) { + fillDetailContextMenu(mgr); + } + }); + Menu menu= menuMgr.createContextMenu(menuControl); + menuControl.setMenu(menu); + + getViewSite().registerContextMenu(ICDebugUIConstants.MODULES_VIEW_DETAIL_ID, menuMgr, fSourceViewer.getSelectionProvider()); + + } + /** + * Adds items to the detail pane's context menu including any extension defined + * actions. + * + * @param menu The menu to add the item to. + */ + protected void fillDetailContextMenu(IMenuManager menu) { + + menu.add(new Separator(ICDebugUIConstants.MODULES_GROUP)); + menu.add(new Separator()); + menu.add(getAction(DETAIL_COPY_ACTION)); + menu.add(getAction(DETAIL_SELECT_ALL_ACTION)); + menu.add(new Separator()); + menu.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); + + } + /** + * Creates the actions to add to the context menu + */ + private void createActions() { + TextViewerAction textAction= new TextViewerAction(fSourceViewer, ITextOperationTarget.SELECT_ALL); + textAction.configureAction(MessagesForDetailPane.DetailPane_Select_All, "", ""); //$NON-NLS-1$ //$NON-NLS-2$ + textAction.setActionDefinitionId(IWorkbenchActionDefinitionIds.SELECT_ALL); + PlatformUI.getWorkbench().getHelpSystem().setHelp(textAction, IDsfDebugUIConstants.DETAIL_PANE_SELECT_ALL_ACTION); + setAction(DETAIL_SELECT_ALL_ACTION, textAction); + + textAction= new TextViewerAction(fSourceViewer, ITextOperationTarget.COPY); + textAction.configureAction(MessagesForDetailPane.DetailPane_Copy, "", ""); //$NON-NLS-1$ //$NON-NLS-2$ + textAction.setActionDefinitionId(IWorkbenchActionDefinitionIds.COPY); + PlatformUI.getWorkbench().getHelpSystem().setHelp(textAction, IDsfDebugUIConstants.DETAIL_PANE_COPY_ACTION); + setAction(DETAIL_COPY_ACTION, textAction); + + setSelectionDependantAction(DETAIL_COPY_ACTION); + + updateSelectionDependentActions(); + } + + + /** + * Job to compute the details for a selection + */ + class DetailJob extends Job { + + private Object fElement; + // whether a result was collected + private IProgressMonitor fMonitor; + + public DetailJob(Object element) { + super("compute module details"); //$NON-NLS-1$ + setSystem(true); + fElement = element; + } + + /* (non-Javadoc) + * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) + */ + @Override + protected IStatus run(IProgressMonitor monitor) { + fMonitor = monitor; + /* + * Make sure this is an element we want to deal with. + */ + IModuleDMContext dmc = null; + if (fElement instanceof IDMVMContext) { + IDMContext vmcdmc = ((IDMVMContext)fElement).getDMContext(); + dmc = DMContexts.getAncestorOfType(vmcdmc, IModuleDMContext.class); + } + + if (dmc == null) return Status.OK_STATUS; + DsfSession session = DsfSession.getSession(dmc.getSessionId()); + if (session == null) return Status.OK_STATUS; + + /* + * Create the query to write the value to the service. Note: no need to + * guard against RejectedExecutionException, because + * DsfSession.getSession() above would only return an active session. + */ + GetModuleDetailsQuery query = new GetModuleDetailsQuery(dmc); + session.getExecutor().execute(query); + + /* + * Now we have the data, go and get it. Since the call is completed now + * the ".get()" will not suspend it will immediately return with the + * data. + */ + try { + detailComputed(getModuleDetail((IModuleDMData) query.get())); + } catch (InterruptedException e) { + assert false; + return Status.OK_STATUS; + } catch (ExecutionException e) { + return Status.OK_STATUS; + } + return Status.OK_STATUS; + } + + /** + * Set the module details in the detail pane view + * @param result + */ + private void detailComputed(final String result) { + if (!fMonitor.isCanceled()) { + WorkbenchJob setDetail = new WorkbenchJob("set details") { //$NON-NLS-1$ + @Override + public IStatus runInUIThread(IProgressMonitor monitor) { + if (!fMonitor.isCanceled()) { + getDetailDocument().set(result); + } + return Status.OK_STATUS; + } + }; + setDetail.setSystem(true); + setDetail.schedule(); + } + } + + } + + /** + * To get the details of the given module selected in Modules View + * @param module + * @return + */ + private String getModuleDetail( IModuleDMData module ) { + StringBuffer sb = new StringBuffer(); + + // Type + String type = null; +// switch( module.getType() ) { +// case ICModule.EXECUTABLE: +// type = ModulesMessages.getString( "ModulesView.Executable" ); //$NON-NLS-1$ +// break; +// case ICModule.SHARED_LIBRARY: +// type = ModulesMessages.getString( "ModulesView.SharedLibrary" ); //$NON-NLS-1$ +// break; +// } + type = ModulesMessages.getString( "ModulesView.SharedLibrary" ); //$NON-NLS-1$ + if ( type != null ) { + sb.append( ModulesMessages.getString( "ModulesView.Type" ) ); //$NON-NLS-1$ + sb.append( type ); + sb.append( '\n' ); + } + + // Symbols flag + sb.append( ModulesMessages.getString( "ModulesView.Symbols" ) ); //$NON-NLS-1$ + sb.append( ( module.isSymbolsLoaded()) ? ModulesMessages.getString( "ModulesView.Loaded" ) : ModulesMessages.getString( "ModulesView.NotLoaded" ) ); //$NON-NLS-1$ //$NON-NLS-2$ + sb.append( '\n' ); + + // Symbols file + sb.append( ModulesMessages.getString( "ModulesView.SymbolsFile" ) ); //$NON-NLS-1$ + sb.append( module.getFile()); + sb.append( '\n' ); + + // Base address + String baseAddress = module.getBaseAddress(); + sb.append( ModulesMessages.getString( "ModulesView.BaseAddress" ) ); //$NON-NLS-1$ + sb.append( baseAddress ); + sb.append( '\n' ); + + // Size + long size = module.getSize(); + if ( size > 0 ) { + sb.append( ModulesMessages.getString( "ModulesView.Size" ) ); //$NON-NLS-1$ + sb.append( size ); + sb.append( '\n' ); + } + + return sb.toString(); + } + + + public class GetModuleDetailsQuery extends Query<Object> { + + private IModuleDMContext fDmc; + + public GetModuleDetailsQuery(IModuleDMContext dmc) { + super(); + fDmc = dmc; + } + + @Override + protected void execute(final DataRequestMonitor<Object> rm) { + /* + * We're in another dispatch, so we must guard against executor + * shutdown again. + */ + final DsfSession session = DsfSession.getSession(fDmc.getSessionId()); + if (session == null) { + cancel(false); + return; + } + + /* + * Guard against a disposed service + */ + DsfServicesTracker tracker = new DsfServicesTracker(DsfUIPlugin.getBundleContext(), fDmc.getSessionId()); + IModules service = tracker.getService(IModules.class); + tracker.dispose(); + if (service == null) { + rm .setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, "Service unavailable", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + service.getModuleData(fDmc, new DataRequestMonitor<IModuleDMData>( session.getExecutor(), rm) { + @Override + protected void handleCompleted() { + /* + * We're in another dispatch, so we must guard against executor shutdown again. + */ + if (!DsfSession.isSessionActive(session.getId())) { + GetModuleDetailsQuery.this.cancel(false); + return; + } + super.handleCompleted(); + } + + @Override + protected void handleSuccess() { + /* + * All good set return value. + */ + rm.setData(getData()); + rm.done(); + } + }); + } + } +} + diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/modules/detail/ModuleDetailPaneFactory.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/modules/detail/ModuleDetailPaneFactory.java new file mode 100644 index 00000000000..e3796b39a23 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/modules/detail/ModuleDetailPaneFactory.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 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: + * Ericsson AB - Modules view for DSF implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.ui.viewmodel.modules.detail; + +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.debug.ui.IDetailPane; +import org.eclipse.debug.ui.IDetailPaneFactory; +import org.eclipse.jface.viewers.IStructuredSelection; + +public class ModuleDetailPaneFactory implements IDetailPaneFactory { + public static final String MODULE_DETAIL_PANE_ID = ModuleDetailPane.ID; + public IDetailPane createDetailPane(String paneID) { + return new ModuleDetailPane(); + } + + public String getDefaultDetailPane(IStructuredSelection selection) { + return null; + } + + public String getDetailPaneDescription(String paneID) { + if (paneID.equals(ModuleDetailPane.ID)){ + return ModuleDetailPane.DESCRIPTION; + } + return null; + } + + public String getDetailPaneName(String paneID) { + if (paneID.equals(ModuleDetailPane.ID)){ + return ModuleDetailPane.NAME; + } + return null; + } + + public Set<?> getDetailPaneTypes(IStructuredSelection selection) { + Set<String> possibleIDs = new HashSet<String>(1); + possibleIDs.add(ModuleDetailPane.ID); + return possibleIDs; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/modules/detail/ModulesAbstractDetailPane.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/modules/detail/ModulesAbstractDetailPane.java new file mode 100644 index 00000000000..277cfd054db --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/modules/detail/ModulesAbstractDetailPane.java @@ -0,0 +1,172 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 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 - adopted to use with Modules view + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.ui.viewmodel.modules.detail; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.eclipse.debug.ui.IDetailPane; +import org.eclipse.jface.action.IAction; +import org.eclipse.ui.IViewSite; +import org.eclipse.ui.IWorkbenchPartSite; +import org.eclipse.ui.texteditor.IUpdate; + +/** + * Abstract class that holds common methods used by implementors of IDetailPane. + */ +public abstract class ModulesAbstractDetailPane implements IDetailPane { + + /** + * The <code>IWorkbenchPartSite</code> that the details area (and the + * variables view) belongs to. + */ + private IWorkbenchPartSite fWorkbenchPartSite; + + /** + * Map of actions. Keys are strings, values + * are <code>IAction</code>. + */ + private Map<String,IAction> fActionMap = new HashMap<String,IAction>(); + + /** + * Collection to track actions that should be updated when selection occurs. + */ + private List<String> fSelectionActions = new ArrayList<String>(); + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.IDetailPane#init(org.eclipse.ui.IWorkbenchPartSite) + */ + public void init(IWorkbenchPartSite workbench) { + fWorkbenchPartSite = workbench; + + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.IDetailPane#dispose() + */ + public void dispose() { + fActionMap.clear(); + fSelectionActions.clear(); + } + + /** + * Adds an action to the Map storing actions. Removes it if action is null. + * + * @param actionID The ID of the action, used as the key in the Map + * @param action The action associated with the ID + */ + protected void setAction(String actionID, IAction action) { + if (action == null) { + fActionMap.remove(actionID); + } else { + fActionMap.put(actionID, action); + } + } + + /** + * Adds the given action to the global action handler for the ViewSite. + * A call to <code>updateActionBars()</code> must be called after changes + * to propagate changes through the workbench. + * + * @param actionID The ID of the action + * @param action The action to be set globally + */ + protected void setGlobalAction(String actionID, IAction action){ + getViewSite().getActionBars().setGlobalActionHandler(actionID, action); + } + + /** + * Adds the given action to the list of actions that will be updated when + * <code>updateSelectionDependentActions()</code> is called. If the string + * is null it will not be added to the list. + * + * @param actionID The ID of the action which should be updated + */ + protected void setSelectionDependantAction(String actionID){ + if (actionID != null) fSelectionActions.add(actionID); + } + + /** + * Gets the action out of the map, casts it to an <code>IAction</code> + * + * @param actionID The ID of the action to find + * @return The action associated with the ID or null if none is found. + */ + protected IAction getAction(String actionID) { + return fActionMap.get(actionID); + } + + /** + * Calls the update method of the action with the given action ID. + * The action must exist in the action map and must be an instance of + * </code>IUpdate</code> + * + * @param actionId The ID of the action to update + */ + protected void updateAction(String actionId) { + IAction action= getAction(actionId); + if (action instanceof IUpdate) { + ((IUpdate) action).update(); + } + } + + /** + * Iterates through the list of selection dependent actions and + * updates them. Use <code>setSelectionDependentAction(String actionID)</code> + * to add an action to the list. The action must have been added to the known + * actions map by calling <code>setAction(String actionID, IAction action)</code> + * before it can be updated by this method. + */ + protected void updateSelectionDependentActions() { + Iterator<String> iterator= fSelectionActions.iterator(); + while (iterator.hasNext()) { + updateAction(iterator.next()); + } + } + + /** + * Gets the view site for this view. May be null if this detail pane + * is not part of a view. + * + * @return The site for this view or <code>null</code> + */ + protected IViewSite getViewSite(){ + if (fWorkbenchPartSite == null){ + return null; + } else { + return (IViewSite) fWorkbenchPartSite.getPart().getSite(); + } + } + + /** + * Gets the workbench part site for this view. May be null if this detail pane + * is not part of a view. + * + * @return The workbench part site or <code>null</code> + */ + protected IWorkbenchPartSite getWorkbenchPartSite() { + return fWorkbenchPartSite; + } + + /** + * Returns whether this detail pane is being displayed in a view with a workbench part site. + * + * @return whether this detail pane is being displayed in a view with a workbench part site. + */ + protected boolean isInView(){ + return fWorkbenchPartSite != null; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/modules/detail/ModulesMessages.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/modules/detail/ModulesMessages.java new file mode 100644 index 00000000000..a29cb586e50 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/modules/detail/ModulesMessages.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2004, 2008 QNX Software 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems, Inc. - extended implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.ui.viewmodel.modules.detail; + +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +/** + * Comment for . + */ +public class ModulesMessages { + + private static final String BUNDLE_NAME = "org.eclipse.cdt.dsf.debug.ui.viewmodel.modules.detail.ModulesMessages";//$NON-NLS-1$ + + private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle( BUNDLE_NAME ); + + private ModulesMessages() { + } + + public static String getString( String key ) { + try { + return RESOURCE_BUNDLE.getString( key ); + } + catch( MissingResourceException e ) { + return '!' + key + '!'; + } + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/modules/detail/ModulesMessages.properties b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/modules/detail/ModulesMessages.properties new file mode 100644 index 00000000000..14cb80d4921 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/modules/detail/ModulesMessages.properties @@ -0,0 +1,25 @@ +############################################################################### +# Copyright (c) 2005, 2008 QNX Software 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: +# QNX Software Systems - initial API and implementation +# Wind River Systems - adapted to work with platform Modules view (bug 210558) +############################################################################### +ModulesView.Executable=executable +ModulesView.SharedLibrary=shared library +ModulesView.Type=Type: +ModulesView.Symbols=Symbols: +ModulesView.Loaded=loaded +ModulesView.NotLoaded=not loaded +ModulesView.SymbolsFile=Symbols file: +ModulesView.CPU=CPU: +ModulesView.BaseAddress=Base address: +ModulesView.Size=Size: +ModulesView.SymbolsLoaded=\ (symbols loaded) +ModulesView.SymbolsNotLoaded=(symbols not loaded) +ModulesView.SelectAll=Select &All +ModulesView.Copy=&Copy diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/numberformat/FormattedValuePreferenceStore.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/numberformat/FormattedValuePreferenceStore.java new file mode 100644 index 00000000000..b0e78f1adac --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/numberformat/FormattedValuePreferenceStore.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * 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.numberformat; + +import org.eclipse.cdt.dsf.debug.service.IFormattedValues; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.IDebugVMConstants; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; + +/** + * Provides default implementation of preference storage. + */ +@SuppressWarnings("restriction") +public class FormattedValuePreferenceStore implements IFormattedValuePreferenceStore { + + private static IFormattedValuePreferenceStore fgSingletonReference; + + public static IFormattedValuePreferenceStore getDefault() { + if (fgSingletonReference == null) { + fgSingletonReference = new FormattedValuePreferenceStore(); + } + return fgSingletonReference; + } + + public String getCurrentNumericFormat( IPresentationContext context ) { + + Object prop = context.getProperty( IDebugVMConstants.CURRENT_FORMAT_STORAGE ); + + if ( prop != null ) { + return (String) prop; + } + return IFormattedValues.NATURAL_FORMAT; + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/numberformat/IFormattedValuePreferenceStore.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/numberformat/IFormattedValuePreferenceStore.java new file mode 100644 index 00000000000..8b6f7d6c41a --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/numberformat/IFormattedValuePreferenceStore.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * 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.numberformat; + +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; + +/** + * + */ + +@SuppressWarnings("restriction") +public interface IFormattedValuePreferenceStore { + /* + * Retrieves for the specified Presentation Context the configured format. + * + * @param context Specified Presentation Context + * @return Format ID. + */ + public String getCurrentNumericFormat( IPresentationContext context ); +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/numberformat/IFormattedValueVMContext.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/numberformat/IFormattedValueVMContext.java new file mode 100644 index 00000000000..561908d575d --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/numberformat/IFormattedValueVMContext.java @@ -0,0 +1,20 @@ +/******************************************************************************* + * 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.numberformat; + +import org.eclipse.cdt.dsf.ui.viewmodel.IVMContext; + +/** + * + */ +public interface IFormattedValueVMContext extends IVMContext { + IFormattedValuePreferenceStore getPreferenceStore(); +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/numberformat/MessagesForNumberFormat.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/numberformat/MessagesForNumberFormat.java new file mode 100644 index 00000000000..3db13710902 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/numberformat/MessagesForNumberFormat.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 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: + * Wind River Systems, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.debug.ui.viewmodel.numberformat; + +import org.eclipse.osgi.util.NLS; + +public class MessagesForNumberFormat extends NLS { + + private static final String BUNDLE_NAME = "org.eclipse.cdt.dsf.debug.ui.viewmodel.numberformat.messages"; //$NON-NLS-1$ + + public static String NumberFormatContribution_Natural_label; + public static String NumberFormatContribution_Decimal_label; + public static String NumberFormatContribution_Hex_label; + public static String NumberFormatContribution_Octal_label; + public static String NumberFormatContribution_Binary_label; + public static String NumberFormatContribution_String_label; + + public static String NumberFormatContribution_EmptyFormatsList_label; + + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, MessagesForNumberFormat.class); + } + + private MessagesForNumberFormat() {} +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/numberformat/NumberFormatsContribution.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/numberformat/NumberFormatsContribution.java new file mode 100644 index 00000000000..bda57b093c4 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/numberformat/NumberFormatsContribution.java @@ -0,0 +1,126 @@ +/******************************************************************************* + * Copyright (c) 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.numberformat; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.cdt.dsf.debug.service.IFormattedValues; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.IDebugVMConstants; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.actions.VMHandlerUtils; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.ActionContributionItem; +import org.eclipse.jface.action.ContributionItem; +import org.eclipse.jface.action.IContributionItem; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.swt.widgets.MenuItem; +import org.eclipse.ui.actions.CompoundContributionItem; +import org.eclipse.ui.menus.IWorkbenchContribution; +import org.eclipse.ui.services.IServiceLocator; + +/** + * Dynamic menu contribution that shows available number formats + * in the current view. + * + * @since 1.1 + */ +@SuppressWarnings("restriction") +public class NumberFormatsContribution extends CompoundContributionItem implements IWorkbenchContribution { + + private static final Map<String, String> FORMATS = new LinkedHashMap<String, String>(); + static { + FORMATS.put(IFormattedValues.NATURAL_FORMAT, MessagesForNumberFormat.NumberFormatContribution_Natural_label); + FORMATS.put(IFormattedValues.HEX_FORMAT, MessagesForNumberFormat.NumberFormatContribution_Hex_label); + FORMATS.put(IFormattedValues.DECIMAL_FORMAT, MessagesForNumberFormat.NumberFormatContribution_Decimal_label); + FORMATS.put(IFormattedValues.OCTAL_FORMAT, MessagesForNumberFormat.NumberFormatContribution_Octal_label); + FORMATS.put(IFormattedValues.BINARY_FORMAT, MessagesForNumberFormat.NumberFormatContribution_Binary_label); + FORMATS.put(IFormattedValues.STRING_FORMAT, MessagesForNumberFormat.NumberFormatContribution_String_label); + } + + private class SelectNumberFormatAction extends Action { + private final IPresentationContext fContext; + private final String fFormatId; + SelectNumberFormatAction(IPresentationContext context, String formatId) { + super(FORMATS.get(formatId), AS_RADIO_BUTTON); + fContext = context; + fFormatId = formatId; + } + + @Override + public void run() { + if (isChecked()) { + fContext.setProperty(IDebugVMConstants.CURRENT_FORMAT_STORAGE, fFormatId); + } + } + } + + private IServiceLocator fServiceLocator; + + private static IContributionItem[] NO_BREAKPOINT_TYPES_CONTRIBUTION_ITEMS = new IContributionItem[] { + new ContributionItem() { + @Override + public void fill(Menu menu, int index) { + MenuItem item = new MenuItem(menu, SWT.NONE); + item.setEnabled(false); + item.setText(MessagesForNumberFormat.NumberFormatContribution_EmptyFormatsList_label); + } + + @Override + public boolean isEnabled() { + return false; + } + } + }; + + @Override + protected IContributionItem[] getContributionItems() { + IVMProvider provider = VMHandlerUtils.getActiveVMProvider(fServiceLocator); + + // If no part or selection, disable all. + if (provider == null) { + return NO_BREAKPOINT_TYPES_CONTRIBUTION_ITEMS; + } + + IPresentationContext context = provider.getPresentationContext(); + Object activeId = context.getProperty(IDebugVMConstants.CURRENT_FORMAT_STORAGE); + if (activeId == null) { + activeId = IFormattedValues.NATURAL_FORMAT; + } + + List<Action> actions = new ArrayList<Action>(FORMATS.size()); + for (String formatId : FORMATS.keySet()) { + Action action = new SelectNumberFormatAction(context, formatId); + if (formatId.equals(activeId)) { + action.setChecked(true); + } + actions.add(action); + } + + if ( actions.isEmpty() ) { + return NO_BREAKPOINT_TYPES_CONTRIBUTION_ITEMS; + } + + IContributionItem[] items = new IContributionItem[actions.size()]; + for (int i = 0; i < actions.size(); i++) { + items[i] = new ActionContributionItem(actions.get(i)); + } + return items; + } + + public void initialize(IServiceLocator serviceLocator) { + fServiceLocator = serviceLocator; + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/numberformat/NumberFormatsPropertyTester.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/numberformat/NumberFormatsPropertyTester.java new file mode 100644 index 00000000000..7dc699bf345 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/numberformat/NumberFormatsPropertyTester.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright (c) 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.numberformat; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.cdt.dsf.debug.service.IFormattedValues; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.IDebugVMConstants; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.actions.VMHandlerUtils; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMContext; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMProvider; +import org.eclipse.core.expressions.PropertyTester; +import org.eclipse.debug.ui.IDebugView; +import org.eclipse.ui.IWorkbenchPart; + +/** + * Property tester for number format information available through the given + * object. The object being tested should be either an {@link IVMContext}, + * through which an instance of {@link IVMProvider} could be obtained. + * Or it could be an {@link IWorkbenchPart}, which is tested to see if it + * is a debug view through which a caching VM provider can be obtained. + * The view's presentation context is used to test the given property. + * <p> + * Three properties are supported: + * <ul> + * <li> "areNumberFormatsSupported" - Checks whether number formats are + * available at all given the receiver.</li> + * <li> "isNumberFormatAvailable" - Checks whether the number format ID in the + * expected value is available for the given receiver.</li> + * <li> "isNumberFormatActive" - Checks whether the number format ID in the expected + * value is the currently active number format for the given receiver.</li> + * </ul> + * </p> + */ +@SuppressWarnings("restriction") +public class NumberFormatsPropertyTester extends PropertyTester { + + private static final String SUPPORTED = "areNumberFormatsSupported"; //$NON-NLS-1$ + private static final String AVAILABLE = "isNumberFormatAvailable"; //$NON-NLS-1$ + private static final String ACTIVE = "isNumberFormatActive"; //$NON-NLS-1$ + + private static final List<String> AVAILABLE_FORMATS = new ArrayList<String>(); + static { + AVAILABLE_FORMATS.add(IFormattedValues.NATURAL_FORMAT); + AVAILABLE_FORMATS.add(IFormattedValues.HEX_FORMAT); + AVAILABLE_FORMATS.add(IFormattedValues.DECIMAL_FORMAT); + AVAILABLE_FORMATS.add(IFormattedValues.OCTAL_FORMAT); + AVAILABLE_FORMATS.add(IFormattedValues.BINARY_FORMAT); + AVAILABLE_FORMATS.add(IFormattedValues.STRING_FORMAT); + }; + + public boolean test(Object receiver, String property, Object[] args, Object expectedValue) { + if (receiver instanceof IVMContext) { + IVMProvider provider = ((IVMContext)receiver).getVMNode().getVMProvider(); + if (provider != null) { + return testProvider(provider, property, expectedValue); + } + } else if (receiver instanceof IDebugView) { + IVMProvider provider = VMHandlerUtils.getVMProviderForPart((IDebugView)receiver); + if (provider != null) { + return testProvider(provider, property, expectedValue); + } + } + return false; + } + + private boolean testProvider(IVMProvider provider, String property, Object expectedValue) { + if (SUPPORTED.equals(property)) { + return true; + } else if (AVAILABLE.equals(property)) { + return AVAILABLE_FORMATS.contains(expectedValue); + } else if (ACTIVE.equals(property)) { + Object activeId = provider.getPresentationContext().getProperty(IDebugVMConstants.CURRENT_FORMAT_STORAGE); + return expectedValue != null && expectedValue.equals(activeId); + } + return false; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/numberformat/messages.properties b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/numberformat/messages.properties new file mode 100644 index 00000000000..fbebfa748d8 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/numberformat/messages.properties @@ -0,0 +1,19 @@ +############################################################################### +# Copyright (c) 2006, 2008 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: +# Wind River Systems Inc - copied for non-restricted version for DSDP/DD/DSF +############################################################################### + +NumberFormatContribution_Natural_label=Natural +NumberFormatContribution_Decimal_label=Decimal +NumberFormatContribution_Hex_label=Hex +NumberFormatContribution_Octal_label=Octal +NumberFormatContribution_Binary_label=Binary +NumberFormatContribution_String_label=String + +NumberFormatContribution_EmptyFormatsList_label=Number formats not available
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/MessagesForRegisterVM.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/MessagesForRegisterVM.java new file mode 100644 index 00000000000..432b0f07d3d --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/MessagesForRegisterVM.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 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.register; + +import org.eclipse.osgi.util.NLS; + +public class MessagesForRegisterVM extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.cdt.dsf.debug.ui.viewmodel.register.messages"; //$NON-NLS-1$ + + public static String RegisterColumnPresentation_description; + + public static String RegisterColumnPresentation_name; + + public static String RegisterColumnPresentation_type; + + public static String RegisterColumnPresentation_value; + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, MessagesForRegisterVM.class); + } + + private MessagesForRegisterVM() { + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/RegisterBitFieldCellModifier.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/RegisterBitFieldCellModifier.java new file mode 100644 index 00000000000..48c8d7b471c --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/RegisterBitFieldCellModifier.java @@ -0,0 +1,199 @@ +/******************************************************************************* + * Copyright (c) 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.register; + +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues; +import org.eclipse.cdt.dsf.debug.service.IRegisters.IBitFieldDMContext; +import org.eclipse.cdt.dsf.debug.service.IRegisters.IBitFieldDMData; +import org.eclipse.cdt.dsf.debug.service.IRegisters.IMnemonic; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.IDebugVMConstants; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.expression.WatchExpressionCellModifier; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.numberformat.IFormattedValuePreferenceStore; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMContext; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; +import org.eclipse.cdt.dsf.ui.viewmodel.update.AbstractCachingVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.update.UserEditEvent; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; + +@SuppressWarnings("restriction") +public class RegisterBitFieldCellModifier extends WatchExpressionCellModifier { + + public static enum BitFieldEditorStyle { NOTHING, BITFIELDCOMBO, BITFIELDTEXT } + + private AbstractCachingVMProvider fProvider; + private BitFieldEditorStyle fStyle; + private IBitFieldDMData fBitFieldData = null; + private Object fElement = null; + private SyncRegisterDataAccess fDataAccess = null; + private IFormattedValuePreferenceStore fFormatPrefStore; + + public RegisterBitFieldCellModifier(AbstractCachingVMProvider provider, + IFormattedValuePreferenceStore formatPrefStore, BitFieldEditorStyle style, SyncRegisterDataAccess access ) + { + fProvider = provider; + fStyle = style; + fDataAccess = access; + fFormatPrefStore = formatPrefStore; + } + + /* + * Used to make sure we are dealing with a valid register. + */ + private IBitFieldDMContext getBitFieldDMC(Object element) { + if (element instanceof IDMVMContext) { + IDMContext dmc = ((IDMVMContext)element).getDMContext(); + return DMContexts.getAncestorOfType(dmc, IBitFieldDMContext.class); + } + return null; + } + + @Override + public boolean canModify(Object element, String property) { + + /* + * If we're in the column value, modify the register data. + * Otherwise, call the super-class to edit the watch expression. + */ + if ( IDebugVMConstants.COLUMN_ID__VALUE.equals(property) ) { + /* + * Make sure we are are dealing with a valid set of information. + */ + if ( getBitFieldDMC(element) == null ) return false; + + fElement = element; + + /* + * We need to read the register in order to get the attributes. + */ + fBitFieldData = fDataAccess.readBitField(element); + + if ( ( fBitFieldData != null ) && ( ! fBitFieldData.isWriteable() ) ) return false; + + return true ; + } else { + return super.canModify(element, property); + } + } + + @Override + public Object getValue(Object element, String property) { + /* + * If we're in the column value, modify the register data. + * Otherwise, call the super-class to edit the watch expression. + */ + if ( IDebugVMConstants.COLUMN_ID__VALUE.equals(property) ) { + /* + * Make sure we are working on the editable areas. + */ + if ( element != fElement ) return false; + + if ( fStyle == BitFieldEditorStyle.BITFIELDTEXT ) { + /* + * We let the Model provider supply the current format. + */ + String formatId; + + if ( element instanceof IVMContext) { + /* + * Find the presentation context and then use it to get the current desired format. + */ + IVMContext ctx = (IVMContext) element; + IPresentationContext presCtx = ctx.getVMNode().getVMProvider().getPresentationContext(); + + formatId = fFormatPrefStore.getCurrentNumericFormat(presCtx); + } + else { + formatId = IFormattedValues.NATURAL_FORMAT; + } + + String value = fDataAccess.getFormattedBitFieldValue(fElement, formatId); + + if ( value == null ) { value = "..."; } //$NON-NLS-1$ + + return value; + } + else { + /* + * This is a COMBO BOX. So we need to take the value of the bitfield and + * compare it to the associated mnemonic values to see which mnemonic is + * representing the current value. At this point the Bitfield Model data + * has already been established since the "canModify()" method is called + * first by the flexible hierarchy proxies. + */ + IMnemonic curMnemonic = fBitFieldData.getCurrentMnemonicValue(); + + int index = 0 ; + for ( IMnemonic mnemonic : fBitFieldData.getMnemonics() ) { + if ( mnemonic.equals( curMnemonic ) ) { + return new Integer( index ); + } + index ++; + } + + return null; + } + } else { + return super.getValue(element, property); + } + } + + @Override + public void modify(Object element, String property, Object value) { + /* + * If we're in the column value, modify the register data. + * Otherwise, call the super-class to edit the watch expression. + */ + if ( IDebugVMConstants.COLUMN_ID__VALUE.equals(property) ) { + if ( fStyle == BitFieldEditorStyle.BITFIELDTEXT ) { + if (value instanceof String) { + /* + * We let the Model provider supply the current format. + */ + String formatId; + + if ( element instanceof IVMContext) { + /* + * Find the presentation context and then use it to get the current desired format. + */ + IVMContext ctx = (IVMContext) element; + IPresentationContext presCtx = ctx.getVMNode().getVMProvider().getPresentationContext(); + + formatId = fFormatPrefStore.getCurrentNumericFormat(presCtx); + } + else { + formatId = IFormattedValues.NATURAL_FORMAT; + } + fDataAccess.writeBitField(element, (String) value, formatId); + fProvider.handleEvent(new UserEditEvent(element)); + } + } + else { + if (value instanceof Integer) { + /* + * Get the integer value corresponding to the selected entry. + */ + Integer val = (Integer) value; + + /* + * Write the bit field using the selected mnemonic. + */ + fDataAccess.writeBitField(element, fBitFieldData.getMnemonics()[val.intValue()]); + fProvider.handleEvent(new UserEditEvent(element)); + } + } + } else { + super.modify(element, property, value); + } + } +} + diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/RegisterBitFieldVMNode.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/RegisterBitFieldVMNode.java new file mode 100644 index 00000000000..0e96bf90d8a --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/RegisterBitFieldVMNode.java @@ -0,0 +1,901 @@ +/******************************************************************************* + * 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.register; + +import java.util.concurrent.RejectedExecutionException; + +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.IFormattedValues; +import org.eclipse.cdt.dsf.debug.service.IRegisters; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMContext; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMData; +import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryChangedEvent; +import org.eclipse.cdt.dsf.debug.service.IRegisters.IBitFieldChangedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRegisters.IBitFieldDMContext; +import org.eclipse.cdt.dsf.debug.service.IRegisters.IBitFieldDMData; +import org.eclipse.cdt.dsf.debug.service.IRegisters.IMnemonic; +import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterChangedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMContext; +import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMData; +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.numberformat.IFormattedValuePreferenceStore; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.numberformat.IFormattedValueVMContext; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.register.RegisterBitFieldCellModifier.BitFieldEditorStyle; +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.IVMContext; +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.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.model.IExpression; +import org.eclipse.debug.internal.ui.DebugPluginImages; +import org.eclipse.debug.internal.ui.DebugUIPlugin; +import org.eclipse.debug.internal.ui.IInternalDebugUIConstants; +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.IDebugUIConstants; +import org.eclipse.debug.ui.actions.IWatchExpressionFactoryAdapter2; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.jface.viewers.CellEditor; +import org.eclipse.jface.viewers.ComboBoxCellEditor; +import org.eclipse.jface.viewers.ICellModifier; +import org.eclipse.jface.viewers.TextCellEditor; +import org.eclipse.jface.viewers.TreePath; +import org.eclipse.swt.widgets.Composite; + +@SuppressWarnings("restriction") +public class RegisterBitFieldVMNode extends AbstractExpressionVMNode + implements IElementEditor, IElementLabelProvider, IElementMementoProvider +{ + protected class BitFieldVMC extends DMVMContext + implements IFormattedValueVMContext + { + private IExpression fExpression; + public BitFieldVMC(IDMContext dmc) { + super(dmc); + } + + public IFormattedValuePreferenceStore getPreferenceStore() { + return fFormattedPrefStore; + } + + public void setExpression(IExpression expression) { + fExpression = expression; + } + + @Override + @SuppressWarnings("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 BitFieldVMC && super.equals(other)) { + BitFieldVMC otherBitField = (BitFieldVMC)other; + return (otherBitField.fExpression == null && fExpression == null) || + (otherBitField.fExpression != null && otherBitField.fExpression.equals(fExpression)); + } + return false; + } + + @Override + public int hashCode() { + return super.hashCode() + (fExpression != null ? fExpression.hashCode() : 0); + } + } + + protected class BitFieldExpressionFactory implements IWatchExpressionFactoryAdapter2 { + + public boolean canCreateWatchExpression(Object element) { + return element instanceof BitFieldVMC; + } + + /** + * Expected format: GRP( GroupName ).REG( RegisterName ).BFLD( BitFieldname ) + */ + public String createWatchExpression(Object element) throws CoreException { + IRegisterGroupDMData groupData = getSyncRegisterDataAccess().getRegisterGroupDMData(element); + IRegisterDMData registerData = getSyncRegisterDataAccess().getRegisterDMData(element); + IBitFieldDMData bitFieldData = getSyncRegisterDataAccess().getBitFieldDMData(element); + + if (groupData != null && registerData != null && bitFieldData != null) { + StringBuffer exprBuf = new StringBuffer(); + + exprBuf.append("GRP( "); exprBuf.append(groupData.getName()); exprBuf.append(" )"); //$NON-NLS-1$ //$NON-NLS-2$ + exprBuf.append(".REG( "); exprBuf.append(registerData.getName()); exprBuf.append(" )"); //$NON-NLS-1$ //$NON-NLS-2$ + exprBuf.append(".BFLD( "); exprBuf.append(bitFieldData.getName()); exprBuf.append(" )"); //$NON-NLS-1$ //$NON-NLS-2$ + + return exprBuf.toString(); + } + + return null; + } + } + + private SyncRegisterDataAccess fSyncRegisterDataAccess = null; + protected IWatchExpressionFactoryAdapter2 fBitFieldExpressionFactory = null; + private final IFormattedValuePreferenceStore fFormattedPrefStore; + + public RegisterBitFieldVMNode(IFormattedValuePreferenceStore prefStore, AbstractDMVMProvider provider, DsfSession session, SyncRegisterDataAccess access) { + super(provider, session, IBitFieldDMContext.class); + fSyncRegisterDataAccess = access; + fFormattedPrefStore = prefStore; + } + + + @Override + public String toString() { + return "RegisterBitFieldVMNode(" + getSession().getId() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + } + + public IFormattedValuePreferenceStore getPreferenceStore() { + return fFormattedPrefStore; + } + + /** + * @since 1.1 + */ + public SyncRegisterDataAccess getSyncRegisterDataAccess() { + return fSyncRegisterDataAccess; + } + + /** + * @since 1.1 + */ + public IWatchExpressionFactoryAdapter2 getWatchExpressionFactory() { + if ( fBitFieldExpressionFactory == null ) { + fBitFieldExpressionFactory = new BitFieldExpressionFactory(); + } + return fBitFieldExpressionFactory; + } + + /** + * Private data access routine which performs the extra level of data access needed to + * get the formatted data value for a specific register. + */ + private void updateFormattedRegisterValue(final ILabelUpdate update, final int labelIndex, final IBitFieldDMContext dmc, final IBitFieldDMData data) + { + final IRegisters regService = getServicesTracker().getService(IRegisters.class); + + /* + * First select the format to be used. This involves checking so see that the preference + * page format is supported by the register service. If the format is not supported then + * we will pick the first available format. + */ + final IPresentationContext context = update.getPresentationContext(); + final String preferencePageFormatId = fFormattedPrefStore.getCurrentNumericFormat(context) ; + + regService.getAvailableFormats( + dmc, + new ViewerDataRequestMonitor<String[]>(getSession().getExecutor(), update) { + @Override + public void handleCompleted() { + if (!isSuccess()) { + handleFailedUpdate(update); + return; + } + + /* + * See if the desired format is supported. + */ + String[] formatIds = getData(); + String finalFormatId = IFormattedValues.HEX_FORMAT; + boolean requestedFormatIsSupported = false; + + for ( String fId : formatIds ) { + if ( preferencePageFormatId.equals(fId) ) { + /* + * Desired format is supported. + */ + finalFormatId = preferencePageFormatId; + requestedFormatIsSupported = true; + break; + } + } + + if ( ! requestedFormatIsSupported ) { + /* + * Desired format is not supported. If there are any formats supported + * then use the first available. + */ + if ( formatIds.length != 0 ) { + finalFormatId = formatIds[0]; + } + else { + /* + * Register service does not support any format. + */ + handleFailedUpdate(update); + return; + } + } + + /* + * Format has been validated. Get the formatted value. + */ + final FormattedValueDMContext valueDmc = regService.getFormattedValueContext(dmc, finalFormatId); + + getDMVMProvider().getModelData( + RegisterBitFieldVMNode.this, update, regService, valueDmc, + new ViewerDataRequestMonitor<FormattedValueDMData>(getSession().getExecutor(), update) { + @Override + public void handleCompleted() { + if (!isSuccess()) { + if (getStatus().getCode() == IDsfStatusConstants.INVALID_STATE) { + update.setLabel("...", labelIndex); //$NON-NLS-1$ + } else { + update.setLabel("Error: " + getStatus().getMessage(), labelIndex); //$NON-NLS-1$ + } + update.setFontData(JFaceResources.getFontDescriptor(IInternalDebugUIConstants.VARIABLE_TEXT_FONT).getFontData()[0], labelIndex); + update.done(); + return; + } + + /* + * Fill the label/column with the properly formatted data value. + */ + IMnemonic mnemonic = data.getCurrentMnemonicValue(); + if ( mnemonic != null ) { + String mnemstr = mnemonic.getLongName() + " - " + getData().getFormattedValue(); //$NON-NLS-1$ + update.setLabel(mnemstr , labelIndex); + } + else { + update.setLabel(getData().getFormattedValue() , labelIndex); + } + + update.setFontData(JFaceResources.getFontDescriptor(IInternalDebugUIConstants.VARIABLE_TEXT_FONT).getFontData()[0], labelIndex); + + // color based on change history + + FormattedValueDMData oldData = (FormattedValueDMData) getDMVMProvider().getArchivedModelData( + RegisterBitFieldVMNode.this, update, valueDmc); + if(oldData != null && !oldData.getFormattedValue().equals(getData().getFormattedValue())) { + update.setBackground( + DebugUIPlugin.getPreferenceColor(IInternalDebugUIConstants.PREF_CHANGED_VALUE_BACKGROUND).getRGB(), labelIndex); + } + update.done(); + } + }, + getExecutor() + ); + } + } + ); + } + + /* + * (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider#update(org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate[]) + */ + public void update(final ILabelUpdate[] updates) { + try { + getSession().getExecutor().execute(new DsfRunnable() { + public void run() { + updateLabelInSessionThread(updates); + }}); + } catch (RejectedExecutionException e) { + for (ILabelUpdate update : updates) { + handleFailedUpdate(update); + } + } + } + + /* + * Updates the requested label based on the specified column. + */ + protected void updateLabelInSessionThread(ILabelUpdate[] updates) { + for (final ILabelUpdate update : updates) { + + final IRegisters regService = getServicesTracker().getService(IRegisters.class); + + if ( regService == null ) { + handleFailedUpdate(update); + continue; + } + + final IBitFieldDMContext dmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), IBitFieldDMContext.class); + + getDMVMProvider().getModelData( + this, + update, + regService, + dmc, + new ViewerDataRequestMonitor<IBitFieldDMData>(getSession().getExecutor(), update) { + @Override + protected void handleCompleted() { + /* + * Check that the request was evaluated and data is still + * valid. The request could fail if the state of the + * service changed during the request, but the view model + * has not been updated yet. + */ + if (!isSuccess()) { + assert getStatus().isOK() || + getStatus().getCode() != IDsfStatusConstants.INTERNAL_ERROR || + getStatus().getCode() != IDsfStatusConstants.NOT_SUPPORTED; + /* + * Instead of just failing this outright we are going to attempt to do more here. + * Failing it outright causes the view to display ... for all columns in the line + * and this is uninformative about what is happening. We may be trying to show a + * register whos retrieval has been cancelled by the lower level. Perhaps because + * we are stepping extremely fast and state changes cause the register service to + * return these requests without ever sending them to the debug engine. + * + */ + String[] localColumns = update.getColumnIds(); + if (localColumns == null) + localColumns = new String[] { IDebugVMConstants.COLUMN_ID__NAME }; + + for (int idx = 0; idx < localColumns.length; idx++) { + if (IDebugVMConstants.COLUMN_ID__NAME.equals(localColumns[idx])) { + /* + * This used to be easy in that the DMC contained the name. Which allowed us + * to display the register name and an error message across from it. Now that + * name must come from the data and we could not retrieve the data we do not + * have anything intelligent to show here. I think this is going to look very + * ugly and will need to be worked on. We know the service has the name with + * it, it is just the dynamic part which cannot be obtained ( as explained in + * comments above ). + */ + update.setLabel("Unknown name", idx); //$NON-NLS-1$ + update.setImageDescriptor(DebugPluginImages.getImageDescriptor(IDebugUIConstants.IMG_OBJS_REGISTER), idx); + } else if (IDebugVMConstants.COLUMN_ID__TYPE.equals(localColumns[idx])) { + update.setLabel("", idx); //$NON-NLS-1$ + } else if (IDebugVMConstants.COLUMN_ID__VALUE.equals(localColumns[idx])) { + if (getStatus().getCode() == IDsfStatusConstants.INVALID_STATE) { + update.setLabel("...", idx); //$NON-NLS-1$ + } else { + update.setLabel("Error: " + getStatus().getMessage(), idx); //$NON-NLS-1$ + } + } else if (IDebugVMConstants.COLUMN_ID__DESCRIPTION.equals(localColumns[idx])) { + update.setLabel("...", idx); //$NON-NLS-1$ + } else if (IDebugVMConstants.COLUMN_ID__EXPRESSION.equals(localColumns[idx])) { + update.setLabel("", idx); //$NON-NLS-1$ + } + + update.setFontData(JFaceResources.getFontDescriptor(IInternalDebugUIConstants.VARIABLE_TEXT_FONT).getFontData()[0], idx); + } + + update.done(); + return; + } + + /* + * If columns are configured, extract the selected values for each + * understood column. First we fill all of those columns which can + * be filled without the extra data mining. We also note if we do + * have to datamine. Any columns need to set the processing flag + * so we know we have further work to do. If there are more columns + * which need data extraction they need to be added in both "for" + * loops. + */ + String[] localColumns = update.getColumnIds(); + if (localColumns == null) localColumns = new String[] { IDebugVMConstants.COLUMN_ID__NAME }; + + boolean weAreExtractingFormattedData = false; + + for (int idx = 0; idx < localColumns.length; idx++) { + if (IDebugVMConstants.COLUMN_ID__NAME.equals(localColumns[idx])) { + update.setLabel(getData().getName(), idx); + } else if (IDebugVMConstants.COLUMN_ID__VALUE.equals(localColumns[idx])) { + weAreExtractingFormattedData = true; + } else if (IDebugVMConstants.COLUMN_ID__TYPE.equals(localColumns[idx])) { + IBitFieldDMData data = getData(); + String typeStr = "Unsigned"; //$NON-NLS-1$ + String ReadAttrStr = "ReadNone"; //$NON-NLS-1$ + String WriteAddrStr = "WriteNone"; //$NON-NLS-1$ + + if ( data.isReadOnce() ) { ReadAttrStr = "ReadOnce"; } //$NON-NLS-1$ + else if ( data.isReadable() ) { ReadAttrStr = "Readable"; } //$NON-NLS-1$ + + if ( data.isReadOnce() ) { WriteAddrStr = "WriteOnce"; } //$NON-NLS-1$ + else if ( data.isReadable() ) { WriteAddrStr = "Writeable"; } //$NON-NLS-1$ + + typeStr += " - " + ReadAttrStr + "/" + WriteAddrStr; //$NON-NLS-1$ //$NON-NLS-2$ + update.setLabel(typeStr, idx); + } else if (IDebugVMConstants.COLUMN_ID__DESCRIPTION.equals(localColumns[idx])) { + update.setLabel(getData().getDescription(), idx); + } else if (IDebugVMConstants.COLUMN_ID__EXPRESSION.equals(localColumns[idx])) { + IVMContext vmc = (IVMContext)update.getElement(); + IExpression expression = (IExpression)vmc.getAdapter(IExpression.class); + if (expression != null) { + update.setLabel(expression.getExpressionText(), idx); + } else { + update.setLabel(getData().getName(), idx); + } + } + + update.setFontData(JFaceResources.getFontDescriptor(IInternalDebugUIConstants.VARIABLE_TEXT_FONT).getFontData()[0], idx); + } + + if ( ! weAreExtractingFormattedData ) { + update.done(); + } else { + for (int idx = 0; idx < localColumns.length; idx++) { + if (IDebugVMConstants.COLUMN_ID__VALUE.equals(localColumns[idx])) { + updateFormattedRegisterValue(update, idx, dmc, getData() ); + } + update.setFontData(JFaceResources.getFontDescriptor(IInternalDebugUIConstants.VARIABLE_TEXT_FONT).getFontData()[0], idx); + } + } + } + }, + getExecutor() + ); + } + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMNode#updateElementsInSessionThread(org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate) + */ + @Override + protected void updateElementsInSessionThread(final IChildrenUpdate update) { + final IRegisterDMContext regDmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), IRegisterDMContext.class); + + if (regDmc == null) { + handleFailedUpdate(update); + return; + } + + IRegisters regService = getServicesTracker().getService(IRegisters.class); + + if ( regService == null ) { + handleFailedUpdate(update); + return; + } + + + regService.getBitFields( + regDmc, + new ViewerDataRequestMonitor<IBitFieldDMContext[]>(getSession().getExecutor(), update) { + @Override + protected void handleFailure() { + handleFailedUpdate(update); + } + + @Override + protected void handleSuccess() { + fillUpdateWithVMCs(update, getData()); + update.done(); + } + }); + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMNode#createVMContext(org.eclipse.cdt.dsf.datamodel.IDMContext) + */ + @Override + protected IDMVMContext createVMContext(IDMContext dmc) { + return new BitFieldVMC(dmc); + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.ui.viewmodel.IVMNode#getDeltaFlags(java.lang.Object) + */ + public int getDeltaFlags(Object e) { + if ( e instanceof ISuspendedDMEvent || + e instanceof IMemoryChangedEvent || + e instanceof IRegisterChangedDMEvent || + (e instanceof PropertyChangeEvent && + ((PropertyChangeEvent)e).getProperty() == IDebugVMConstants.CURRENT_FORMAT_STORAGE) ) + { + return IModelDelta.CONTENT; + } + + if (e instanceof IBitFieldChangedDMEvent) { + return IModelDelta.STATE; + } + + return IModelDelta.NO_CHANGE; + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.ui.viewmodel.IVMNode#buildDelta(java.lang.Object, org.eclipse.cdt.dsf.ui.viewmodel.VMDelta, int, org.eclipse.cdt.dsf.concurrent.RequestMonitor) + */ + public void buildDelta(Object e, VMDelta parentDelta, int nodeOffset, RequestMonitor rm) { + // The following events can affect any bit field's values, + // refresh the contents of the parent element (i.e. all the registers). + if ( e instanceof ISuspendedDMEvent || + e instanceof IMemoryChangedEvent || + e instanceof IRegisterChangedDMEvent || + (e instanceof PropertyChangeEvent && + ((PropertyChangeEvent)e).getProperty() == IDebugVMConstants.CURRENT_FORMAT_STORAGE) ) + { + parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); + } + + if (e instanceof IBitFieldChangedDMEvent) { + // Create a delta indicating that the value of bit field has changed. + parentDelta.addNode( createVMContext(((IBitFieldChangedDMEvent)e).getDMContext()), IModelDelta.STATE ); + } + + rm.done(); + } + + /* + * (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__VALUE.equals(columnId)) { + /* + * In order to decide what kind of editor to present we need to know if there are + * mnemonics which can be used to represent the values. If there are then we will + * create a Combo editor for them. Otherwise we will just make a normal text cell + * editor. If there are bit groups then the modifier will check the size of the + * value being entered. + */ + IBitFieldDMData bitFieldData = getSyncRegisterDataAccess().readBitField(element); + + if ( bitFieldData != null && bitFieldData.isWriteable() ) { + + IMnemonic[] mnemonics = bitFieldData.getMnemonics(); + + if ( mnemonics != null && mnemonics.length != 0 ) { + + /* + * Create the list of readable dropdown selections. + */ + String[] StringValues = new String[ mnemonics.length ]; + + int idx = 0 ; + for ( IMnemonic mnemonic : mnemonics ) { + StringValues[ idx ++ ] = mnemonic.getLongName(); + } + + /* + * Not we are complex COMBO and return the right editor. + */ + return new ComboBoxCellEditor(parent, StringValues); + } + else { + /* + * Text editor even if we need to clamp the value entered. + */ + return new TextCellEditor(parent); + } + } + } else 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) { + + /* + * In order to decide what kind of modifier to present we need to know if there + * are mnemonics which can be used to represent the values. + */ + IBitFieldDMData bitFieldData = getSyncRegisterDataAccess().readBitField(element); + + if ( bitFieldData != null && bitFieldData.isWriteable() ) { + + IMnemonic[] mnemonics = bitFieldData.getMnemonics(); + + if ( mnemonics != null && mnemonics.length != 0 ) { + /* + * Note we are complex COMBO and return the right editor. + */ + return new RegisterBitFieldCellModifier( + getDMVMProvider(), fFormattedPrefStore, BitFieldEditorStyle.BITFIELDCOMBO, getSyncRegisterDataAccess() ); + } + else { + /* + * Text editor even if we need to clamp the value entered. + */ + return new RegisterBitFieldCellModifier( + getDMVMProvider(), fFormattedPrefStore, BitFieldEditorStyle.BITFIELDTEXT, getSyncRegisterDataAccess() ); + } + } + else { + return null; + } + } + + /** + * Expected format: GRP( GroupName ).REG( RegisterName ).BFLD( BitFieldname ) + */ + + public boolean canParseExpression(IExpression expression) { + return parseExpressionForBitFieldName(expression.getExpressionText()) != null; + } + + private String parseExpressionForBitFieldName(String expression) { + + if (expression.startsWith("GRP(")) { //$NON-NLS-1$ + + /* + * Get the group portion. + */ + int startIdx = "GRP(".length(); //$NON-NLS-1$ + int endIdx = expression.indexOf(')', startIdx); + if ( startIdx == -1 || endIdx == -1 ) { + return null; + } + String remaining = expression.substring(endIdx+1); + if ( ! remaining.startsWith(".REG(") ) { //$NON-NLS-1$ + return null; + } + + /* + * Get the register portion. + */ + startIdx = ".REG(".length(); //$NON-NLS-1$ + endIdx = remaining.indexOf(')', startIdx); + if ( startIdx == -1 || endIdx == -1 ) { + return null; + } + remaining = remaining.substring(endIdx+1); + + /* + * Get the bit-field portion. + */ + if ( ! remaining.startsWith(".BFLD(") ) { //$NON-NLS-1$ + return null; + } + startIdx = ".BFLD(".length(); //$NON-NLS-1$ + endIdx = remaining.indexOf(')', startIdx); + if ( startIdx == -1 || endIdx == -1 ) { + return null; + } + String bitFieldName = remaining.substring(startIdx, endIdx); + + /* + * Make sure there is nothing following. If there is then this + * is not a properly formed expression and we do not claim it. + */ + remaining = remaining.substring( endIdx + 1); + + if ( remaining.length() != 0 ) { + return null; + } + + return bitFieldName.trim(); + } + + return null; + } + + /* + * (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 IBitFieldDMContext dmc = DMContexts.getAncestorOfType(((IDMVMContext)element).getDMContext(), IBitFieldDMContext.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 bitFieldName = parseExpressionForBitFieldName(expression.getExpressionText()); + try { + getSession().getExecutor().execute(new DsfRunnable() { + public void run() { + IRegisters registersService = getServicesTracker().getService(IRegisters.class); + if (registersService != null) { + registersService.getBitFieldData( + dmc, + new DataRequestMonitor<IBitFieldDMData>(ImmediateExecutor.getInstance(), rm) { + @Override + protected void handleSuccess() { + rm.setData( getData().getName().equals(bitFieldName) ); + 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 BitFieldVMC) { + ((BitFieldVMC)element).setExpression(expression); + } + } + + /* + * (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) { + return IModelDelta.CONTENT; + } + + if (event instanceof PropertyChangeEvent && + ((PropertyChangeEvent)event).getProperty() == IDebugVMConstants.CURRENT_FORMAT_STORAGE) { + return IModelDelta.CONTENT; + } + + if (event instanceof IMemoryChangedEvent) { + return IModelDelta.CONTENT; + } + + 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(final IExpression expression, final int elementIdx, final Object event, final VMDelta parentDelta, final TreePath path, final RequestMonitor rm) + { + // Always refresh the contents of the view upon suspended event. + if (event instanceof ISuspendedDMEvent) { + 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) + { + // The following events can affect register values, refresh the state + // of the expression. + if ( event instanceof IRegisterChangedDMEvent || + event instanceof IMemoryChangedEvent || + (event instanceof PropertyChangeEvent && + ((PropertyChangeEvent)event).getProperty() == IDebugVMConstants.CURRENT_FORMAT_STORAGE) ) + { + parentDelta.addNode(element, IModelDelta.STATE); + } + + rm.done(); + } + + /* + * (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 = "BITFIELD_MEMENTO_NAME"; //$NON-NLS-1$ + + public void compareElements(IElementCompareRequest[] requests) { + for ( final IElementCompareRequest request : requests ) { + final String mementoName = request.getMemento().getString(MEMENTO_NAME); + + final IBitFieldDMContext regDmc = findDmcInPath(request.getViewerInput(), request.getElementPath(), IBitFieldDMContext.class); + 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.getBitFieldData( + regDmc, + new DataRequestMonitor<IBitFieldDMData>(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( "BitField." + 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 IBitFieldDMContext regDmc = findDmcInPath(request.getViewerInput(), request.getElementPath(), IBitFieldDMContext.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.getBitFieldData( + regDmc, + new DataRequestMonitor<IBitFieldDMData>(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, "BitField." + getData().getName()); //$NON-NLS-1$ + } + request.done(); + } + }); + } else { + request.done(); + } + } + }); + } catch (RejectedExecutionException e) { + request.done(); + } + } + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/RegisterCellModifier.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/RegisterCellModifier.java new file mode 100644 index 00000000000..60b6e8d92f6 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/RegisterCellModifier.java @@ -0,0 +1,158 @@ +/******************************************************************************* + * 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.register; + +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues; +import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMContext; +import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMData; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.IDebugVMConstants; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.expression.WatchExpressionCellModifier; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.numberformat.IFormattedValuePreferenceStore; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMContext; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; +import org.eclipse.cdt.dsf.ui.viewmodel.update.AbstractCachingVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.update.UserEditEvent; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; + +@SuppressWarnings("restriction") +public class RegisterCellModifier extends WatchExpressionCellModifier { + + private AbstractCachingVMProvider fProvider; + private SyncRegisterDataAccess fDataAccess = null; + private IFormattedValuePreferenceStore fFormattedValuePreferenceStore; + + public RegisterCellModifier(AbstractCachingVMProvider provider, + IFormattedValuePreferenceStore formattedValuePreferenceStore, SyncRegisterDataAccess access) + { + fProvider = provider; + fDataAccess = access; + fFormattedValuePreferenceStore = formattedValuePreferenceStore; + } + + public SyncRegisterDataAccess getRegisterDataAccess() { + return fDataAccess; + } + + public IFormattedValuePreferenceStore getPreferenceStore() { + return fFormattedValuePreferenceStore; + } + /* + * Used to make sure we are dealing with a valid register. + */ + protected IRegisterDMContext getRegisterDMC(Object element) { + if (element instanceof IDMVMContext) { + IDMContext dmc = ((IDMVMContext)element).getDMContext(); + return DMContexts.getAncestorOfType(dmc, IRegisterDMContext.class); + } + return null; + } + + @Override + public boolean canModify(Object element, String property) { + + /* + * If we're in the column value, modify the register data. + * Otherwise, call the super-class to edit the watch expression. + */ + if (IDebugVMConstants.COLUMN_ID__VALUE.equals(property)) { + /* + * Make sure we are are dealing with a valid set of information. + */ + if ( getRegisterDMC(element) == null ) return false; + + /* + * We need to read the register in order to get the attributes. + */ + + IRegisterDMData regData = fDataAccess.readRegister(element); + + if ( ( regData != null ) && ( ! regData.isWriteable() ) ) return false; + + return true ; + } else { + return super.canModify(element, property); + } + } + + @Override + public Object getValue(Object element, String property) { + /* + * If we're in the column value, modify the register data. + * Otherwise, call the super-class to edit the watch expression. + */ + if ( IDebugVMConstants.COLUMN_ID__VALUE.equals(property) ) { + /* + * We let the Model provider supply the current format. + */ + String formatId; + + if ( element instanceof IVMContext) { + /* + * Find the presentation context and then use it to get the current desired format. + */ + IVMContext ctx = (IVMContext) element; + IPresentationContext presCtx = ctx.getVMNode().getVMProvider().getPresentationContext(); + + formatId = fFormattedValuePreferenceStore.getCurrentNumericFormat(presCtx); + } + else { + formatId = IFormattedValues.NATURAL_FORMAT; + } + + String value = + + fDataAccess.getFormattedRegisterValue(element, formatId); + + if ( value == null ) { return "..."; } //$NON-NLS-1$ + else { return value; } + } else { + return super.getValue(element, property); + } + } + + @Override + public void modify(Object element, String property, Object value) { + /* + * If we're in the column value, modify the register data. + * Otherwise, call the super-class to edit the watch expression. + */ + + if ( IDebugVMConstants.COLUMN_ID__VALUE.equals(property) ) { + + if (value instanceof String) { + /* + * We let the Model provider supply the current format. + */ + String formatId; + + if ( element instanceof IVMContext) { + /* + * Find the presentation context and then use it to get the current desired format. + */ + IVMContext ctx = (IVMContext) element; + IPresentationContext presCtx = ctx.getVMNode().getVMProvider().getPresentationContext(); + + formatId = fFormattedValuePreferenceStore.getCurrentNumericFormat(presCtx); + } + else { + formatId = IFormattedValues.NATURAL_FORMAT; + } + + fDataAccess.writeRegister(element, (String) value, formatId); + fProvider.handleEvent(new UserEditEvent(element)); + } + } else { + super.modify(element, property, value); + } + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/RegisterColumnPresentation.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/RegisterColumnPresentation.java new file mode 100644 index 00000000000..03ef9ee3ff5 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/RegisterColumnPresentation.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * 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.register; + +import org.eclipse.cdt.dsf.debug.ui.viewmodel.IDebugVMConstants; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.jface.resource.ImageDescriptor; + +/** + * + */ +@SuppressWarnings("restriction") +public class RegisterColumnPresentation implements IColumnPresentation { + + public static final String ID = DsfUIPlugin.PLUGIN_ID + ".REGISTERS_COLUMN_PRESENTATION_ID"; //$NON-NLS-1$ + + public void init(IPresentationContext context) { + } + + public void dispose() { + } + + // @see org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentation#getAvailableColumns() + public String[] getAvailableColumns() { + return new String[] { IDebugVMConstants.COLUMN_ID__NAME, IDebugVMConstants.COLUMN_ID__TYPE, IDebugVMConstants.COLUMN_ID__VALUE, IDebugVMConstants.COLUMN_ID__DESCRIPTION, }; + } + + // @see org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentation#getHeader(java.lang.String) + public String getHeader(String id) { + if (IDebugVMConstants.COLUMN_ID__NAME.equals(id)) { + return MessagesForRegisterVM.RegisterColumnPresentation_name; + } else if (IDebugVMConstants.COLUMN_ID__TYPE.equals(id)) { + return MessagesForRegisterVM.RegisterColumnPresentation_type; + } else if (IDebugVMConstants.COLUMN_ID__VALUE.equals(id)) { + return MessagesForRegisterVM.RegisterColumnPresentation_value; + } else if (IDebugVMConstants.COLUMN_ID__DESCRIPTION.equals(id)) { + return MessagesForRegisterVM.RegisterColumnPresentation_description; + } + return null; + } + + // @see org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentation#getId() + public String getId() { + return ID; + } + + public ImageDescriptor getImageDescriptor(String id) { + return null; + } + + + + // @see org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentation#getInitialColumns() + public String[] getInitialColumns() { + return new String[] { IDebugVMConstants.COLUMN_ID__NAME, IDebugVMConstants.COLUMN_ID__VALUE, IDebugVMConstants.COLUMN_ID__DESCRIPTION }; + } + + // @see org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentation#isOptional() + public boolean isOptional() { + return true; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/RegisterGroupVMNode.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/RegisterGroupVMNode.java new file mode 100644 index 00000000000..0d6fc7b2c9e --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/RegisterGroupVMNode.java @@ -0,0 +1,586 @@ +/******************************************************************************* + * 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.register; + +import java.util.concurrent.RejectedExecutionException; + +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.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.IVMContext; +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.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.model.IExpression; +import org.eclipse.debug.internal.ui.DebugPluginImages; +import org.eclipse.debug.internal.ui.IInternalDebugUIConstants; +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.IDebugUIConstants; +import org.eclipse.debug.ui.actions.IWatchExpressionFactoryAdapter2; +import org.eclipse.jface.resource.JFaceResources; +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; + +@SuppressWarnings("restriction") +public class RegisterGroupVMNode extends AbstractExpressionVMNode + implements IElementEditor, IElementLabelProvider, IElementMementoProvider +{ + protected class RegisterGroupVMC extends DMVMContext + { + private IExpression fExpression; + public RegisterGroupVMC(IDMContext dmc) { + super(dmc); + } + + public void setExpression(IExpression expression) { + fExpression = expression; + } + + @Override + @SuppressWarnings("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(); + + public RegisterGroupVMNode(AbstractDMVMProvider provider, DsfSession session, SyncRegisterDataAccess syncDataAccess) { + super(provider, session, IRegisterGroupDMContext.class); + 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; + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMNode#updateElementsInSessionThread(org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate) + */ + @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(); + }}); + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMNode#createVMContext(org.eclipse.cdt.dsf.datamodel.IDMContext) + */ + @Override + protected IDMVMContext createVMContext(IDMContext dmc) { + return new RegisterGroupVMC(dmc); + } + + /* + * (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider#update(org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate[]) + */ + public void update(final ILabelUpdate[] updates) { + try { + getSession().getExecutor().execute(new DsfRunnable() { + public void run() { + updateLabelInSessionThread(updates); + }}); + } catch (RejectedExecutionException e) { + for (ILabelUpdate update : updates) { + handleFailedUpdate(update); + } + } + } + + /* + * Updates the labels with the required information for each visible column. + */ + protected void updateLabelInSessionThread(ILabelUpdate[] updates) { + for (final ILabelUpdate update : updates) { + + final IRegisterGroupDMContext dmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), IRegisterGroupDMContext.class); + if ( dmc == null ) { + handleFailedUpdate(update); + continue; + } + + IRegisters regService = getServicesTracker().getService(IRegisters.class); + if ( regService == null ) { + handleFailedUpdate(update); + continue; + } + + getDMVMProvider().getModelData( + this, + update, + regService, + dmc, + new ViewerDataRequestMonitor<IRegisterGroupDMData>(getSession().getExecutor(), update) { + @Override + protected void handleCompleted() { + /* + * Check that the request was evaluated and data is still + * valid. The request could fail if the state of the + * service changed during the request, but the view model + * has not been updated yet. + */ + if (!isSuccess()) { + assert getStatus().isOK() || + getStatus().getCode() != IDsfStatusConstants.INTERNAL_ERROR || + getStatus().getCode() != IDsfStatusConstants.NOT_SUPPORTED; + handleFailedUpdate(update); + return; + } + + /* + * If columns are configured, call the protected methods to + * fill in column values. + */ + String[] localColumns = update.getColumnIds(); + if (localColumns == null) localColumns = new String[] { null }; + + for (int i = 0; i < localColumns.length; i++) { + fillColumnLabel(dmc, getData(), localColumns[i], i, update); + } + update.done(); + } + }, + getExecutor()); + } + } + + /* + * Based on the specified visible column, provide the appropriate value/label. + */ + protected void fillColumnLabel(IRegisterGroupDMContext dmContext, IRegisterGroupDMData dmData, + String columnId, int idx, ILabelUpdate update) + { + update.setFontData(JFaceResources.getFontDescriptor(IInternalDebugUIConstants.VARIABLE_TEXT_FONT).getFontData()[0], idx); + + if (IDebugVMConstants.COLUMN_ID__NAME.equals(columnId)) { + update.setLabel(dmData.getName(), idx); + update.setImageDescriptor(DebugPluginImages.getImageDescriptor(IDebugUIConstants.IMG_OBJS_REGISTER_GROUP), idx); + } else if (IDebugVMConstants.COLUMN_ID__VALUE.equals(columnId)) { + update.setLabel("", idx); //$NON-NLS-1$ + } else if (IDebugVMConstants.COLUMN_ID__DESCRIPTION.equals(columnId)) { + update.setLabel(dmData.getDescription(), idx); + } else if (IDebugVMConstants.COLUMN_ID__TYPE.equals(columnId)) { + update.setLabel("", idx); //$NON-NLS-1$ + } else if (IDebugVMConstants.COLUMN_ID__EXPRESSION.equals(columnId)) { + IVMContext vmc = (IVMContext)update.getElement(); + IExpression expression = (IExpression)vmc.getAdapter(IExpression.class); + if (expression != null) { + update.setLabel(expression.getExpressionText(), idx); + } else { + update.setLabel(dmData.getName(), idx); + } + } + else if ( columnId == null ) { + /* + * If the Column ID comes in as "null" then this is the case where the user has decided + * to not have any columns. So we need a default action which makes the most sense and + * is doable. In this case we elect to simply display the name. + */ + update.setLabel(dmData.getName(), idx); + update.setImageDescriptor(DebugPluginImages.getImageDescriptor(IDebugUIConstants.IMG_OBJS_REGISTER_GROUP), idx); + } + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.ui.viewmodel.IVMNode#getDeltaFlags(java.lang.Object) + */ + 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; + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.ui.viewmodel.IVMNode#buildDelta(java.lang.Object, org.eclipse.cdt.dsf.ui.viewmodel.VMDelta, int, org.eclipse.cdt.dsf.concurrent.RequestMonitor) + */ + 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(); + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.debug.ui.viewmodel.expression.IExpressionVMNode#canParseExpression(org.eclipse.debug.core.model.IExpression) + */ + 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(); + } + } + } +}
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/RegisterRootDMVMNode.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/RegisterRootDMVMNode.java new file mode 100644 index 00000000000..316a0477d2f --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/RegisterRootDMVMNode.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 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.register; + +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.RootDMVMNode; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementCompareRequest; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoRequest; + +/* + * We are extending the ROOT VM node for the register view so we can + * provide Memento providers for the root node. In the Register VM + * Provider we are returning a pseudo VMContext selection when the + * original input is a child of an execution context we return a selection + * which represents an Execution Context instead. This ensures that the + * Register View does not collapse and redraw when going from frame to frame + * when stepping or just when selecting within the view. + */ +@SuppressWarnings("restriction") +public class RegisterRootDMVMNode extends RootDMVMNode implements IElementMementoProvider { + + public RegisterRootDMVMNode(AbstractVMProvider provider) { + super(provider); + } + + /* + * (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoProvider#compareElements(org.eclipse.debug.internal.ui.viewers.model.provisional.IElementCompareRequest[]) + */ + public void compareElements(IElementCompareRequest[] requests) { + for ( IElementMementoRequest request : requests ) { 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 ( IElementMementoRequest request : requests ) { request.done(); } + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/RegisterVMNode.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/RegisterVMNode.java new file mode 100644 index 00000000000..c48ec9590b9 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/RegisterVMNode.java @@ -0,0 +1,960 @@ +/******************************************************************************* + * 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.register; + +import java.util.ArrayList; +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +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.IFormattedValues; +import org.eclipse.cdt.dsf.debug.service.IRegisters; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMContext; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMData; +import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryChangedEvent; +import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterChangedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMContext; +import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMData; +import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterGroupDMData; +import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegistersChangedDMEvent; +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.numberformat.IFormattedValuePreferenceStore; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.numberformat.IFormattedValueVMContext; +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.IVMContext; +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.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.model.IExpression; +import org.eclipse.debug.internal.ui.DebugPluginImages; +import org.eclipse.debug.internal.ui.DebugUIPlugin; +import org.eclipse.debug.internal.ui.IInternalDebugUIConstants; +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.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.IPresentationContext; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.debug.ui.actions.IWatchExpressionFactoryAdapter2; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.util.PropertyChangeEvent; +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; + +@SuppressWarnings("restriction") +public class RegisterVMNode extends AbstractExpressionVMNode + implements IElementEditor, IElementLabelProvider, IElementMementoProvider +{ + protected class RegisterVMC extends DMVMContext + implements IFormattedValueVMContext + { + private IExpression fExpression; + public RegisterVMC(IDMContext dmc) { + super(dmc); + } + + public void setExpression(IExpression expression) { + fExpression = expression; + } + + @Override + @SuppressWarnings("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 RegisterVMC && super.equals(other)) { + RegisterVMC otherReg = (RegisterVMC)other; + return (otherReg.fExpression == null && fExpression == null) || + (otherReg.fExpression != null && otherReg.fExpression.equals(fExpression)); + } + return false; + } + + @Override + public int hashCode() { + return super.hashCode() + (fExpression != null ? fExpression.hashCode() : 0); + } + + public IFormattedValuePreferenceStore getPreferenceStore() { + return fFormattedPrefStore; + } + } + + protected class RegisterExpressionFactory implements IWatchExpressionFactoryAdapter2 { + + public boolean canCreateWatchExpression(Object element) { + return element instanceof RegisterVMC; + } + + /** + * Expected format: GRP( GroupName ).REG( RegisterName ) + */ + public String createWatchExpression(Object element) throws CoreException { + IRegisterGroupDMData groupData = getSyncRegisterDataAccess().getRegisterGroupDMData(element); + IRegisterDMData registerData = getSyncRegisterDataAccess().getRegisterDMData(element); + + if (groupData != null && registerData != null) { + StringBuffer exprBuf = new StringBuffer(); + + exprBuf.append("GRP( "); exprBuf.append(groupData.getName()); exprBuf.append(" )"); //$NON-NLS-1$ //$NON-NLS-2$ + exprBuf.append(".REG( "); exprBuf.append(registerData.getName()); exprBuf.append(" )"); //$NON-NLS-1$ //$NON-NLS-2$ + + return exprBuf.toString(); + } + + return null; + } + } + + private IWatchExpressionFactoryAdapter2 fRegisterExpressionFactory = null; + final private SyncRegisterDataAccess fSyncRegisterDataAccess; + private final IFormattedValuePreferenceStore fFormattedPrefStore; + + public RegisterVMNode(IFormattedValuePreferenceStore prefStore, AbstractDMVMProvider provider, DsfSession session, SyncRegisterDataAccess syncDataAccess) { + super(provider, session, IRegisterDMContext.class); + fSyncRegisterDataAccess = syncDataAccess; + fFormattedPrefStore = prefStore; + } + + @Override + public String toString() { + return "RegisterVMNode(" + getSession().getId() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + } + + protected SyncRegisterDataAccess getSyncRegisterDataAccess() { + return fSyncRegisterDataAccess; + } + + public IFormattedValuePreferenceStore getPreferenceStore() { + return fFormattedPrefStore; + } + + /** + * @since 1.1 + */ + public IWatchExpressionFactoryAdapter2 getWatchExpressionFactory() { + if ( fRegisterExpressionFactory == null ) { + fRegisterExpressionFactory = new RegisterExpressionFactory(); + } + return fRegisterExpressionFactory; + } + + /* + * This class is used to hold the associated information needed to finally get the + * formatted value for a register DMC. It starts out with the basic set sans the + * actual formatted register DMC. Once found this is added to the information. + */ + private class QueuedValueUpdate { + + ILabelUpdate fUpdate; + int fIndex ; + IRegisterDMContext fDmc; + FormattedValueDMContext fValueDmc = null; + + public QueuedValueUpdate( ILabelUpdate update, int index , IRegisterDMContext dmc ) { + fUpdate = update; + fIndex = index; + fDmc = dmc; + } + + public ILabelUpdate getUpdate() { return fUpdate; } + public int getIndex() { return fIndex; } + public IRegisterDMContext getDmc() { return fDmc; } + + public void setValueDmc( FormattedValueDMContext dmc ) { fValueDmc = dmc; } + public FormattedValueDMContext getValueDmc() { return fValueDmc; } + } + + private void retrieveAllFormattedDataValues( final ArrayList<QueuedValueUpdate> updates ) { + + final IRegisters regService = getServicesTracker().getService(IRegisters.class); + if ( regService == null ) { + for ( final QueuedValueUpdate up : updates ) { + handleFailedUpdate(up.getUpdate()); + } + return; + } + + for ( final QueuedValueUpdate up : updates ) { + + final ILabelUpdate update = up.getUpdate(); + final FormattedValueDMContext valueDmc = up.getValueDmc(); + + /* + * It is possible that we could not get a formatted DMC. In this case the setup + * logic puts a null as the value. So in this case we just complete this one + * with nothing. + */ + if ( valueDmc == null ) { + update.done(); + continue; + } + + final int idx = up.getIndex(); + + getDMVMProvider().getModelData( + RegisterVMNode.this, + update, + regService, + valueDmc, + new ViewerDataRequestMonitor<FormattedValueDMData>(getSession().getExecutor(), update) { + @Override + public void handleCompleted() { + if (!isSuccess()) { + if (getStatus().getCode() == IDsfStatusConstants.INVALID_STATE) { + update.setLabel("...", idx); //$NON-NLS-1$ + } else { + update.setLabel("Error: " + getStatus().getMessage(), idx); //$NON-NLS-1$ + } + update.setFontData(JFaceResources.getFontDescriptor(IInternalDebugUIConstants.VARIABLE_TEXT_FONT).getFontData()[0], idx); + update.done(); + return; + } + /* + * Fill the label/column with the properly formatted data value. + */ + update.setLabel(getData().getFormattedValue(), idx); + + // color based on change history + FormattedValueDMData oldData = (FormattedValueDMData) getDMVMProvider().getArchivedModelData(RegisterVMNode.this, update, valueDmc); + if(oldData != null && !oldData.getFormattedValue().equals(getData().getFormattedValue())) { + update.setBackground(DebugUIPlugin.getPreferenceColor(IInternalDebugUIConstants.PREF_CHANGED_VALUE_BACKGROUND).getRGB(), idx); + } + update.setFontData(JFaceResources.getFontDescriptor(IInternalDebugUIConstants.VARIABLE_TEXT_FONT).getFontData()[0], idx); + update.done(); + } + }, + getSession().getExecutor() + ); + } + } + + /** + * Private data access routine which performs the extra level of data access needed to + * get the formatted data value for a specific register. + */ + private void getFormattedDmcForReqister( final ILabelUpdate update, final IRegisterDMContext dmc, final DataRequestMonitor<FormattedValueDMContext> rm) + { + final IRegisters regService = getServicesTracker().getService(IRegisters.class); + if ( regService == null ) { + rm.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED, "", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + /* + * First select the format to be used. This involves checking so see that the preference + * page format is supported by the register service. If the format is not supported then + * we will pick the first available format. + */ + final IPresentationContext context = update.getPresentationContext(); + final String preferencePageFormatId = getPreferenceStore().getCurrentNumericFormat(context) ; + + regService.getAvailableFormats( + dmc, + new DataRequestMonitor<String[]>(getSession().getExecutor(), rm) { + @Override + public void handleSuccess() { + /* + * See if the desired format is supported. + */ + String[] formatIds = getData(); + String finalFormatId = IFormattedValues.NATURAL_FORMAT; + boolean requestedFormatIsSupported = false; + + for ( String fId : formatIds ) { + if ( preferencePageFormatId.equals(fId) ) { + /* + * Desired format is supported. + */ + finalFormatId = preferencePageFormatId; + requestedFormatIsSupported = true; + break; + } + } + + if ( ! requestedFormatIsSupported ) { + /* + * Desired format is not supported. If there are any formats supported + * then use the first available. + */ + if ( formatIds.length != 0 ) { + finalFormatId = formatIds[0]; + } + else { + /* + * Register service does not support any format. + */ + handleFailure(); + return; + } + } + + /* + * Format has been validated. Return it. + */ + rm.setData(regService.getFormattedValueContext(dmc, finalFormatId)); + rm.done(); + } + } + ); + } + + /* + * (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider#update(org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate[]) + */ + public void update(final ILabelUpdate[] updates) { + try { + getSession().getExecutor().execute(new DsfRunnable() { + public void run() { + updateLabelInSessionThread(updates); + }}); + } catch (RejectedExecutionException e) { + for (ILabelUpdate update : updates) { + handleFailedUpdate(update); + } + } + } + + /* + * Updates the labels which are controlled by the column being requested. + */ + protected void updateLabelInSessionThread(final ILabelUpdate[] updates) { + + /* + * This list represents all the QUEUED requests for formatted DMCs. This allows us to issue the + * requests for the data in the same dispatch cycle. Thus the lower level services is given its + * best chance to coalesce the registers in to a single request. + */ + final ArrayList<QueuedValueUpdate> valueUpdatesToProcess = new ArrayList<QueuedValueUpdate>(); + + final DsfExecutor dsfExecutor = getSession().getExecutor(); + final CountingRequestMonitor crm = + new CountingRequestMonitor(dsfExecutor, null) { + @Override + public void handleCompleted() { + if (!isSuccess()) { + for ( ILabelUpdate up : updates ) { + handleFailedUpdate(up); + } + return; + } + + /* + * We have all of the formatted DMCs. Go issue the requests for the formatted data + * in a single dispatch cycle. + */ + retrieveAllFormattedDataValues( valueUpdatesToProcess ); + } + }; + + crm.setDoneCount( calculateTheNumberOfRowsWithValueColumns(updates) ); + + /* + * Process each update request, creating a QUEUE of requests which need further processing + * for the formatted values. + */ + for (final ILabelUpdate update : updates) { + + final IRegisterDMContext dmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), IRegisterDMContext.class); + if ( dmc == null ) { + handleFailedUpdate(update); + continue; + } + + IRegisters regService = getServicesTracker().getService(IRegisters.class); + if ( regService == null ) { + handleFailedUpdate(update); + continue; + } + + getDMVMProvider().getModelData( + this, + update, + regService, + dmc, + new ViewerDataRequestMonitor<IRegisterDMData>(getSession().getExecutor(), update) { + @Override + protected void handleCompleted() { + /* + * Check that the request was evaluated and data is still + * valid. The request could fail if the state of the + * service changed during the request, but the view model + * has not been updated yet. + */ + if (!isSuccess()) { + assert getStatus().isOK() || + getStatus().getCode() != IDsfStatusConstants.INTERNAL_ERROR || + getStatus().getCode() != IDsfStatusConstants.NOT_SUPPORTED; + /* + * Instead of just failing this outright we are going to attempt to do more here. + * Failing it outright causes the view to display ... for all columns in the line + * and this is uninformative about what is happening. We may be trying to show a + * register whos retrieval has been cancelled by the lower level. Perhaps because + * we are stepping extremely fast and state changes cause the register service to + * return these requests without ever sending them to the debug engine. + * + */ + String[] localColumns = update.getColumnIds(); + if (localColumns == null) + localColumns = new String[] { IDebugVMConstants.COLUMN_ID__NAME }; + + for (int idx = 0; idx < localColumns.length; idx++) { + if (IDebugVMConstants.COLUMN_ID__NAME.equals(localColumns[idx])) { + /* + * This used to be easy in that the DMC contained the name. Which allowed us + * to display the register name and an error message across from it. Now that + * name must come from the data and we could not retrieve the data we do not + * have anything intelligent to show here. I think this is going to look very + * ugly and will need to be worked on. We know the service has the name with + * it, it is just the dynamic part which cannot be obtained ( as explained in + * comments above ). + */ + update.setLabel("Unknown name", idx); //$NON-NLS-1$ + update.setImageDescriptor(DebugPluginImages.getImageDescriptor(IDebugUIConstants.IMG_OBJS_REGISTER), idx); + } else if (IDebugVMConstants.COLUMN_ID__TYPE.equals(localColumns[idx])) { + update.setLabel("", idx); //$NON-NLS-1$ + } else if (IDebugVMConstants.COLUMN_ID__VALUE.equals(localColumns[idx])) { + if (getStatus().getCode() == IDsfStatusConstants.INVALID_STATE) { + update.setLabel("...", idx); //$NON-NLS-1$ + } else { + update.setLabel("Error: " + getStatus().getMessage(), idx); //$NON-NLS-1$ + } + } else if (IDebugVMConstants.COLUMN_ID__DESCRIPTION.equals(localColumns[idx])) { + update.setLabel("...", idx); //$NON-NLS-1$ + } else if (IDebugVMConstants.COLUMN_ID__EXPRESSION.equals(localColumns[idx])) { + update.setLabel("", idx); //$NON-NLS-1$ + } + + update.setFontData(JFaceResources.getFontDescriptor(IInternalDebugUIConstants.VARIABLE_TEXT_FONT).getFontData()[0], idx); + } + + update.done(); + return; + } + + /* + * If columns are configured, extract the selected values for each understood column. First we fill all + * of those columns which can be filled without the extra data mining. We also note, if we do have to + * datamine. Any columns need to set the processing flag so we know we have further work to do. If there + * are more columns which need data extraction they need to be added in both "for" loops. + */ + String[] localColumns = update.getColumnIds(); + if (localColumns == null) localColumns = new String[] { IDebugVMConstants.COLUMN_ID__NAME }; + + boolean allFieldsProcessed = true; + + for (int idx = 0; idx < localColumns.length; idx++) { + update.setFontData(JFaceResources.getFontDescriptor(IInternalDebugUIConstants.VARIABLE_TEXT_FONT).getFontData()[0], idx); + if (IDebugVMConstants.COLUMN_ID__NAME.equals(localColumns[idx])) { + update.setLabel(getData().getName(), idx); + update.setImageDescriptor(DebugPluginImages.getImageDescriptor(IDebugUIConstants.IMG_OBJS_REGISTER), idx); + } else if (IDebugVMConstants.COLUMN_ID__VALUE.equals(localColumns[idx])) { + allFieldsProcessed = false; + /* + * Create an entry which holds all related data and add it to the list to process + * when all the formatted DMCs are gathered. + */ + final QueuedValueUpdate valueUpdate = new QueuedValueUpdate(update,idx,dmc); + valueUpdatesToProcess.add(valueUpdate); + + /* + * Fetch the associated formatted DMC for this field. Note that every time we + * complete the request for a Formatted DMC we tell the Counting Request Monitor + * we have completed one in the list. + */ + getFormattedDmcForReqister( + update, dmc, + new ViewerDataRequestMonitor<FormattedValueDMContext>(dsfExecutor, update) { + @Override + public void handleCompleted() { + if ( getStatus().isOK() ) { + valueUpdate.setValueDmc(getData()); + } + else { + valueUpdate.setValueDmc(null); + } + crm.done(); + } + }); + } else if (IDebugVMConstants.COLUMN_ID__TYPE.equals(localColumns[idx])) { + IRegisterDMData data = getData(); + String typeStr = "Unsigned"; //$NON-NLS-1$ + String ReadAttrStr = "ReadNone"; //$NON-NLS-1$ + String WriteAddrStr = "WriteNone"; //$NON-NLS-1$ + + if ( data.isFloat() ) { typeStr = "Floating Point"; } //$NON-NLS-1$ + + if ( data.isReadOnce() ) { ReadAttrStr = "ReadOnce"; } //$NON-NLS-1$ + else if ( data.isReadable() ) { ReadAttrStr = "Readable"; } //$NON-NLS-1$ + + if ( data.isReadOnce() ) { WriteAddrStr = "WriteOnce"; } //$NON-NLS-1$ + else if ( data.isReadable() ) { WriteAddrStr = "Writeable"; } //$NON-NLS-1$ + + typeStr += " - " + ReadAttrStr + "/" + WriteAddrStr; //$NON-NLS-1$ //$NON-NLS-2$ + update.setLabel(typeStr, idx); + } else if (IDebugVMConstants.COLUMN_ID__DESCRIPTION.equals(localColumns[idx])) { + update.setLabel(getData().getDescription(), idx); + } else if (IDebugVMConstants.COLUMN_ID__EXPRESSION.equals(localColumns[idx])) { + IVMContext vmc = (IVMContext)update.getElement(); + IExpression expression = (IExpression)vmc.getAdapter(IExpression.class); + if (expression != null) { + update.setLabel(expression.getExpressionText(), idx); + } else { + update.setLabel(getData().getName(), idx); + } + } + } + + if ( allFieldsProcessed ) { + update.done(); + } + } + }, + getSession().getExecutor()); + } + } + + private int calculateTheNumberOfRowsWithValueColumns( ILabelUpdate updates[] ) { + int count = 0; + for (final ILabelUpdate update : updates) { + String[] columns = update.getColumnIds(); + if (columns == null) columns = new String[] { IDebugVMConstants.COLUMN_ID__NAME }; + + for (int idx = 0; idx < columns.length; idx++) { + if (IDebugVMConstants.COLUMN_ID__VALUE.equals(columns[idx])) { + count ++; + } + } + } + return count; + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMNode#update(org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate[]) + */ + @Override + public void update(IHasChildrenUpdate[] updates) { + // As an optimization, always indicate that register groups have + // children. + for (IHasChildrenUpdate update : updates) { + update.setHasChilren(true); + update.done(); + } + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMNode#updateElementsInSessionThread(org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate) + */ + @Override + protected void updateElementsInSessionThread(final IChildrenUpdate update) { + + IRegisters regService = getServicesTracker().getService(IRegisters.class); + + if ( regService == null ) { + handleFailedUpdate(update); + return; + } + + regService.getRegisters( + createCompositeDMVMContext(update), + new ViewerDataRequestMonitor<IRegisterDMContext[]>(getSession().getExecutor(), update) { + @Override + public void handleCompleted() { + if (!isSuccess()) { + handleFailedUpdate(update); + return; + } + fillUpdateWithVMCs(update, getData()); + update.done(); + } + }); + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMNode#createVMContext(org.eclipse.cdt.dsf.datamodel.IDMContext) + */ + @Override + protected IDMVMContext createVMContext(IDMContext dmc) { + return new RegisterVMC(dmc); + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.ui.viewmodel.IVMNode#getDeltaFlags(java.lang.Object) + */ + public int getDeltaFlags(Object e) { + if ( e instanceof ISuspendedDMEvent || + e instanceof IMemoryChangedEvent || + e instanceof IRegistersChangedDMEvent || + (e instanceof PropertyChangeEvent && + ((PropertyChangeEvent)e).getProperty() == IDebugVMConstants.CURRENT_FORMAT_STORAGE) ) + { + return IModelDelta.CONTENT; + } + + if (e instanceof IRegisterChangedDMEvent) { + return IModelDelta.STATE; + } + + return IModelDelta.NO_CHANGE; + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.ui.viewmodel.IVMNode#buildDelta(java.lang.Object, org.eclipse.cdt.dsf.ui.viewmodel.VMDelta, int, org.eclipse.cdt.dsf.concurrent.RequestMonitor) + */ + public void buildDelta(Object e, VMDelta parentDelta, int nodeOffset, RequestMonitor rm) { + // The following events can affect any register's values, + // refresh the contents of the parent element (i.e. all the registers). + if ( e instanceof ISuspendedDMEvent || + e instanceof IMemoryChangedEvent || + e instanceof IRegistersChangedDMEvent || + (e instanceof PropertyChangeEvent && + ((PropertyChangeEvent)e).getProperty() == IDebugVMConstants.CURRENT_FORMAT_STORAGE) ) + { + // Create a delta that the whole register group has changed. + parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); + } + + if (e instanceof IRegisterChangedDMEvent) { + parentDelta.addNode( createVMContext(((IRegisterChangedDMEvent)e).getDMContext()), IModelDelta.STATE ); + } + + rm.done(); + } + + /** + * Expected format: GRP( GroupName ).REG( RegisterName ) + * or: $RegisterName + */ + public boolean canParseExpression(IExpression expression) { + return parseExpressionForRegisterName(expression.getExpressionText()) != null; + } + + private String parseExpressionForRegisterName(String expression) { + if (expression.startsWith("GRP(")) { //$NON-NLS-1$ + /* + * Get the group portion. + */ + int startIdx = "GRP(".length(); //$NON-NLS-1$ + int endIdx = expression.indexOf(')', startIdx); + if ( startIdx == -1 || endIdx == -1 ) { + return null; + } + String remaining = expression.substring(endIdx+1); + if ( ! remaining.startsWith(".REG(") ) { //$NON-NLS-1$ + return null; + } + + /* + * Get the register portion. + */ + startIdx = ".REG(".length(); //$NON-NLS-1$ + endIdx = remaining.indexOf(')', startIdx); + if ( startIdx == -1 || endIdx == -1 ) { + return null; + } + String regName = remaining.substring(startIdx,endIdx); + return regName.trim(); + } + else if ( expression.startsWith("$") ) { //$NON-NLS-1$ + /* + * At this point I am leaving this code here to represent the register case. To do this + * correctly would be to use the findRegister function and upgrade the register service + * to deal with registers that do not have a specified group parent context. I do not + * have the time for this right now. So by saying we do not handle this the Expression + * VM node will take it and pass it to the debug engine as a generic expression. Most + * debug engines ( GDB included ) have an inherent knowledge of the core registers as + * part of their expression evaluation and will respond with a flat value for the reg. + * This is not totally complete in that you should be able to express a register which + * has bit fields for example and the bit fields should be expandable in the expression + * view. With this method it will just appear to have a single value and no sub-fields. + * I will file a defect/enhancement for this to mark it. This comment will act as the + * place-holder for the future work. + */ + return null; + } + + return null; + } + + /* + * (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 IRegisterDMContext dmc = DMContexts.getAncestorOfType(((IDMVMContext)element).getDMContext(), IRegisterDMContext.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 regName = parseExpressionForRegisterName(expression.getExpressionText()); + try { + getSession().getExecutor().execute(new DsfRunnable() { + public void run() { + IRegisters registersService = getServicesTracker().getService(IRegisters.class); + if (registersService != null) { + registersService.getRegisterData( + dmc, + new DataRequestMonitor<IRegisterDMData>(ImmediateExecutor.getInstance(), rm) { + @Override + protected void handleSuccess() { + rm.setData( getData().getName().equals(regName) ); + 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 RegisterVMC) { + ((RegisterVMC)element).setExpression(expression); + } + } + + /* + * (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 IRegisterChangedDMEvent || + event instanceof IMemoryChangedEvent || + (event instanceof PropertyChangeEvent && + ((PropertyChangeEvent)event).getProperty() == IDebugVMConstants.CURRENT_FORMAT_STORAGE) ) + { + return IModelDelta.STATE; + } + + if (event instanceof IRegistersChangedDMEvent || + event instanceof ISuspendedDMEvent) + { + return IModelDelta.CONTENT; + } + + 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 the register definition has changed, refresh all the + // expressions in the expression manager. This is because some + // expressions that were previously invalid, may now represent new + // registers. + if (event instanceof IRegistersChangedDMEvent) { + parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); + } + + // Always refresh the contents of the view upon suspended event. + if (event instanceof ISuspendedDMEvent) { + 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) + { + // The following events can affect register values, refresh the state + // of the expression. + if ( event instanceof IRegisterChangedDMEvent || + event instanceof IMemoryChangedEvent || + (event instanceof PropertyChangeEvent && + ((PropertyChangeEvent)event).getProperty() == IDebugVMConstants.CURRENT_FORMAT_STORAGE) ) + { + parentDelta.addNode(element, IModelDelta.STATE); + } + + rm.done(); + } + + /* + * (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); + } + else if (IDebugVMConstants.COLUMN_ID__VALUE.equals(columnId)) { + /* + * See if the register is writable and if so we will created a + * cell editor for it. + */ + IRegisterDMData regData = getSyncRegisterDataAccess().readRegister(element); + + if ( regData != null && regData.isWriteable() ) { + 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 new RegisterCellModifier( + getDMVMProvider(), fFormattedPrefStore, getSyncRegisterDataAccess() ); + } + + /* + * (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 = "REGISTER_MEMENTO_NAME"; //$NON-NLS-1$ + + public void compareElements(IElementCompareRequest[] requests) { + for ( final IElementCompareRequest request : requests ) { + final IRegisterDMContext regDmc = findDmcInPath(request.getViewerInput(), request.getElementPath(), IRegisterDMContext.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.getRegisterData( + regDmc, + new DataRequestMonitor<IRegisterDMData>(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( "Register." + 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 IRegisterDMContext regDmc = findDmcInPath(request.getViewerInput(), request.getElementPath(), IRegisterDMContext.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.getRegisterData( + regDmc, + new DataRequestMonitor<IRegisterDMData>(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, "Register." + getData().getName()); //$NON-NLS-1$ + } + request.done(); + } + }); + } else { + request.done(); + } + } + }); + } catch (RejectedExecutionException e) { + request.done(); + } + } + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/RegisterVMProvider.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/RegisterVMProvider.java new file mode 100644 index 00000000000..01badb93297 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/RegisterVMProvider.java @@ -0,0 +1,271 @@ +/******************************************************************************* + * 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.register; + +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.ICachingService; +import org.eclipse.cdt.dsf.debug.service.IRegisters; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +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.update.BreakpointHitUpdatePolicy; +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.AbstractVMContext; +import org.eclipse.cdt.dsf.ui.viewmodel.IRootVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMNode; +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.update.AutomaticUpdatePolicy; +import org.eclipse.cdt.dsf.ui.viewmodel.update.IVMUpdatePolicy; +import org.eclipse.cdt.dsf.ui.viewmodel.update.ManualUpdatePolicy; +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.jface.preference.IPreferenceStore; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; + +/** + * Provides the VIEW MODEL for the DEBUG MODEL REGISTER view. + */ +@SuppressWarnings("restriction") +public class RegisterVMProvider extends AbstractDMVMProvider +{ + 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); + } + }; + + /* + * Current default for register formatting. + */ + public RegisterVMProvider(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)); + + /* + * Create the register data access routines. + */ + SyncRegisterDataAccess regAccess = new SyncRegisterDataAccess(session) ; + + /* + * Create the top level node to deal with the root selection. + */ + IRootVMNode rootNode = new RegisterRootDMVMNode(this); + + /* + * Create the Group nodes next. They represent the first level shown in the view. + */ + IVMNode registerGroupNode = new RegisterGroupVMNode(this, getSession(), regAccess); + addChildNodes(rootNode, new IVMNode[] { registerGroupNode }); + + /* + * Create the next level which is the registers themselves. + */ + IVMNode registerNode = new RegisterVMNode(FormattedValuePreferenceStore.getDefault(), this, getSession(), regAccess); + addChildNodes(registerGroupNode, new IVMNode[] { registerNode }); + + /* + * Create the next level which is the bitfield level. + */ + IVMNode bitFieldNode = new RegisterBitFieldVMNode(FormattedValuePreferenceStore.getDefault(), this, getSession(), regAccess); + addChildNodes(registerNode, new IVMNode[] { bitFieldNode }); + + /* + * Now set this schema set as the layout set. + */ + setRootNode(rootNode); + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.ui.viewmodel.update.AbstractCachingVMProvider#createUpdateModes() + */ + @Override + protected IVMUpdatePolicy[] createUpdateModes() { + return new IVMUpdatePolicy[] { new AutomaticUpdatePolicy(), new ManualUpdatePolicy(), new BreakpointHitUpdatePolicy() }; + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider#dispose() + */ + @Override + public void dispose() { + DsfDebugUITools.getPreferenceStore().removePropertyChangeListener(fPreferencesListener); + getPresentationContext().removePropertyChangeListener(fPresentationContextListener); + super.dispose(); + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMProvider#createColumnPresentation(org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext, java.lang.Object) + */ + @Override + public IColumnPresentation createColumnPresentation(IPresentationContext context, Object element) { + return new RegisterColumnPresentation(); + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMProvider#getColumnPresentationId(org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext, java.lang.Object) + */ + @Override + public String getColumnPresentationId(IPresentationContext context, Object element) { + return RegisterColumnPresentation.ID; + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMProvider#canSkipHandlingEvent(java.lang.Object, java.lang.Object) + */ + @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; + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMProvider#update(org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputUpdate) + */ + @Override + public void update(IViewerInputUpdate update) { + /* + * Use the execution context in the current selection as the input provider. + * This insures that the REGISTER VIEW will not collapse and expand on stepping or on + * re-selection in the DEBUG VIEW. Currently the register content is not stack frame + * specific. If it were to become so then we would need to modify this policy. + */ + Object element = update.getElement(); + if (element instanceof IDMVMContext) { + IDMContext ctx = ((IDMVMContext) element).getDMContext(); + + IExecutionDMContext execDmc = DMContexts.getAncestorOfType(ctx, IExecutionDMContext.class); + if ( execDmc != null ) { + /* + * This tells the Flexible Hierarchy that element driving this view has not changed + * and there is no need to redraw the view. Since this is a somewhat fake VMContext + * we provide our Root Layout node as the representative VM node. + */ + update.setInputElement(new ViewInputElement(RegisterVMProvider.this.getRootVMNode(), execDmc)); + update.done(); + return; + } + } + + /* + * If we reach here, then we did not override the standard behavior. Invoke the + * super class and this will provide the default standard behavior. + */ + super.update(update); + } + + /* + * Provides a local implementation of the IDMVMContext. This allows us to return one + * of our own making, representing the DMContext we want to use as selection criteria. + */ + private class ViewInputElement extends AbstractVMContext implements IDMVMContext { + + final private IDMContext fDMContext; + + public ViewInputElement(IVMNode node, IDMContext dmc) { + super(node); + fDMContext = dmc; + } + + public IDMContext getDMContext() { + return fDMContext; + } + + /** + * The IAdaptable implementation. If the adapter is the DM context, + * return the context, otherwise delegate to IDMContext.getAdapter(). + */ + @Override + @SuppressWarnings("unchecked") + 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(fDMContext)) { + return fDMContext; + } else { + return fDMContext.getAdapter(adapter); + } + } + } + + @Override + public boolean equals(Object obj) { + + if ( obj instanceof ViewInputElement && ((ViewInputElement) obj).fDMContext.equals(fDMContext) ) { + return true; + } + return false; + } + + @Override + public int hashCode() { + return fDMContext.hashCode(); + } + } + + @Override + public void refresh() { + super.refresh(); + try { + getSession().getExecutor().execute(new DsfRunnable() { + public void run() { + DsfServicesTracker tracker = new DsfServicesTracker(DsfUIPlugin.getBundleContext(), getSession().getId()); + IRegisters registerService = tracker.getService(IRegisters.class); + if (registerService instanceof ICachingService) { + ((ICachingService)registerService).flushCache(null); + } + tracker.dispose(); + } + }); + } catch (RejectedExecutionException e) { + // Session disposed, ignore. + } + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/SyncRegisterDataAccess.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/SyncRegisterDataAccess.java new file mode 100644 index 00000000000..bca7931582b --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/SyncRegisterDataAccess.java @@ -0,0 +1,813 @@ +/******************************************************************************* + * Copyright (c) 2007, 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.register; + +import java.util.concurrent.ExecutionException; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; +import org.eclipse.cdt.dsf.concurrent.Query; +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +import org.eclipse.cdt.dsf.concurrent.ThreadSafeAndProhibitedFromDsfExecutor; +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.IFormattedValues.FormattedValueDMContext; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMData; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.IFormattedDataDMContext; +import org.eclipse.cdt.dsf.debug.service.IRegisters.IBitFieldDMContext; +import org.eclipse.cdt.dsf.debug.service.IRegisters.IBitFieldDMData; +import org.eclipse.cdt.dsf.debug.service.IRegisters.IMnemonic; +import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMContext; +import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMData; +import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterGroupDMContext; +import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterGroupDMData; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.cdt.dsf.service.DsfServices; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.util.tracker.ServiceTracker; + +@ThreadSafeAndProhibitedFromDsfExecutor("fSession#getExecutor") +public class SyncRegisterDataAccess { + + abstract public class RegistersServiceQuery<V, K extends IDMContext> extends Query<V> { + + final protected K fDmc; + + public RegistersServiceQuery(K dmc) { + fDmc = dmc; + } + + @Override + protected void execute(final DataRequestMonitor<V> rm) { + /* + * We're in another dispatch, so we must guard against executor + * shutdown again. + */ + final DsfSession session = DsfSession.getSession(fDmc.getSessionId()); + if (session == null) { + cancel(false); + rm.done(); + return; + } + + /* + * Guard against a disposed service + */ + IRegisters service = getService(); + if (service == null) { + rm.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, + "Service unavailable", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + doExecute(service, rm); + } + + abstract protected void doExecute(IRegisters registersService, DataRequestMonitor<V> rm); + } + + + /** + * The session that this data access operates in. + */ + private final DsfSession fSession; + + /** + * Need to use the OSGi service tracker here (instead of DsfServiceTracker), + * because we're accessing it in non-dispatch thread. DsfServiceTracker is + * not thread-safe. + */ + @ThreadSafe + private ServiceTracker fServiceTracker; + + public SyncRegisterDataAccess(DsfSession session) { + fSession = session; + } + + @ThreadSafe + private synchronized IRegisters getService() { + + String serviceId = DsfServices.createServiceFilter(IRegisters.class, fSession.getId()); + if (fServiceTracker == null) { + try { + fServiceTracker = new ServiceTracker(DsfUIPlugin.getBundleContext(), DsfUIPlugin + .getBundleContext().createFilter(serviceId), null); + fServiceTracker.open(); + } catch (InvalidSyntaxException e) { + return null; + } + } + return (IRegisters) fServiceTracker.getService(); + } + + @ThreadSafe + public synchronized void dispose() { + if (fServiceTracker != null) { + fServiceTracker.close(); + } + } + + public class GetBitFieldValueQuery extends RegistersServiceQuery<IBitFieldDMData, IBitFieldDMContext> { + + public GetBitFieldValueQuery(IBitFieldDMContext dmc) { + super(dmc); + } + + @Override + protected void doExecute(IRegisters service, final DataRequestMonitor<IBitFieldDMData> rm) { + service.getBitFieldData( + fDmc, + new DataRequestMonitor<IBitFieldDMData>(ImmediateExecutor.getInstance(), rm) { + @Override + protected void handleSuccess() { + /* + * All good set return value. + */ + rm.setData(getData()); + rm.done(); + } + }); + } + } + + public IBitFieldDMContext getBitFieldDMC(Object element) { + if (element instanceof IDMVMContext) { + IDMContext dmc = ((IDMVMContext) element).getDMContext(); + return DMContexts.getAncestorOfType(dmc, IBitFieldDMContext.class); + } + return null; + } + + public IBitFieldDMData readBitField(Object element) { + /* + * Get the DMC and the session. If element is not an register DMC, or + * session is stale, then bail out. + */ + IBitFieldDMContext dmc = getBitFieldDMC(element); + if (dmc == null) + return null; + DsfSession session = DsfSession.getSession(dmc.getSessionId()); + if (session == null) + return null; + + /* + * Create the query to request the value from service. Note: no need to + * guard agains RejectedExecutionException, because + * DsfSession.getSession() above would only return an active session. + */ + GetBitFieldValueQuery query = new GetBitFieldValueQuery(dmc); + session.getExecutor().execute(query); + + /* + * Now we have the data, go and get it. Since the call is completed now + * the ".get()" will not suspend it will immediately return with the + * data. + */ + try { + return query.get(); + } catch (InterruptedException e) { + assert false; + return null; + } catch (ExecutionException e) { + return null; + } + } + + public class SetBitFieldValueQuery extends RegistersServiceQuery<Object, IBitFieldDMContext> { + + private String fValue; + private String fFormatId; + + public SetBitFieldValueQuery(IBitFieldDMContext dmc, String value, String formatId) { + super(dmc); + fValue = value; + fFormatId = formatId; + } + + @Override + protected void doExecute(IRegisters service, final DataRequestMonitor<Object> rm) { + // Write the bit field using a string/format style. + service.writeBitField( + fDmc, fValue, fFormatId, + new DataRequestMonitor<IBitFieldDMData>(ImmediateExecutor.getInstance(), rm) { + @Override + protected void handleSuccess() { + /* + * All good set return value. + */ + rm.setData(new Object()); + rm.done(); + } + }); + } + } + + public void writeBitField(Object element, String value, String formatId) { + + /* + * Get the DMC and the session. If element is not an register DMC, or + * session is stale, then bail out. + */ + IBitFieldDMContext dmc = getBitFieldDMC(element); + if (dmc == null) + return; + DsfSession session = DsfSession.getSession(dmc.getSessionId()); + if (session == null) + return; + + /* + * Create the query to write the value to the service. Note: no need to + * guard agains RejectedExecutionException, because + * DsfSession.getSession() above would only return an active session. + */ + SetBitFieldValueQuery query = new SetBitFieldValueQuery(dmc, value, formatId); + session.getExecutor().execute(query); + + /* + * Now we have the data, go and get it. Since the call is completed now + * the ".get()" will not suspend it will immediately return with the + * data. + */ + try { + /* + * Return value is irrelevant, any error would come through with an + * exception. + */ + query.get(); + } catch (InterruptedException e) { + assert false; + } catch (ExecutionException e) { + assert false; + /* + * View must be shutting down, no need to show erro dialog. + */ + } + } + + public class SetBitFieldValueMnemonicQuery extends RegistersServiceQuery<Object, IBitFieldDMContext> { + IMnemonic fMnemonic; + + public SetBitFieldValueMnemonicQuery(IBitFieldDMContext dmc, IMnemonic mnemonic) { + super(dmc); + fMnemonic = mnemonic; + } + + @Override + protected void doExecute(IRegisters service, final DataRequestMonitor<Object> rm) { + // Write the bit field using the mnemonic style. + service.writeBitField( + fDmc, fMnemonic, + new DataRequestMonitor<IBitFieldDMData>(ImmediateExecutor.getInstance(), rm) { + @Override + protected void handleSuccess() { + /* + * All good set return value. + */ + rm.setData(new Object()); + rm.done(); + } + }); + } + } + + public void writeBitField(Object element, IMnemonic mnemonic) { + + /* + * Get the DMC and the session. If element is not an register DMC, or + * session is stale, then bail out. + */ + IBitFieldDMContext dmc = getBitFieldDMC(element); + if (dmc == null) + return; + DsfSession session = DsfSession.getSession(dmc.getSessionId()); + if (session == null) + return; + + /* + * Create the query to write the value to the service. Note: no need to + * guard agains RejectedExecutionException, because + * DsfSession.getSession() above would only return an active session. + */ + SetBitFieldValueMnemonicQuery query = new SetBitFieldValueMnemonicQuery(dmc, mnemonic); + session.getExecutor().execute(query); + + /* + * Now we have the data, go and get it. Since the call is completed now + * the ".get()" will not suspend it will immediately return with the + * data. + */ + try { + /* + * Return value is irrelevant, any error would come through with an + * exception. + */ + query.get(); + } catch (InterruptedException e) { + assert false; + } catch (ExecutionException e) { + /* + * View must be shutting down, no need to show erro dialog. + */ + } + } + + public IRegisterGroupDMContext getRegisterGroupDMC(Object element) { + if (element instanceof IDMVMContext) { + IDMContext dmc = ((IDMVMContext) element).getDMContext(); + return DMContexts.getAncestorOfType(dmc, IRegisterGroupDMContext.class); + } + return null; + } + + public IRegisterDMContext getRegisterDMC(Object element) { + if (element instanceof IDMVMContext) { + IDMContext dmc = ((IDMVMContext) element).getDMContext(); + return DMContexts.getAncestorOfType(dmc, IRegisterDMContext.class); + } + return null; + } + + public IFormattedDataDMContext getFormattedDMC(Object element) { + if (element instanceof IDMVMContext) { + IDMContext dmc = ((IDMVMContext) element).getDMContext(); + IRegisterDMContext regdmc = DMContexts.getAncestorOfType(dmc, IRegisterDMContext.class); + return DMContexts.getAncestorOfType(regdmc, IFormattedDataDMContext.class); + } + return null; + } + + public class GetRegisterGroupValueQuery extends RegistersServiceQuery<IRegisterGroupDMData, IRegisterGroupDMContext> { + public GetRegisterGroupValueQuery(IRegisterGroupDMContext dmc) { + super(dmc); + } + + @Override + protected void doExecute(IRegisters service, final DataRequestMonitor<IRegisterGroupDMData> rm) { + service.getRegisterGroupData( + fDmc, + new DataRequestMonitor<IRegisterGroupDMData>(ImmediateExecutor.getInstance(), rm) { + @Override + protected void handleSuccess() { + /* + * All good set return value. + */ + rm.setData(getData()); + rm.done(); + } + }); + } + } + + public IRegisterGroupDMData readRegisterGroup(Object element) { + /* + * Get the DMC and the session. If element is not an register DMC, or + * session is stale, then bail out. + */ + IRegisterGroupDMContext dmc = getRegisterGroupDMC(element); + if (dmc == null) + return null; + DsfSession session = DsfSession.getSession(dmc.getSessionId()); + if (session == null) + return null; + + /* + * Create the query to request the value from service. Note: no need to + * guard agains RejectedExecutionException, because + * DsfSession.getSession() above would only return an active session. + */ + GetRegisterGroupValueQuery query = new GetRegisterGroupValueQuery(dmc); + session.getExecutor().execute(query); + + /* + * Now we have the data, go and get it. Since the call is completed now + * the ".get()" will not suspend it will immediately return with the + * data. + */ + try { + return query.get(); + } catch (InterruptedException e) { + assert false; + return null; + } catch (ExecutionException e) { + return null; + } + } + + public class GetRegisterValueQuery extends RegistersServiceQuery<IRegisterDMData, IRegisterDMContext> { + public GetRegisterValueQuery(IRegisterDMContext dmc) { + super(dmc); + } + + @Override + protected void doExecute(IRegisters service, final DataRequestMonitor<IRegisterDMData> rm) { + service.getRegisterData( + fDmc, + new DataRequestMonitor<IRegisterDMData>(ImmediateExecutor.getInstance(), rm) { + @Override + protected void handleSuccess() { + /* + * All good set return value. + */ + rm.setData(getData()); + rm.done(); + } + }); + } + } + + public IRegisterDMData readRegister(Object element) { + /* + * Get the DMC and the session. If element is not an register DMC, or + * session is stale, then bail out. + */ + IRegisterDMContext dmc = getRegisterDMC(element); + if (dmc == null) + return null; + DsfSession session = DsfSession.getSession(dmc.getSessionId()); + if (session == null) + return null; + + /* + * Create the query to request the value from service. Note: no need to + * guard agains RejectedExecutionException, because + * DsfSession.getSession() above would only return an active session. + */ + GetRegisterValueQuery query = new GetRegisterValueQuery(dmc); + session.getExecutor().execute(query); + + /* + * Now we have the data, go and get it. Since the call is completed now + * the ".get()" will not suspend it will immediately return with the + * data. + */ + try { + return query.get(); + } catch (InterruptedException e) { + assert false; + return null; + } catch (ExecutionException e) { + return null; + } + } + + public class SetRegisterValueQuery extends RegistersServiceQuery<Object, IRegisterDMContext> { + private String fValue; + + private String fFormatId; + + public SetRegisterValueQuery(IRegisterDMContext dmc, String value, String formatId) { + super(dmc); + fValue = value; + fFormatId = formatId; + } + + @Override + protected void doExecute(IRegisters service, final DataRequestMonitor<Object> rm) { + /* + * Write the bit field using a string/format style. + */ + service.writeRegister( + fDmc, fValue, fFormatId, + new DataRequestMonitor<IBitFieldDMData>(ImmediateExecutor.getInstance(), rm) { + @Override + protected void handleSuccess() { + /* + * All good set return value. + */ + rm.setData(new Object()); + rm.done(); + } + }); + } + } + + public void writeRegister(Object element, String value, String formatId) { + + /* + * Get the DMC and the session. If element is not an register DMC, or + * session is stale, then bail out. + */ + IRegisterDMContext dmc = getRegisterDMC(element); + if (dmc == null) + return; + DsfSession session = DsfSession.getSession(dmc.getSessionId()); + if (session == null) + return; + + /* + * Create the query to write the value to the service. Note: no need to + * guard agains RejectedExecutionException, because + * DsfSession.getSession() above would only return an active session. + */ + SetRegisterValueQuery query = new SetRegisterValueQuery(dmc, value, formatId); + session.getExecutor().execute(query); + + /* + * Now we have the data, go and get it. Since the call is completed now + * the ".get()" will not suspend it will immediately return with the + * data. + */ + try { + /* + * Return value is irrelevant, any error would come through with an + * exception. + */ + query.get(); + } catch (InterruptedException e) { + assert false; + } catch (ExecutionException e) { + /* + * View must be shutting down, no need to show erro dialog. + */ + } + } + + public class GetSupportFormatsValueQuery extends RegistersServiceQuery<String[], IFormattedDataDMContext> { + + public GetSupportFormatsValueQuery(IFormattedDataDMContext dmc) { + super(dmc); + } + + @Override + protected void doExecute(IRegisters service, final DataRequestMonitor<String[]> rm) { + service.getAvailableFormats(fDmc, rm); + } + } + + public String[] getSupportedFormats(Object element) { + + /* + * Get the DMC and the session. If element is not an register DMC, or + * session is stale, then bail out. + */ + IFormattedDataDMContext dmc = null; + if (element instanceof IDMVMContext) { + IDMContext vmcdmc = ((IDMVMContext) element).getDMContext(); + IRegisterDMContext regdmc = DMContexts.getAncestorOfType(vmcdmc, IRegisterDMContext.class); + dmc = DMContexts.getAncestorOfType(regdmc, IFormattedDataDMContext.class); + } + + if (dmc == null) + return null; + DsfSession session = DsfSession.getSession(dmc.getSessionId()); + if (session == null) + return null; + + /* + * Create the query to write the value to the service. Note: no need to + * guard agains RejectedExecutionException, because + * DsfSession.getSession() above would only return an active session. + */ + GetSupportFormatsValueQuery query = new GetSupportFormatsValueQuery(dmc); + session.getExecutor().execute(query); + + /* + * Now we have the data, go and get it. Since the call is completed now + * the ".get()" will not suspend it will immediately return with the + * data. + */ + try { + return query.get(); + } catch (InterruptedException e) { + assert false; + return null; + } catch (ExecutionException e) { + return null; + } + } + + public class GetFormattedValueValueQuery extends RegistersServiceQuery<String, IFormattedDataDMContext> { + + private String fFormatId; + + public GetFormattedValueValueQuery(IFormattedDataDMContext dmc, String formatId) { + super(dmc); + fFormatId = formatId; + } + + @Override + protected void doExecute(IRegisters service, final DataRequestMonitor<String> rm) { + /* + * Convert to the proper formatting DMC then go get the formatted + * value. + */ + + FormattedValueDMContext formDmc = service.getFormattedValueContext(fDmc, fFormatId); + + service.getFormattedExpressionValue( + formDmc, + new DataRequestMonitor<FormattedValueDMData>(ImmediateExecutor.getInstance(), rm) { + @Override + protected void handleSuccess() { + /* + * All good set return value. + */ + rm.setData(getData().getFormattedValue()); + rm.done(); + } + }); + } + } + + public String getFormattedRegisterValue(Object element, String formatId) { + + /* + * Get the DMC and the session. If element is not an register DMC, or + * session is stale, then bail out. + */ + IFormattedDataDMContext dmc = null; + if (element instanceof IDMVMContext) { + IDMContext vmcdmc = ((IDMVMContext) element).getDMContext(); + IRegisterDMContext regdmc = DMContexts.getAncestorOfType(vmcdmc, IRegisterDMContext.class); + dmc = DMContexts.getAncestorOfType(regdmc, IFormattedDataDMContext.class); + } + + if (dmc == null) + return null; + DsfSession session = DsfSession.getSession(dmc.getSessionId()); + if (session == null) + return null; + + /* + * Create the query to write the value to the service. Note: no need to + * guard agains RejectedExecutionException, because + * DsfSession.getSession() above would only return an active session. + */ + GetFormattedValueValueQuery query = new GetFormattedValueValueQuery(dmc, formatId); + session.getExecutor().execute(query); + + /* + * Now we have the data, go and get it. Since the call is completed now + * the ".get()" will not suspend it will immediately return with the + * data. + */ + try { + return query.get(); + } catch (InterruptedException e) { + assert false; + return null; + } catch (ExecutionException e) { + return null; + } + } + + public String getFormattedBitFieldValue(Object element, String formatId) { + + /* + * Get the DMC and the session. If element is not an register DMC, or + * session is stale, then bail out. + */ + IFormattedDataDMContext dmc = null; + if (element instanceof IDMVMContext) { + IDMContext vmcdmc = ((IDMVMContext) element).getDMContext(); + IBitFieldDMContext bitfielddmc = DMContexts.getAncestorOfType(vmcdmc, IBitFieldDMContext.class); + dmc = DMContexts.getAncestorOfType(bitfielddmc, IFormattedDataDMContext.class); + } + + if (dmc == null) + return null; + DsfSession session = DsfSession.getSession(dmc.getSessionId()); + if (session == null) + return null; + + /* + * Create the query to write the value to the service. Note: no need to + * guard agains RejectedExecutionException, because + * DsfSession.getSession() above would only return an active session. + */ + GetFormattedValueValueQuery query = new GetFormattedValueValueQuery(dmc, formatId); + session.getExecutor().execute(query); + + /* + * Now we have the data, go and get it. Since the call is completed now + * the ".get()" will not suspend it will immediately return with the + * data. + */ + try { + return query.get(); + } catch (InterruptedException e) { + assert false; + return null; + } catch (ExecutionException e) { + return null; + } + } + + public class GetRegisterGroupDataQuery extends RegistersServiceQuery<IRegisterGroupDMData, IRegisterGroupDMContext> { + + public GetRegisterGroupDataQuery(IRegisterGroupDMContext dmc) { + super(dmc); + } + + @Override + protected void doExecute(IRegisters service, final DataRequestMonitor<IRegisterGroupDMData> rm) { + service.getRegisterGroupData(fDmc, rm); + } + } + + public IRegisterGroupDMData getRegisterGroupDMData(Object element) { + IRegisterGroupDMContext dmc = null; + if (element instanceof IDMVMContext) { + dmc = DMContexts.getAncestorOfType( + ((IDMVMContext) element).getDMContext(), + IRegisterGroupDMContext.class); + } + + DsfSession session = DsfSession.getSession(dmc.getSessionId()); + + if (dmc != null && session != null) { + GetRegisterGroupDataQuery query = new GetRegisterGroupDataQuery(dmc); + session.getExecutor().execute(query); + + try { + return query.get(); + } catch (InterruptedException e) { + } catch (ExecutionException e) { + } + } + return null; + } + + + public class GetRegisterDataQuery extends RegistersServiceQuery<IRegisterDMData, IRegisterDMContext> { + + public GetRegisterDataQuery(IRegisterDMContext dmc) { + super(dmc); + } + + @Override + protected void doExecute(IRegisters service, final DataRequestMonitor<IRegisterDMData> rm) { + service.getRegisterData(fDmc, rm); + } + } + + public IRegisterDMData getRegisterDMData(Object element) { + IRegisterDMContext dmc = null; + if (element instanceof IDMVMContext) { + dmc = DMContexts.getAncestorOfType( ((IDMVMContext) element).getDMContext(), IRegisterDMContext.class ); + } + DsfSession session = DsfSession.getSession(dmc.getSessionId()); + + if (dmc != null && session != null) { + GetRegisterDataQuery query = new GetRegisterDataQuery(dmc); + session.getExecutor().execute(query); + + try { + return query.get(); + } catch (InterruptedException e) { + } catch (ExecutionException e) { + } + } + return null; + } + + public class GetBitFieldQuery extends RegistersServiceQuery<IBitFieldDMData, IBitFieldDMContext> { + + public GetBitFieldQuery(IBitFieldDMContext dmc) { + super(dmc); + } + + @Override + protected void doExecute(IRegisters service, final DataRequestMonitor<IBitFieldDMData> rm) { + service.getBitFieldData(fDmc, rm); + } + } + + public IBitFieldDMData getBitFieldDMData(Object element) { + IBitFieldDMContext dmc = null; + if (element instanceof IDMVMContext) { + dmc = DMContexts.getAncestorOfType( ((IDMVMContext) element).getDMContext(), IBitFieldDMContext.class ); + } + DsfSession session = DsfSession.getSession(dmc.getSessionId()); + + if (dmc != null && session != null) { + GetBitFieldQuery query = new GetBitFieldQuery(dmc); + session.getExecutor().execute(query); + + try { + return query.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/viewmodel/register/messages.properties b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/messages.properties new file mode 100644 index 00000000000..a5bb472ba12 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/messages.properties @@ -0,0 +1,15 @@ +############################################################################### +# Copyright (c) 2007, 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 +############################################################################### + +RegisterColumnPresentation_name=Name +RegisterColumnPresentation_type=Type +RegisterColumnPresentation_value=Value +RegisterColumnPresentation_description=Description diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/update/BreakpointHitUpdatePolicy.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/update/BreakpointHitUpdatePolicy.java new file mode 100644 index 00000000000..49ae09bbfaa --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/update/BreakpointHitUpdatePolicy.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2007, 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.update; + +import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.StateChangeReason; +import org.eclipse.cdt.dsf.ui.viewmodel.update.IElementUpdateTester; +import org.eclipse.cdt.dsf.ui.viewmodel.update.ManualUpdatePolicy; + +/** + * + */ +public class BreakpointHitUpdatePolicy extends ManualUpdatePolicy { + + public static String BREAKPOINT_HIT_UPDATE_POLICY_ID = "org.eclipse.cdt.dsf.debug.ui.viewmodel.update.breakpointHitUpdatePolicy"; //$NON-NLS-1$ + + @Override + public String getID() { + return BREAKPOINT_HIT_UPDATE_POLICY_ID; + } + + @Override + public String getName() { + return MessagesForVMUpdate.BreakpointHitUpdatePolicy_name; + } + + @Override + public IElementUpdateTester getElementUpdateTester(Object event) { + if(event instanceof ISuspendedDMEvent) { + ISuspendedDMEvent suspendedEvent = (ISuspendedDMEvent)event; + if(suspendedEvent.getReason().equals(StateChangeReason.BREAKPOINT)) { + return super.getElementUpdateTester(REFRESH_EVENT); + } + } + return super.getElementUpdateTester(event); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/update/MessagesForVMUpdate.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/update/MessagesForVMUpdate.java new file mode 100644 index 00000000000..a734d7d623f --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/update/MessagesForVMUpdate.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 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.update; + +import org.eclipse.osgi.util.NLS; + +public class MessagesForVMUpdate extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.cdt.dsf.debug.ui.viewmodel.update.messages"; //$NON-NLS-1$ + + public static String BreakpointHitUpdatePolicy_name; + + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, MessagesForVMUpdate.class); + } + + private MessagesForVMUpdate() { + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/update/messages.properties b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/update/messages.properties new file mode 100644 index 00000000000..98e6fad25fd --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/update/messages.properties @@ -0,0 +1,12 @@ +############################################################################### +# Copyright (c) 2007, 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 +############################################################################### + +BreakpointHitUpdatePolicy_name=Breakpoint Hit diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/MessagesForVariablesVM.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/MessagesForVariablesVM.java new file mode 100644 index 00000000000..8ea0064be22 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/MessagesForVariablesVM.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 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.variable; + +import org.eclipse.osgi.util.NLS; + +public class MessagesForVariablesVM extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.cdt.dsf.debug.ui.viewmodel.variable.messages"; //$NON-NLS-1$ + + public static String VariableColumnPresentation_name; + + public static String VariableColumnPresentation_type; + + public static String VariableColumnPresentation_value; + + public static String VariableColumnPresentation_address; + + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, MessagesForVariablesVM.class); + } + + private MessagesForVariablesVM() { + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/SyncVariableDataAccess.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/SyncVariableDataAccess.java new file mode 100644 index 00000000000..e49197e51df --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/SyncVariableDataAccess.java @@ -0,0 +1,543 @@ +/******************************************************************************* + * Copyright (c) 2007, 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.variable; + +import java.util.concurrent.ExecutionException; + +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.concurrent.ThreadSafe; +import org.eclipse.cdt.dsf.concurrent.ThreadSafeAndProhibitedFromDsfExecutor; +import org.eclipse.cdt.dsf.debug.service.IExpressions; +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.IFormattedValues.FormattedValueDMContext; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMData; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.IFormattedDataDMContext; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.service.IDsfService; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.util.tracker.ServiceTracker; + +@ThreadSafeAndProhibitedFromDsfExecutor("fSession#getExecutor") +public class SyncVariableDataAccess { + + /** + * The session that this data access operates in. + */ + private final DsfSession fSession; + + /** + * Need to use the OSGi service tracker here (instead of DsfServiceTracker), + * because we're accessing it in non-dispatch thread. DsfServiceTracker is + * not thread-safe. + */ + @ThreadSafe + private ServiceTracker fServiceTracker; + + + public SyncVariableDataAccess(DsfSession session) { + fSession = session; + } + + @ThreadSafe + private synchronized IExpressions getService() { + + if (fServiceTracker == null) { + try { + fServiceTracker = new ServiceTracker( + DsfUIPlugin.getBundleContext(), + DsfUIPlugin.getBundleContext().createFilter(getServiceFilter()), null); + fServiceTracker.open(); + } catch (InvalidSyntaxException e) { + return null; + } + } + return (IExpressions) fServiceTracker.getService(); + } + + private String getServiceFilter() { + StringBuffer filter = new StringBuffer(); + filter.append("(&"); //$NON-NLS-1$ + filter.append("(OBJECTCLASS="); //$NON-NLS-1$ + filter.append(IExpressions.class.getName()); + filter.append(')'); + filter.append('('); + filter.append(IDsfService.PROP_SESSION_ID); + filter.append('='); + filter.append(fSession.getId()); + filter.append(')'); + filter.append(')'); + return filter.toString(); + } + + @ThreadSafe + public synchronized void dispose() { + if (fServiceTracker != null) { + fServiceTracker.close(); + } + } + + public IExpressionDMContext getVariableDMC(Object element) { + if (element instanceof IAdaptable) { + return (IExpressionDMContext) ((IAdaptable) element).getAdapter(IExpressionDMContext.class); + } + return null; + } + + + public class GetVariableValueQuery extends Query<IExpressionDMData> { + + private IExpressionDMContext fDmc; + + public GetVariableValueQuery(IExpressionDMContext dmc) { + super(); + fDmc = dmc; + } + + @Override + protected void execute(final DataRequestMonitor<IExpressionDMData> rm) { + /* + * Guard against the session being disposed. If session is disposed + * it could mean that the executor is shut-down, which in turn could + * mean that we can't complete the RequestMonitor argument. in that + * case, cancel to notify waiting thread. + */ + final DsfSession session = DsfSession.getSession(fDmc.getSessionId()); + if (session == null) { + cancel(false); + rm.done(); + return; + } + + IExpressions service = getService(); + if (service == null) { + rm.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, "Service not available", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + service.getExpressionData(fDmc, new DataRequestMonitor<IExpressionDMData>(session.getExecutor(), rm) { + @Override + protected void handleSuccess() { + /* + * All good set return value. + */ + rm.setData(getData()); + rm.done(); + } + }); + } + } + + public IExpressionDMContext getExpressionDMC(Object element) { + if (element instanceof IAdaptable) { + return (IExpressionDMContext) ((IAdaptable) element).getAdapter(IExpressionDMContext.class); + } + return null; + } + + public IExpressionDMData readVariable(Object element) { + /* + * Get the DMC and the session. If element is not an expression DMC, or + * session is stale, then bail out. + */ + IExpressionDMContext dmc = getExpressionDMC(element); + if (dmc == null) return null; + DsfSession session = DsfSession.getSession(dmc.getSessionId()); + if (session == null) return null; + + /* + * Create the query to request the value from service. Note: no need to + * guard against RejectedExecutionException, because + * DsfSession.getSession() above would only return an active session. + */ + GetVariableValueQuery query = new GetVariableValueQuery(dmc); + session.getExecutor().execute(query); + + /* + * Now we have the data, go and get it. Since the call is completed now + * the ".get()" will not suspend it will immediately return with the + * data. + */ + try { + return query.get(); + } catch (InterruptedException e) { + assert false; + return null; + } catch (ExecutionException e) { + return null; + } + } + + public class SetVariableValueQuery extends Query<Object> { + + private IExpressionDMContext fDmc; + private String fValue; + private String fFormatId; + + public SetVariableValueQuery(IExpressionDMContext dmc, String value, String formatId) { + super(); + fDmc = dmc; + fValue = value; + fFormatId = formatId; + } + + @Override + protected void execute(final DataRequestMonitor<Object> rm) { + /* + * We're in another dispatch, so we must guard against executor + * shutdown again. + */ + final DsfSession session = DsfSession.getSession(fDmc.getSessionId()); + if (session == null) { + cancel(false); + rm.done(); + return; + } + + /* + * Guard against a disposed service + */ + IExpressions service = getService(); + if (service == null) { + rm.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, "Service unavailable", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + /* + * Write the expression value using a string/format style. + */ + service.writeExpression( + fDmc, + fValue, + fFormatId, + new DataRequestMonitor<IExpressionDMData>(session.getExecutor(), rm) { + @Override + protected void handleSuccess() { + /* + * All good set return value. + */ + rm.setData(new Object()); + rm.done(); + } + } + ); + } + } + + public void writeVariable(Object element, String value, String formatId) { + + /* + * Get the DMC and the session. If element is not an register DMC, or + * session is stale, then bail out. + */ + IExpressionDMContext dmc = getExpressionDMC(element); + if (dmc == null) return; + DsfSession session = DsfSession.getSession(dmc.getSessionId()); + if (session == null) return; + + /* + * Create the query to write the value to the service. Note: no need to + * guard against RejectedExecutionException, because + * DsfSession.getSession() above would only return an active session. + */ + SetVariableValueQuery query = new SetVariableValueQuery(dmc, value, formatId); + session.getExecutor().execute(query); + + /* + * Now we have the data, go and get it. Since the call is completed now + * the ".get()" will not suspend it will immediately return with the + * data. + */ + try { + /* + * Return value is irrelevant, any error would come through with an + * exception. + */ + query.get(); + } catch (InterruptedException e) { + assert false; + } catch (ExecutionException e) { + /* + * View must be shutting down, no need to show error dialog. + */ + } + } + + public IFormattedDataDMContext getFormattedDMC(Object element) { + if (element instanceof IAdaptable) { + return (IFormattedDataDMContext) ((IAdaptable) element).getAdapter(IFormattedDataDMContext.class); + } + return null; + } + + public class GetSupportFormatsValueQuery extends Query<Object> { + + IFormattedDataDMContext fDmc; + + public GetSupportFormatsValueQuery(IFormattedDataDMContext dmc) { + super(); + fDmc = dmc; + } + + @Override + protected void execute(final DataRequestMonitor<Object> rm) { + /* + * We're in another dispatch, so we must guard against executor + * shutdown again. + */ + final DsfSession session = DsfSession.getSession(fDmc.getSessionId()); + if (session == null) { + cancel(false); + rm.done(); + return; + } + + /* + * Guard against a disposed service + */ + IExpressions service = getService(); + if (service == null) { + rm.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, "Service unavailable", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + /* + * Get the available formats from the service. + */ + service.getAvailableFormats( + fDmc, + new DataRequestMonitor<String[]>(session.getExecutor(), rm) { + @Override + protected void handleSuccess() { + /* + * All good set return value. + */ + rm.setData(new Object()); + rm.done(); + } + } + ); + } + } + + public String[] getSupportedFormats(Object element) { + + /* + * Get the DMC and the session. If element is not an register DMC, or + * session is stale, then bail out. + */ + IFormattedDataDMContext dmc = getFormattedDMC(element); + if (dmc == null) return null; + DsfSession session = DsfSession.getSession(dmc.getSessionId()); + if (session == null) return null; + + /* + * Create the query to write the value to the service. Note: no need to + * guard against RejectedExecutionException, because + * DsfSession.getSession() above would only return an active session. + */ + GetSupportFormatsValueQuery query = new GetSupportFormatsValueQuery(dmc); + session.getExecutor().execute(query); + + /* + * Now we have the data, go and get it. Since the call is completed now + * the ".get()" will not suspend it will immediately return with the + * data. + */ + try { + return (String[]) query.get(); + } catch (InterruptedException e) { + assert false; + return null; + } catch (ExecutionException e) { + return null; + } + } + + public class GetFormattedValueValueQuery extends Query<Object> { + + private IFormattedDataDMContext fDmc; + private String fFormatId; + + public GetFormattedValueValueQuery(IFormattedDataDMContext dmc, String formatId) { + super(); + fDmc = dmc; + fFormatId = formatId; + } + + @Override + protected void execute(final DataRequestMonitor<Object> rm) { + /* + * We're in another dispatch, so we must guard against executor + * shutdown again. + */ + final DsfSession session = DsfSession.getSession(fDmc.getSessionId()); + if (session == null) { + cancel(false); + rm.done(); + return; + } + + /* + * Guard against a disposed service + */ + IExpressions service = getService(); + if (service == null) { + rm .setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, "Service unavailable", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + /* + * Convert to the proper formatting DMC then go get the formatted value. + */ + + FormattedValueDMContext formDmc = service.getFormattedValueContext(fDmc, fFormatId); + + service.getFormattedExpressionValue(formDmc, new DataRequestMonitor<FormattedValueDMData>(session.getExecutor(), rm) { + @Override + protected void handleSuccess() { + /* + * All good set return value. + */ + rm.setData(getData().getFormattedValue()); + rm.done(); + } + }); + } + } + + public String getFormattedValue(Object element, String formatId) { + + /* + * Get the DMC and the session. If element is not an register DMC, or + * session is stale, then bail out. + */ + IFormattedDataDMContext dmc = getFormattedDMC(element); + if (dmc == null) return null; + DsfSession session = DsfSession.getSession(dmc.getSessionId()); + if (session == null) return null; + + /* + * Create the query to write the value to the service. Note: no need to + * guard against RejectedExecutionException, because + * DsfSession.getSession() above would only return an active session. + */ + GetFormattedValueValueQuery query = new GetFormattedValueValueQuery(dmc, formatId); + session.getExecutor().execute(query); + + /* + * Now we have the data, go and get it. Since the call is completed now + * the ".get()" will not suspend it will immediately return with the + * data. + */ + try { + return (String) query.get(); + } catch (InterruptedException e) { + assert false; + return null; + } catch (ExecutionException e) { + return null; + } + } + + /** + * @since 1.1 + */ + public class CanWriteExpressionQuery extends Query<Boolean> { + + private IExpressionDMContext fDmc; + + public CanWriteExpressionQuery(IExpressionDMContext dmc) { + super(); + fDmc = dmc; + } + + @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(fDmc.getSessionId()); + if (session == null) { + cancel(false); + rm.done(); + return; + } + + /* + * Guard against a disposed service + */ + IExpressions service = getService(); + if (service == null) { + rm .setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, "Service unavailable", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + service.canWriteExpression(fDmc, new DataRequestMonitor<Boolean>(session.getExecutor(), rm) { + @Override + protected void handleSuccess() { + /* + * All good set return value. + */ + rm.setData(getData()); + rm.done(); + } + }); + } + } + + public boolean canWriteExpression(Object element) { + /* + * Get the DMC and the session. If element is not an expression DMC, or + * session is stale, then bail out. + */ + IExpressionDMContext dmc = getExpressionDMC(element); + if (dmc == null) return false; + DsfSession session = DsfSession.getSession(dmc.getSessionId()); + if (session == null) return false; + + /* + * Create the query to make the request to the service. Note: no need to + * guard against RejectedExecutionException, because + * DsfSession.getSession() above would only return an active session. + */ + CanWriteExpressionQuery query = new CanWriteExpressionQuery(dmc); + session.getExecutor().execute(query); + + /* + * Now we have the data, go and get it. Since the call is completed now + * the ".get()" will not suspend it will immediately return with the + * data. + */ + try { + return query.get(); + } catch (InterruptedException e) { + assert false; + return false; + } catch (ExecutionException e) { + return false; + } + } + + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/VariableCellModifier.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/VariableCellModifier.java new file mode 100644 index 00000000000..7d558cbbd5e --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/VariableCellModifier.java @@ -0,0 +1,138 @@ +/******************************************************************************* + * Copyright (c) 2007, 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.variable; + +import org.eclipse.cdt.dsf.debug.service.IFormattedValues; +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.IDebugVMConstants; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.expression.WatchExpressionCellModifier; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.numberformat.IFormattedValuePreferenceStore; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMContext; +import org.eclipse.cdt.dsf.ui.viewmodel.update.AbstractCachingVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.update.UserEditEvent; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; + +@SuppressWarnings("restriction") +public class VariableCellModifier extends WatchExpressionCellModifier { + + private AbstractCachingVMProvider fProvider; + private SyncVariableDataAccess fDataAccess = null; + private IFormattedValuePreferenceStore fPrefStore; + + public VariableCellModifier(AbstractCachingVMProvider provider, + IFormattedValuePreferenceStore formattedValuePreferenceStore, SyncVariableDataAccess access) + { + fProvider = provider; + fDataAccess = access; + fPrefStore = formattedValuePreferenceStore; + } + + /* + * Used to make sure we are dealing with a valid variable. + */ + private IExpressionDMContext getVariableDMC(Object element) { + if (element instanceof IAdaptable) { + return (IExpressionDMContext)((IAdaptable)element).getAdapter(IExpressionDMContext.class); + } + return null; + } + + @Override + public boolean canModify(Object element, String property) { + // If we're in the column value, modify the register data. Otherwise, call the super-class to edit + // the watch expression. + + if (IDebugVMConstants.COLUMN_ID__VALUE.equals(property)) { + // Make sure we are are dealing with a valid set of information. + + if (getVariableDMC(element) == null) { + return false; + } + + return fDataAccess.canWriteExpression(element); + } + + return super.canModify(element, property); + } + + @Override + public Object getValue(Object element, String property) { + // If we're in the column value, modify the variable value. Otherwise, call the super-class to edit + // the watch expression. + + if (IDebugVMConstants.COLUMN_ID__VALUE.equals(property)) { + /* + * We let the Model provider supply the current format. + */ + String formatId; + + if ( element instanceof IVMContext) { + /* + * Find the presentation context and then use it to get the current desired format. + */ + IVMContext ctx = (IVMContext) element; + IPresentationContext presCtx = ctx.getVMNode().getVMProvider().getPresentationContext(); + + formatId = fPrefStore.getCurrentNumericFormat(presCtx); + } + else { + formatId = IFormattedValues.NATURAL_FORMAT; + } + + String value = fDataAccess.getFormattedValue(element, formatId); + + if (value == null) { + return "..."; //$NON-NLS-1$ + } + + return value; + } + + return super.getValue(element, property); + } + + @Override + public void modify(Object element, String property, Object value) { + /* + * If we're in the column value, modify the register data. Otherwise, call the super-class to edit + * the watch expression. + */ + if (IDebugVMConstants.COLUMN_ID__VALUE.equals(property)) { + if (value instanceof String) { + /* + * We let the Model provider supply the current format. + */ + String formatId; + + if ( element instanceof IVMContext) { + /* + * Find the presentation context and then use it to get the current desired format. + */ + IVMContext ctx = (IVMContext) element; + IPresentationContext presCtx = ctx.getVMNode().getVMProvider().getPresentationContext(); + + formatId = fPrefStore.getCurrentNumericFormat(presCtx); + } + else { + formatId = IFormattedValues.NATURAL_FORMAT; + } + + fDataAccess.writeVariable(element, (String) value, formatId); + fProvider.handleEvent(new UserEditEvent(element)); + } + } + else { + super.modify(element, property, value); + } + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/VariableColumnPresentation.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/VariableColumnPresentation.java new file mode 100644 index 00000000000..725b02c0c59 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/VariableColumnPresentation.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * 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.variable; + +import org.eclipse.cdt.dsf.debug.ui.viewmodel.IDebugVMConstants; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.jface.resource.ImageDescriptor; + +/** + * + */ +@SuppressWarnings("restriction") +public class VariableColumnPresentation implements IColumnPresentation { + public static final String ID = DsfUIPlugin.PLUGIN_ID + ".VARIABLES_COLUMN_PRESENTATION_ID"; //$NON-NLS-1$ + + // @see org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentation#init(org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext) + public void init(IPresentationContext context) {} + + // @see org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentation#dispose() + public void dispose() {} + + // @see org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentation#getAvailableColumns() + public String[] getAvailableColumns() { + return new String[] { IDebugVMConstants.COLUMN_ID__NAME, IDebugVMConstants.COLUMN_ID__TYPE, IDebugVMConstants.COLUMN_ID__VALUE, IDebugVMConstants.COLUMN_ID__ADDRESS }; + } + + // @see org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentation#getHeader(java.lang.String) + public String getHeader(String id) { + if (IDebugVMConstants.COLUMN_ID__NAME.equals(id)) { + return MessagesForVariablesVM.VariableColumnPresentation_name; + } else if (IDebugVMConstants.COLUMN_ID__TYPE.equals(id)) { + return MessagesForVariablesVM.VariableColumnPresentation_type; + } else if (IDebugVMConstants.COLUMN_ID__VALUE.equals(id)) { + return MessagesForVariablesVM.VariableColumnPresentation_value; + } else if (IDebugVMConstants.COLUMN_ID__ADDRESS.equals(id)) { + return MessagesForVariablesVM.VariableColumnPresentation_address; + } + return null; + } + + // @see org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentation#getId() + public String getId() { + return ID; + } + + // @see org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentation#getImageDescriptor(java.lang.String) + public ImageDescriptor getImageDescriptor(String id) { + return null; + } + + // @see org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentation#getInitialColumns() + public String[] getInitialColumns() { + return new String[] { IDebugVMConstants.COLUMN_ID__NAME, IDebugVMConstants.COLUMN_ID__TYPE, IDebugVMConstants.COLUMN_ID__VALUE }; + } + + // @see org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentation#isOptional() + public boolean isOptional() { + return true; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/VariableVMNode.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/VariableVMNode.java new file mode 100644 index 00000000000..bed8ab511e7 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/VariableVMNode.java @@ -0,0 +1,995 @@ +/******************************************************************************* + * 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.variable; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.core.IAddress; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.MultiRequestMonitor; +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.IExpressions; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues; +import org.eclipse.cdt.dsf.debug.service.IStack; +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionChangedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMAddress; +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.IFormattedValues.FormattedValueDMContext; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMData; +import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryChangedEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; +import org.eclipse.cdt.dsf.debug.service.IStack.IVariableDMContext; +import org.eclipse.cdt.dsf.debug.service.IStack.IVariableDMData; +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.IExpressionUpdate; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.numberformat.IFormattedValuePreferenceStore; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.numberformat.IFormattedValueVMContext; +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.IVMContext; +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.utils.Addr32; +import org.eclipse.cdt.utils.Addr64; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.model.IExpression; +import org.eclipse.debug.internal.ui.DebugUIPlugin; +import org.eclipse.debug.internal.ui.IInternalDebugUIConstants; +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.internal.ui.viewers.model.provisional.IViewerUpdate; +import org.eclipse.debug.ui.actions.IWatchExpressionFactoryAdapter2; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.util.PropertyChangeEvent; +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.graphics.RGB; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.ui.IMemento; + +@SuppressWarnings({"restriction", "nls"}) +public class VariableVMNode extends AbstractExpressionVMNode + implements IElementEditor, IElementLabelProvider, IElementMementoProvider +{ + + private final IFormattedValuePreferenceStore fFormattedPrefStore; + + private final SyncVariableDataAccess fSyncVariableDataAccess; + + public class VariableExpressionVMC extends DMVMContext implements IFormattedValueVMContext { + + private IExpression fExpression; + + public VariableExpressionVMC(IDMContext dmc) { + super(dmc); + } + + public IFormattedValuePreferenceStore getPreferenceStore() { + return fFormattedPrefStore; + } + + public void setExpression(IExpression expression) { + fExpression = expression; + } + + @Override + @SuppressWarnings("unchecked") + public Object getAdapter(Class adapter) { + if (fExpression != null && adapter.isAssignableFrom(fExpression.getClass())) { + return fExpression; + } else if (adapter.isAssignableFrom(IWatchExpressionFactoryAdapter2.class)) { + return fVariableExpressionFactory; + } else { + return super.getAdapter(adapter); + } + } + + @Override + public boolean equals(Object other) { + if (other instanceof VariableExpressionVMC && super.equals(other)) { + VariableExpressionVMC otherGroup = (VariableExpressionVMC)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 VariableExpressionFactory implements IWatchExpressionFactoryAdapter2 { + + public boolean canCreateWatchExpression(Object element) { + return element instanceof VariableExpressionVMC; + } + + public String createWatchExpression(Object element) throws CoreException { + + VariableExpressionVMC exprVmc = (VariableExpressionVMC) element; + + IExpressionDMContext exprDmc = DMContexts.getAncestorOfType(exprVmc.getDMContext(), IExpressionDMContext.class); + if (exprDmc != null) { + return exprDmc.getExpression(); + } + + return null; + } + } + + final protected VariableExpressionFactory fVariableExpressionFactory = new VariableExpressionFactory(); + + public VariableVMNode(IFormattedValuePreferenceStore prefStore, AbstractDMVMProvider provider, + DsfSession session, SyncVariableDataAccess syncVariableDataAccess) + { + super(provider, session, IExpressions.IExpressionDMContext.class); + fFormattedPrefStore = prefStore; + fSyncVariableDataAccess = syncVariableDataAccess; + } + + @Override + public String toString() { + return "VariableVMNode(" + getSession().getId() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + } + + @Override + protected IDMVMContext createVMContext(IDMContext dmc) { + return new VariableExpressionVMC(dmc); + } + + + public void update(final ILabelUpdate[] updates) { + try { + getSession().getExecutor().execute(new DsfRunnable() { + public void run() { + updateLabelInSessionThread(updates); + }}); + } catch (RejectedExecutionException e) { + for (ILabelUpdate update : updates) { + handleFailedUpdate(update); + } + } + } + + private void fillInExpressionErrorInfo( ILabelUpdate update, IExpressionDMContext dmc, IStatus status ) { + /* + * Instead of just failing this outright we are going to attempt to do more here. + * Failing it outright causes the view to display ... for all columns in the line + * and this is uninformative about what is happening. It will be very common that + * one or more variables at that given instance in time are not evaluatable. They + * may be out of scope and will come back into scope later. + */ + String[] localColumns = update.getColumnIds(); + if (localColumns == null) + localColumns = new String[] { IDebugVMConstants.COLUMN_ID__NAME }; + + for (int idx = 0; idx < localColumns.length; idx++) { + if (IDebugVMConstants.COLUMN_ID__NAME.equals(localColumns[idx])) { + update.setLabel(dmc.getExpression(), idx); + } else if (IDebugVMConstants.COLUMN_ID__TYPE.equals(localColumns[idx])) { + update.setLabel("", idx); + } else if (IDebugVMConstants.COLUMN_ID__VALUE.equals(localColumns[idx])) { + update.setLabel("Error : " + status.getMessage(), idx); + } else if (IDebugVMConstants.COLUMN_ID__ADDRESS.equals(localColumns[idx])) { + update.setLabel("", idx); + } else if (IDebugVMConstants.COLUMN_ID__DESCRIPTION.equals(localColumns[idx])) { + update.setLabel("", idx); + } else if (IDebugVMConstants.COLUMN_ID__EXPRESSION.equals(localColumns[idx])) { + update.setLabel(dmc.getExpression(), idx); + } else { + update.setLabel("", idx); + } + update.setFontData(JFaceResources.getFontDescriptor(IInternalDebugUIConstants.VARIABLE_TEXT_FONT).getFontData()[0], idx); + } + } + + protected void updateLabelInSessionThread(ILabelUpdate[] updates) { + for (final ILabelUpdate update : updates) { + + final IExpressionDMContext dmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), IExpressions.IExpressionDMContext.class); + + if ( dmc == null ) { + // Workaround for a bug in platform, where the find operation may use wrong label provider. + // See bug 246618. + update.done(); + continue; + } + + getDMVMProvider().getModelData( + this, update, + getServicesTracker().getService(IExpressions.class, null), + dmc, + new ViewerDataRequestMonitor<IExpressionDMData>(getSession().getExecutor(), update) { + @Override + protected void handleCompleted() { + // Check that the request was evaluated and data is still valid. The request could + // fail if the state of the service changed during the request, but the view model + // has not been updated yet. + if (!isSuccess()) { + assert getStatus().isOK() || + getStatus().getCode() != IDsfStatusConstants.INTERNAL_ERROR || + getStatus().getCode() != IDsfStatusConstants.NOT_SUPPORTED; + + fillInExpressionErrorInfo( update, dmc, getStatus() ); + + update.done(); + return; + } + + // If columns are configured, extract the selected values for each understood column. + // First, we fill all of those columns which can be filled without extra data mining. + // We also note if we do have to do extra data mining. Any columns need to set the + // processing flag so we know we have further work to do. If there are more columns + // which need data extraction they need to be added in both "for" loops. + String[] localColumns = update.getColumnIds(); + if (localColumns == null) + localColumns = new String[] { IDebugVMConstants.COLUMN_ID__NAME }; + + int extractingFormattedDataIndex = -1; + int extractingAddressDataIndex = -1; + + for (int idx = 0; idx < localColumns.length; idx++) { + if (IDebugVMConstants.COLUMN_ID__NAME.equals(localColumns[idx])) { + update.setLabel(getData().getName(), idx); + } else if (IDebugVMConstants.COLUMN_ID__TYPE.equals(localColumns[idx])) { + update.setLabel(getData().getTypeName(), idx); + } else if (IDebugVMConstants.COLUMN_ID__VALUE.equals(localColumns[idx])) { + extractingFormattedDataIndex = idx; + } else if (IDebugVMConstants.COLUMN_ID__ADDRESS.equals(localColumns[idx])) { + extractingAddressDataIndex = idx; + } else if (IDebugVMConstants.COLUMN_ID__DESCRIPTION.equals(localColumns[idx])) { + update.setLabel("", idx); + } else if (IDebugVMConstants.COLUMN_ID__EXPRESSION.equals(localColumns[idx])) { + IVMContext vmc = (IVMContext)update.getElement(); + IExpression expression = (IExpression)vmc.getAdapter(IExpression.class); + if (expression != null) { + update.setLabel(expression.getExpressionText(), idx); + } else { + update.setLabel(getData().getName(), idx); + } + } + update.setFontData(JFaceResources.getFontDescriptor(IInternalDebugUIConstants.VARIABLE_TEXT_FONT).getFontData()[0], idx); + } + + if ( ( extractingFormattedDataIndex == -1 ) && ( extractingAddressDataIndex == -1 ) ) { + update.done(); + } else { + /* + * We are either updating the value or the address or possibly both. + * We will create a overarching monitor to handle completing the update + * when either/both of the lower level updates are done. + */ + final DsfExecutor dsfExecutor = getSession().getExecutor(); + + final MultiRequestMonitor<RequestMonitor> mrm = + new MultiRequestMonitor<RequestMonitor>(dsfExecutor, null) { + @Override + public void handleCompleted() { + if (!isSuccess()) { + handleFailedUpdate(update); + return; + } + update.done(); + } + }; + + /* + * Deal with the value. + */ + if ( extractingFormattedDataIndex != -1 ) { + RequestMonitor rm = new RequestMonitor(dsfExecutor, null) { + @Override + public void handleCompleted() { + mrm.requestMonitorDone(this); + } + }; + + mrm.add(rm); + updateFormattedExpressionValue(update, extractingFormattedDataIndex, dmc, getData(),rm); + } + + /* + * Deal with the address. + */ + if ( extractingAddressDataIndex != -1 ) { + RequestMonitor rm = new RequestMonitor(dsfExecutor, null) { + @Override + public void handleCompleted() { + mrm.requestMonitorDone(this); + } + }; + + mrm.add(rm); + updateAddressData(update, extractingAddressDataIndex, dmc, rm); + } + } + } + }, + getExecutor() + ); + } + } + + /** + * Private data access routine which performs the extra level of data access needed to + * get the formatted data value for a specific register. + */ + private void updateAddressData(final ILabelUpdate update, + final int labelIndex, + final IExpressionDMContext dmc, + final RequestMonitor monitor) + { + /* + * First select the format to be used. This involves checking so see that the preference + * page format is supported by the register service. If the format is not supported then + * we will pick the first available format. + */ + final IExpressions expressionService = getServicesTracker().getService(IExpressions.class); + + // Get the variable information and update the corresponding memory locations + if (expressionService != null) { + expressionService.getExpressionAddressData(dmc, + new DataRequestMonitor<IExpressionDMAddress>(getExecutor(), monitor) { + @Override + protected void handleCompleted() { + if ( isSuccess() ) { + // Figure out which memory area was modified + IExpressionDMAddress expression = getData(); + IAddress expAddress = expression.getAddress(); + if (expAddress instanceof Addr64) { + update.setLabel( "0x" + ((Addr64) expAddress).toString(16), labelIndex); + } + else if (expAddress instanceof Addr32) { + update.setLabel( "0x" + ((Addr32) expAddress).toString(16), labelIndex); + } + else { + update.setLabel( "Unknown address format", labelIndex); + } + } + else { + /* + * We could not get the format. Currently GDB does not handle getting the address of + * a constant for example. We could put the error message in, but that would not be + * all that helpful top the user. The interface is a new one and perhaps failing to + * return a valid set of information is just saying it does not exist. Anyway, for + * now we will just put nothing in. + */ + update.setLabel( "", labelIndex); + } + + update.setFontData(JFaceResources.getFontDescriptor(IInternalDebugUIConstants.VARIABLE_TEXT_FONT).getFontData()[0], labelIndex); + monitor.done(); + } + } + ); + } + } + + /** + * Private data access routine which performs the extra level of data access needed to + * get the formatted data value for a specific register. + */ + private void updateFormattedExpressionValue(final ILabelUpdate update, + final int labelIndex, + final IExpressionDMContext dmc, + final IExpressionDMData expressionDMData, + final RequestMonitor monitor) + { + final IExpressions expressionService = getServicesTracker().getService(IExpressions.class); + /* + * First select the format to be used. This involves checking so see that the preference + * page format is supported by the register service. If the format is not supported then + * we will pick the first available format. + */ + final IPresentationContext context = update.getPresentationContext(); + final String preferencePageFormatId = fFormattedPrefStore.getCurrentNumericFormat(context) ; + + expressionService.getAvailableFormats( + dmc, + new DataRequestMonitor<String[]>(getSession().getExecutor(), monitor) { + @Override + public void handleCompleted() { + if (!isSuccess()) { + monitor.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, "Format information not available", null)); + monitor.done(); + return; + } + + /* + * See if the desired format is supported. + */ + final String[] formatIds = getData(); + String finalFormatId = IFormattedValues.NATURAL_FORMAT; + boolean requestedFormatIsSupported = false; + + for ( String fId : formatIds ) { + if ( preferencePageFormatId.equals(fId) ) { + // The desired format is supported. + + finalFormatId = preferencePageFormatId; + requestedFormatIsSupported = true; + break; + } + } + + if ( ! requestedFormatIsSupported ) { + /* + * Desired format is not supported. If there are any formats supported + * then use the first available. + */ + if ( formatIds.length != 0 ) { + finalFormatId = formatIds[0]; + } + else { + // Expression service does not support any format. + + monitor.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, "Service does not support any formats", null)); + monitor.done(); + return; + } + } + + /* + * Format has been validated. Get the formatted value. + */ + final FormattedValueDMContext valueDmc = expressionService.getFormattedValueContext(dmc, finalFormatId); + + getDMVMProvider().getModelData( + VariableVMNode.this, + update, + expressionService, + valueDmc, + new DataRequestMonitor<FormattedValueDMData>(getSession().getExecutor(), monitor) { + @Override + public void handleCompleted() { + if (!isSuccess()) { + monitor.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, getStatus().getMessage(), null)); + monitor.done(); + return; + } + + final String formattedValue = getData().getFormattedValue(); + final String formattedStringId = valueDmc.getFormatID(); + + if ( formattedStringId.equals(IFormattedValues.STRING_FORMAT) ) { + /* + * In this case we are being asked to fill in the value information with STRING_FORMAT. + * So we do not need to append it to the value as we did in the past. + */ + completeFillinInUpdateWithValue(update, labelIndex, valueDmc, formattedValue, null, null, monitor); + } + else { + /* + * The format specified is not STRING_FORMAT and as we did before we need to append + * the string information to the value ( if it exists ). So first see if STRING_FORMAT + * is supported by the service. + */ + boolean foundStringFormat = false; + + for ( String format : formatIds ) { + if ( format.equals(IFormattedValues.STRING_FORMAT) ) { + foundStringFormat = true; + } + } + + if ( foundStringFormat ) { + /* + * So STRING_FORMAT is supported so we can go get it and append it to the value. + * + * Note : Currently the Reference Model MI Expression Service does not support the + * STRING_FORMAT. The view still pretty much looks the same however, to one + * where the STRING_FORMAT is supplied. This is because when GDB is ask to + * evaluate a variable it will return the STRING_FORMAT information appended + * to the address so it looks good. GDB appends all kinds of usefull info to + * requests for data values, based on the value types. So the expressions do + * look good. If the Reference Model Expression Service ever does implement + * STRING_FORMAT this will need to be revisited. There would be duplicate + * information displayed and the view would look broken. However this needs + * to be put back in to satisfy Bugzilla defect "225612", which represents a + * regression in the display of data from 0.9 to 1.x. + */ + final FormattedValueDMContext stringDmc = expressionService.getFormattedValueContext(dmc, IFormattedValues.STRING_FORMAT); + + getDMVMProvider().getModelData( + VariableVMNode.this, + update, + expressionService, + stringDmc, + new DataRequestMonitor<FormattedValueDMData>(getSession().getExecutor(), monitor) { + @Override + public void handleCompleted() { + if (!isSuccess()) { + monitor.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, getStatus().getMessage(), null)); + monitor.done(); + return; + } + + String stringValue = getData().getFormattedValue(); + + completeFillinInUpdateWithValue(update, labelIndex, valueDmc, formattedValue, stringDmc, stringValue, monitor); + } + }, + getExecutor() + ); + } + else { + /* + * The STRING_FORMAT is not supported. So all we can do is fill it in without it. + */ + completeFillinInUpdateWithValue(update, labelIndex, valueDmc, formattedValue, null, null, monitor); + } + } + } + }, + getExecutor() + ); + } + } + ); + } + + private void completeFillinInUpdateWithValue(ILabelUpdate update, + int labelIndex, + FormattedValueDMContext valueDmc, + String value, + FormattedValueDMContext stringFormatDmc, + String stringFormatValue, + RequestMonitor monitor) + { + /* + * Complete filling in the VALUE. The form is + * + * "Numerical value" "STRING_FORMAT value" + * + * This makes it so if the value is a pointer to something else we conveniently + * fill in the something else ( typically a string ). + */ + + StringBuffer stringValueBuf = new StringBuffer(value); + if(stringFormatValue != null && stringFormatValue.length() > 0) + { + stringValueBuf.append(" "); + stringValueBuf.append(stringFormatValue); + } + update.setLabel(stringValueBuf.toString(), labelIndex); + update.setFontData(JFaceResources.getFontDescriptor(IInternalDebugUIConstants.VARIABLE_TEXT_FONT).getFontData()[0], labelIndex); + + /* + * Get old values for comparison ( if available ). + */ + FormattedValueDMData oldStringData = null; + FormattedValueDMData oldData = + + (FormattedValueDMData) getDMVMProvider().getArchivedModelData(VariableVMNode.this, update, valueDmc); + + if ( stringFormatDmc != null) { + oldStringData = (FormattedValueDMData) getDMVMProvider().getArchivedModelData(VariableVMNode.this, update, stringFormatDmc); + } + + /* + * Highlight the value if either the value (address) has changed or the string (memory at the value) has changed + */ + if ( ( oldData != null && ! oldData.getFormattedValue().equals(value) ) || + ( oldStringData != null && ! oldStringData.getFormattedValue().equals(stringFormatValue) ) + ) { + RGB rgb = DebugUIPlugin.getPreferenceColor(IInternalDebugUIConstants.PREF_CHANGED_VALUE_BACKGROUND).getRGB(); + update.setBackground(rgb, labelIndex); + } + + /* + * Now we finally can complete this one. + */ + monitor.done(); + } + + public CellEditor getCellEditor(IPresentationContext context, String columnId, Object element, Composite parent) { + if (IDebugVMConstants.COLUMN_ID__VALUE.equals(columnId)) { + return new TextCellEditor(parent); + } + else if (IDebugVMConstants.COLUMN_ID__EXPRESSION.equals(columnId)) { + return new TextCellEditor(parent); + } + + return null; + } + + public ICellModifier getCellModifier(IPresentationContext context, Object element) { + return new VariableCellModifier(getDMVMProvider(), fFormattedPrefStore, fSyncVariableDataAccess); + } + + public boolean canParseExpression(IExpression expression) { + // At this point we are going to say we will allow anything as an expression. + // Since the evaluation of VM Node implementations searches in the order of + // registration and we always make sure we register the VariableVMNode last, + // we know that the other possible handlers have passed the expression by. So + // we are going to say OK and let the expression evaluation of whatever debug + // backend is connected to decide. This does not allow us to put up any good + // diagnostic error message ( instead the error will come from the backend ). + // But it does allow for the most flexibility + + return true; + } + + @Override + public void update(final IExpressionUpdate update) { + try { + getSession().getExecutor().execute(new Runnable() { + public void run() { + final IExpressions expressionService = getServicesTracker().getService(IExpressions.class); + if (expressionService != null) { + IExpressionDMContext expressionDMC = expressionService.createExpression( + createCompositeDMVMContext(update), + update.getExpression().getExpressionText()); + VariableExpressionVMC variableVmc = new VariableExpressionVMC(expressionDMC); + variableVmc.setExpression(update.getExpression()); + + update.setExpressionElement(variableVmc); + update.done(); + } else { + handleFailedUpdate(update); + } + } + }); + } catch (RejectedExecutionException e) { + handleFailedUpdate(update); + } + } + + + @Override + protected void handleFailedUpdate(IViewerUpdate update) { + if (update instanceof IExpressionUpdate) { + update.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, "Update failed", null)); //$NON-NLS-1$ + update.done(); + } else { + super.handleFailedUpdate(update); + } + } + @Override + protected void associateExpression(Object element, IExpression expression) { + if (element instanceof VariableExpressionVMC) { + ((VariableExpressionVMC)element).setExpression(expression); + } + } + + @Override + protected void updateElementsInSessionThread(final IChildrenUpdate update) { + // Get the data model context object for the current node in the hierarchy. + + final IExpressionDMContext expressionDMC = findDmcInPath(update.getViewerInput(), update.getElementPath(), IExpressionDMContext.class); + + if ( expressionDMC != null ) { + getSubexpressionsUpdateElementsInSessionThread( update ); + } + else { + getLocalsUpdateElementsInSessionThread( update ); + } + } + + private void getSubexpressionsUpdateElementsInSessionThread(final IChildrenUpdate update) { + + final IExpressionDMContext expressionDMC = findDmcInPath(update.getViewerInput(), update.getElementPath(), IExpressionDMContext.class); + + if ( expressionDMC != null ) { + + // Get the services we need to use. + + final IExpressions expressionService = getServicesTracker().getService(IExpressions.class); + + if (expressionService == null) { + handleFailedUpdate(update); + return; + } + + final DsfExecutor dsfExecutor = getSession().getExecutor(); + + // Call IExpressions.getSubExpressions() to get an Iterable of IExpressionDMContext objects representing + // the sub-expressions of the expression represented by the current expression node. + + final DataRequestMonitor<IExpressionDMContext[]> rm = + new ViewerDataRequestMonitor<IExpressionDMContext[]>(dsfExecutor, update) { + @Override + public void handleCompleted() { + if (!isSuccess()) { + handleFailedUpdate(update); + return; + } + fillUpdateWithVMCs(update, getData()); + update.done(); + } + }; + + // Make the asynchronous call to IExpressions.getSubExpressions(). The results are processed in the + // DataRequestMonitor.handleCompleted() above. + + expressionService.getSubExpressions(expressionDMC, rm); + } else { + handleFailedUpdate(update); + } + } + + private void getLocalsUpdateElementsInSessionThread(final IChildrenUpdate update) { + + final IFrameDMContext frameDmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), IFrameDMContext.class); + + // Get the services we need to use. + + final IExpressions expressionService = getServicesTracker().getService(IExpressions.class); + final IStack stackFrameService = getServicesTracker().getService(IStack.class); + + if ( frameDmc == null || expressionService == null || stackFrameService == null) { + handleFailedUpdate(update); + return; + } + + final DsfExecutor dsfExecutor = getSession().getExecutor(); + + // Call IStack.getLocals() to get an array of IVariableDMContext objects representing the local + // variables in the stack frame represented by frameDmc. + + final DataRequestMonitor<IVariableDMContext[]> rm = + new ViewerDataRequestMonitor<IVariableDMContext[]>(dsfExecutor, update) { + @Override + public void handleCompleted() { + if (!isSuccess()) { + handleFailedUpdate(update); + return; + } + + // For each IVariableDMContext object returned by IStack.getLocals(), call + // MIStackFrameService.getModelData() to get the IVariableDMData object. This requires + // a MultiRequestMonitor object. + + // First, get the data model context objects for the local variables. + + IVariableDMContext[] localsDMCs = getData(); + + if (localsDMCs == null) { + handleFailedUpdate(update); + return; + } + + if ( localsDMCs.length == 0 ) { + // There are no locals so just complete the request + update.done(); + return; + } + + // Create a List in which we store the DM data objects for the local variables. This is + // necessary because there is no MultiDataRequestMonitor. :) + + final List<IVariableDMData> localsDMData = new ArrayList<IVariableDMData>(); + + // Create the MultiRequestMonitor to handle completion of the set of getModelData() calls. + + final MultiRequestMonitor<DataRequestMonitor<IVariableDMData>> mrm = + new MultiRequestMonitor<DataRequestMonitor<IVariableDMData>>(dsfExecutor, null) { + @Override + public void handleCompleted() { + // Now that all the calls to getModelData() are complete, we create an + // IExpressionDMContext object for each local variable name, saving them all + // in an array. + + if (!isSuccess()) { + handleFailedUpdate(update); + return; + } + + IExpressionDMContext[] expressionDMCs = new IExpressionDMContext[localsDMData.size()]; + + int i = 0; + + for (IVariableDMData localDMData : localsDMData) { + expressionDMCs[i++] = expressionService.createExpression(frameDmc, localDMData.getName()); + } + + // Lastly, we fill the update from the array of view model context objects + // that reference the ExpressionDMC objects for the local variables. This is + // the last code to run for a given call to updateElementsInSessionThread(). + // We can now leave anonymous-inner-class hell. + + fillUpdateWithVMCs(update, expressionDMCs); + update.done(); + } + }; + + // Perform a set of getModelData() calls, one for each local variable's data model + // context object. In the handleCompleted() method of the DataRequestMonitor, add the + // IVariableDMData object to the localsDMData List for later processing (see above). + + for (IVariableDMContext localDMC : localsDMCs) { + DataRequestMonitor<IVariableDMData> rm = + new ViewerDataRequestMonitor<IVariableDMData>(dsfExecutor, update) { + @Override + public void handleCompleted() { + localsDMData.add(getData()); + mrm.requestMonitorDone(this); + } + }; + + mrm.add(rm); + + getDMVMProvider().getModelData(VariableVMNode.this, update, stackFrameService, localDMC, rm, getExecutor()); + } + } + }; + + // Make the asynchronous call to IStack.getLocals(). The results are processed in the + // DataRequestMonitor.handleCompleted() above. + + stackFrameService.getLocals(frameDmc, rm); + } + + //private final static int MAX_STRING_VALUE_LENGTH = 40; + + public int getDeltaFlags(Object e) { + if ( e instanceof ISuspendedDMEvent || + e instanceof IMemoryChangedEvent || + e instanceof IExpressionChangedDMEvent || + (e instanceof PropertyChangeEvent && + ((PropertyChangeEvent)e).getProperty() == IDebugVMConstants.CURRENT_FORMAT_STORAGE) ) + { + // Create a delta that the whole register group has changed. + return IModelDelta.CONTENT; + } + + return IModelDelta.NO_CHANGE; + } + + public void buildDelta(final Object e, final VMDelta parentDelta, final int nodeOffset, final RequestMonitor requestMonitor) { + + // The following events can affect any expression's values, + // refresh the contents of the parent element (i.e. all the expressions). + if ( e instanceof ISuspendedDMEvent || + e instanceof IMemoryChangedEvent || + e instanceof IExpressionChangedDMEvent || + (e instanceof PropertyChangeEvent && + ((PropertyChangeEvent)e).getProperty() == IDebugVMConstants.CURRENT_FORMAT_STORAGE) ) + { + // Create a delta that the whole register group has changed. + parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); + } + + requestMonitor.done(); + } + + public int getDeltaFlagsForExpression(IExpression expression, Object event) { + if ( event instanceof IExpressionChangedDMEvent || + event instanceof IMemoryChangedEvent || + (event instanceof PropertyChangeEvent && + ((PropertyChangeEvent)event).getProperty() == IDebugVMConstants.CURRENT_FORMAT_STORAGE) ) + { + return IModelDelta.CONTENT; + } + + if (event instanceof ISuspendedDMEvent) + { + return IModelDelta.CONTENT; + } + + return IModelDelta.NO_CHANGE; + } + + public void buildDeltaForExpression(IExpression expression, int elementIdx, Object event, VMDelta parentDelta, + TreePath path, RequestMonitor rm) + { + // Always refresh the contents of the view upon suspended event. + if (event instanceof ISuspendedDMEvent) { + parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); + } + + rm.done(); + } + + public void buildDeltaForExpressionElement(Object element, int elementIdx, Object event, VMDelta parentDelta, + RequestMonitor rm) + { + // The following events can affect expression values, refresh the state + // of the expression. + if ( event instanceof IExpressionChangedDMEvent || + event instanceof IMemoryChangedEvent || + (event instanceof PropertyChangeEvent && + ((PropertyChangeEvent)event).getProperty() == IDebugVMConstants.CURRENT_FORMAT_STORAGE) ) + { + parentDelta.addNode(element, IModelDelta.CONTENT); + } + + rm.done(); + } + + + + /* + * (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoProvider#compareElements(org.eclipse.debug.internal.ui.viewers.model.provisional.IElementCompareRequest[]) + */ + private String produceExpressionElementName( String viewName , IExpressionDMContext expression ) { + + return "Variable." + expression.getExpression(); //$NON-NLS-1$ + } + + private final String MEMENTO_NAME = "VARIABLE_MEMENTO_NAME"; //$NON-NLS-1$ + + public void compareElements(IElementCompareRequest[] requests) { + + for ( IElementCompareRequest request : requests ) { + + Object element = request.getElement(); + IMemento memento = request.getMemento(); + String mementoName = memento.getString(MEMENTO_NAME); //$NON-NLS-1$ + + if (mementoName != null) { + if (element instanceof IDMVMContext) { + + IDMContext dmc = ((IDMVMContext)element).getDMContext(); + + if ( dmc instanceof IExpressionDMContext) { + + String elementName = produceExpressionElementName( request.getPresentationContext().getId(), (IExpressionDMContext) dmc ); + request.setEqual( elementName.equals( mementoName ) ); + } + } + } + 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 ( IElementMementoRequest request : requests ) { + + Object element = request.getElement(); + IMemento memento = request.getMemento(); + + if (element instanceof IDMVMContext) { + + IDMContext dmc = ((IDMVMContext)element).getDMContext(); + + if ( dmc instanceof IExpressionDMContext) { + + String elementName = produceExpressionElementName( request.getPresentationContext().getId(), (IExpressionDMContext) dmc ); + memento.putString(MEMENTO_NAME, elementName); + } + } + request.done(); + } + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/VariableVMProvider.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/VariableVMProvider.java new file mode 100644 index 00000000000..b6424ae75cf --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/VariableVMProvider.java @@ -0,0 +1,140 @@ +/******************************************************************************* + * Copyright (c) 2007, 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.variable; + +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.debug.service.ICachingService; +import org.eclipse.cdt.dsf.debug.service.IExpressions; +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.update.BreakpointHitUpdatePolicy; +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.IRootVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMNode; +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.cdt.dsf.ui.viewmodel.update.ManualUpdatePolicy; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentationFactory; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; + +@SuppressWarnings("restriction") +public class VariableVMProvider extends AbstractDMVMProvider + implements IColumnPresentationFactory +{ + 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 VariableVMProvider(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)); + + /* + * Create the variable data access routines. + */ + SyncVariableDataAccess varAccess = new SyncVariableDataAccess(session) ; + + /* + * Create the top level node to deal with the root selection. + */ + IRootVMNode rootNode = new RootDMVMNode(this); + setRootNode(rootNode); + + /* + * Create the next level which represents members of structs/unions/enums and elements of arrays. + */ + IVMNode subExpressioNode = new VariableVMNode(FormattedValuePreferenceStore.getDefault(), this, getSession(), varAccess); + addChildNodes(rootNode, new IVMNode[] { subExpressioNode }); + + // Configure the sub-expression node to be a child of itself. This way the content + // provider will recursively drill-down the variable hierarchy. + addChildNodes(subExpressioNode, new IVMNode[] { subExpressioNode }); + } + + @Override + public void dispose() { + DsfDebugUITools.getPreferenceStore().removePropertyChangeListener(fPreferencesListener); + getPresentationContext().removePropertyChangeListener(fPresentationContextListener); + super.dispose(); + } + + @Override + public IColumnPresentation createColumnPresentation(IPresentationContext context, Object element) { + return new VariableColumnPresentation(); + } + + @Override + public String getColumnPresentationId(IPresentationContext context, Object element) { + return VariableColumnPresentation.ID; + } + + @Override + protected IVMUpdatePolicy[] createUpdateModes() { + return new IVMUpdatePolicy[] { new AutomaticUpdatePolicy(), new ManualUpdatePolicy(), new BreakpointHitUpdatePolicy() }; + } + + @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); + } + tracker.dispose(); + } + }); + } catch (RejectedExecutionException e) { + // Session disposed, ignore. + } + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/messages.properties b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/messages.properties new file mode 100644 index 00000000000..0b40d097c33 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/messages.properties @@ -0,0 +1,16 @@ +############################################################################### +# Copyright (c) 2007, 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 +# Wind River Systems - added Address +############################################################################### + +VariableColumnPresentation_name=Name +VariableColumnPresentation_type=Type +VariableColumnPresentation_value=Value +VariableColumnPresentation_address=Address |