diff options
Diffstat (limited to 'dsf')
546 files changed, 69954 insertions, 0 deletions
diff --git a/dsf/org.eclipse.cdt.dsf.ui/.classpath b/dsf/org.eclipse.cdt.dsf.ui/.classpath new file mode 100644 index 00000000000..304e86186aa --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/.classpath @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/> + <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/dsf/org.eclipse.cdt.dsf.ui/.options b/dsf/org.eclipse.cdt.dsf.ui/.options new file mode 100644 index 00000000000..cda9510d97c --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/.options @@ -0,0 +1,10 @@ +org.eclipse.cdt.dsf.ui/debug = false + +org.eclipse.cdt.dsf.ui/debug/vm/contentProvider = false +org.eclipse.cdt.dsf.ui/debug/vm/delta = false +org.eclipse.cdt.dsf.ui/debug/vm/cache = false +org.eclipse.cdt.dsf.ui/debug/vm/presentationId = +org.eclipse.cdt.dsf.ui/debug/vm/atomicUpdate = false + +org.eclipse.cdt.dsf.ui/debug/stepping = false +org.eclipse.cdt.dsf.ui/debug/disassembly = false diff --git a/dsf/org.eclipse.cdt.dsf.ui/.project b/dsf/org.eclipse.cdt.dsf.ui/.project new file mode 100644 index 00000000000..984545aa7a1 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/.project @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>org.eclipse.cdt.dsf.ui</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.ManifestBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.SchemaBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.api.tools.apiAnalysisBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.pde.PluginNature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + <nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature> + </natures> +</projectDescription> diff --git a/dsf/org.eclipse.cdt.dsf.ui/.settings/.api_filters b/dsf/org.eclipse.cdt.dsf.ui/.settings/.api_filters new file mode 100644 index 00000000000..cc3e1bff31e --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/.settings/.api_filters @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<component id="org.eclipse.cdt.dsf.ui" version="2"> + <resource path="src/org/eclipse/dd/dsf/ui/viewmodel/AbstractVMAdapter.java" type="org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMAdapter"> + <filter id="338792546"> + <message_arguments> + <message_argument value="org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMAdapter"/> + <message_argument value="getVMProviderIterable()"/> + </message_arguments> + </filter> + </resource> +</component> diff --git a/dsf/org.eclipse.cdt.dsf.ui/.settings/org.eclipse.jdt.core.prefs b/dsf/org.eclipse.cdt.dsf.ui/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..5d20faf6545 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,70 @@ +#Mon Jun 23 15:02:26 CEST 2008 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.5 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.autoboxing=ignore +org.eclipse.jdt.core.compiler.problem.deprecation=warning +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning +org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=warning +org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore +org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=warning +org.eclipse.jdt.core.compiler.problem.nullReference=error +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning +org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled +org.eclipse.jdt.core.compiler.problem.unusedImport=error +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=warning +org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.source=1.5 diff --git a/dsf/org.eclipse.cdt.dsf.ui/META-INF/MANIFEST.MF b/dsf/org.eclipse.cdt.dsf.ui/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..78d85557a07 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/META-INF/MANIFEST.MF @@ -0,0 +1,47 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %pluginName +Bundle-Vendor: %providerName +Bundle-SymbolicName: org.eclipse.cdt.dsf.ui;singleton:=true +Bundle-Version: 2.0.0.qualifier +Bundle-Activator: org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin +Bundle-Localization: plugin +Require-Bundle: org.eclipse.ui, + org.eclipse.core.runtime, + org.eclipse.core.variables, + org.eclipse.debug.ui, + org.eclipse.ui.ide, + org.eclipse.jface.text, + org.eclipse.ui.workbench.texteditor, + org.eclipse.cdt.dsf, + org.eclipse.cdt.dsf.ui, + org.eclipse.cdt.core, + org.eclipse.cdt.debug.core, + org.eclipse.cdt.debug.ui, + org.eclipse.jface.text;bundle-version="3.4.0", + org.eclipse.ui.editors;bundle-version="3.4.0", + org.eclipse.ui.workbench.texteditor;bundle-version="3.4.0", + org.eclipse.ui.ide;bundle-version="3.4.0", + org.eclipse.cdt.ui;bundle-version="5.0.0", + org.eclipse.core.expressions;bundle-version="3.4.0", + org.eclipse.core.filesystem;bundle-version="1.2.0" +Bundle-ActivationPolicy: lazy +Export-Package: org.eclipse.cdt.dsf.debug.ui, + org.eclipse.cdt.dsf.debug.ui.actions, + org.eclipse.cdt.dsf.debug.ui.contexts, + org.eclipse.cdt.dsf.debug.ui.sourcelookup, + org.eclipse.cdt.dsf.debug.ui.viewmodel, + org.eclipse.cdt.dsf.debug.ui.viewmodel.actions, + org.eclipse.cdt.dsf.debug.ui.viewmodel.expression, + org.eclipse.cdt.dsf.debug.ui.viewmodel.launch, + org.eclipse.cdt.dsf.debug.ui.viewmodel.modules, + org.eclipse.cdt.dsf.debug.ui.viewmodel.numberformat, + org.eclipse.cdt.dsf.debug.ui.viewmodel.register, + org.eclipse.cdt.dsf.debug.ui.viewmodel.update, + org.eclipse.cdt.dsf.debug.ui.viewmodel.variable, + org.eclipse.cdt.dsf.ui.concurrent, + org.eclipse.cdt.dsf.ui.viewmodel, + org.eclipse.cdt.dsf.ui.viewmodel.datamodel, + org.eclipse.cdt.dsf.ui.viewmodel.properties, + org.eclipse.cdt.dsf.ui.viewmodel.update +Bundle-RequiredExecutionEnvironment: J2SE-1.5 diff --git a/dsf/org.eclipse.cdt.dsf.ui/about.html b/dsf/org.eclipse.cdt.dsf.ui/about.html new file mode 100644 index 00000000000..cb740ae8bc8 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/about.html @@ -0,0 +1,24 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"><head> + + +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>About</title></head><body lang="EN-US"> +<h2>About This Content</h2> + +<p>June 5, 2007</p> +<h3>License</h3> + +<p>The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>. +For purposes of the EPL, "Program" will mean the Content.</p> + +<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p> + +</body></html>
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.dsf.ui/build.properties b/dsf/org.eclipse.cdt.dsf.ui/build.properties new file mode 100644 index 00000000000..e061b4e53d0 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/build.properties @@ -0,0 +1,17 @@ +############################################################################### +# 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 +############################################################################### +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + plugin.xml,\ + plugin.properties,\ + about.html diff --git a/dsf/org.eclipse.cdt.dsf.ui/icons/disassembly.gif b/dsf/org.eclipse.cdt.dsf.ui/icons/disassembly.gif Binary files differnew file mode 100644 index 00000000000..34f5290474b --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/icons/disassembly.gif diff --git a/dsf/org.eclipse.cdt.dsf.ui/icons/library_obj.gif b/dsf/org.eclipse.cdt.dsf.ui/icons/library_obj.gif Binary files differnew file mode 100644 index 00000000000..dd994fdbe69 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/icons/library_obj.gif diff --git a/dsf/org.eclipse.cdt.dsf.ui/icons/library_syms_obj.gif b/dsf/org.eclipse.cdt.dsf.ui/icons/library_syms_obj.gif Binary files differnew file mode 100644 index 00000000000..7da17d4194e --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/icons/library_syms_obj.gif diff --git a/dsf/org.eclipse.cdt.dsf.ui/icons/refresh.gif b/dsf/org.eclipse.cdt.dsf.ui/icons/refresh.gif Binary files differnew file mode 100644 index 00000000000..b6b8dc6836d --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/icons/refresh.gif diff --git a/dsf/org.eclipse.cdt.dsf.ui/icons/refreshall.gif b/dsf/org.eclipse.cdt.dsf.ui/icons/refreshall.gif Binary files differnew file mode 100644 index 00000000000..74d1cc35b7c --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/icons/refreshall.gif diff --git a/dsf/org.eclipse.cdt.dsf.ui/icons/refreshalways.gif b/dsf/org.eclipse.cdt.dsf.ui/icons/refreshalways.gif Binary files differnew file mode 100644 index 00000000000..2c508633881 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/icons/refreshalways.gif diff --git a/dsf/org.eclipse.cdt.dsf.ui/icons/refreshmanual.gif b/dsf/org.eclipse.cdt.dsf.ui/icons/refreshmanual.gif Binary files differnew file mode 100644 index 00000000000..b4b031e58d9 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/icons/refreshmanual.gif diff --git a/dsf/org.eclipse.cdt.dsf.ui/icons/refreshonbreak.gif b/dsf/org.eclipse.cdt.dsf.ui/icons/refreshonbreak.gif Binary files differnew file mode 100644 index 00000000000..f16fbb072f8 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/icons/refreshonbreak.gif diff --git a/dsf/org.eclipse.cdt.dsf.ui/plugin.properties b/dsf/org.eclipse.cdt.dsf.ui/plugin.properties new file mode 100644 index 00000000000..7bed11abaf6 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/plugin.properties @@ -0,0 +1,13 @@ +############################################################################### +# Copyright (c) 2006 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 +############################################################################### +pluginName=Debugger Services Framework UI +providerName=Eclipse.org + diff --git a/dsf/org.eclipse.cdt.dsf.ui/plugin.xml b/dsf/org.eclipse.cdt.dsf.ui/plugin.xml new file mode 100644 index 00000000000..8bafbeb2921 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/plugin.xml @@ -0,0 +1,599 @@ +<?xml version="1.0" encoding="UTF-8"?> +<?eclipse version="3.2"?> +<plugin> + <extension point="org.eclipse.debug.core.watchExpressionDelegates"> + <watchExpressionDelegate + debugModel="org.eclipse.cdt.dsf.debug.ui" + delegateClass="org.eclipse.cdt.dsf.debug.ui.viewmodel.expression.WatchExpressionDelegate"/> + </extension> + + <extension point="org.eclipse.core.runtime.preferences"> + <initializer class="org.eclipse.cdt.dsf.debug.ui.PreferenceInitializer"/> + </extension> + + <extension point="org.eclipse.ui.editors.annotationTypes"> + <type + name="org.eclipse.cdt.dsf.debug.currentIP"> + </type> + <type + name="org.eclipse.cdt.dsf.debug.secondaryIP"> + </type> + </extension> + + <extension point="org.eclipse.ui.editors.markerAnnotationSpecification"> + <specification + annotationImageProvider="org.eclipse.cdt.dsf.debug.ui.sourcelookup.InstructionPointerImageProvider" + annotationType="org.eclipse.cdt.dsf.debug.currentIP" + colorPreferenceKey="currentIPColor" + colorPreferenceValue="198,219,174" + highlightPreferenceKey="currentIPHighlight" + highlightPreferenceValue="true" + label="%debugCurrentInstructionPointer" + overviewRulerPreferenceKey="currentIPOverviewRuler" + overviewRulerPreferenceValue="true" + presentationLayer="6" + textPreferenceKey="currentIPIndication" + textPreferenceValue="false" + verticalRulerPreferenceKey="currentIPVerticalRuler" + verticalRulerPreferenceValue="true"> + </specification> + <specification + annotationImageProvider="org.eclipse.cdt.dsf.debug.ui.sourcelookup.InstructionPointerImageProvider" + annotationType="org.eclipse.cdt.dsf.debug.secondaryIP" + colorPreferenceKey="secondaryIPColor" + colorPreferenceValue="219,235,204" + highlightPreferenceKey="secondaryIPHighlight" + highlightPreferenceValue="true" + label="%debugCallStack" + overviewRulerPreferenceKey="secondaryIPOverviewRuler" + overviewRulerPreferenceValue="true" + presentationLayer="6" + textPreferenceKey="secondaryIPIndication" + textPreferenceValue="false" + verticalRulerPreferenceKey="secondaryIPVerticalRuler" + verticalRulerPreferenceValue="true"> + </specification> + </extension> + + <extension point="org.eclipse.ui.viewActions"> + <!-- Variables View menu contributions --> + <viewContribution + id="org.eclipse.cdt.dsf.debug.ui.viewmodel.update.Refresh" + targetID="org.eclipse.debug.ui.VariableView"> + <action + class="org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.actions.RefreshActionDelegate" + icon="icons/refresh.gif" + id="org.eclipse.cdt.dsf.debug.ui.viewmodel.update.actions.Refresh" + label="%action.refresh.label" + toolbarPath="additions"> + </action> + </viewContribution> + + <!-- Registers View menu contributions --> + <viewContribution + id="org.eclipse.cdt.dsf.debug.ui.viewmodel.update.Refresh" + targetID="org.eclipse.debug.ui.RegisterView"> + <action + class="org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.actions.RefreshActionDelegate" + icon="icons/refresh.gif" + id="org.eclipse.cdt.dsf.debug.ui.viewmodel.update.actions.Refresh" + label="%action.refresh.label" + toolbarPath="additions"> + </action> + </viewContribution> + + <!-- Expressions View menu contributions --> + <viewContribution + id="org.eclipse.cdt.dsf.debug.ui.viewmodel.update.Refresh" + targetID="org.eclipse.debug.ui.ExpressionView"> + <action + class="org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.actions.RefreshActionDelegate" + icon="icons/refresh.gif" + id="org.eclipse.cdt.dsf.debug.ui.viewmodel.update.actions.Refresh" + label="%action.refresh.label" + toolbarPath="additions"> + </action> + </viewContribution> + + <!-- Debug View menu contributions --> + <viewContribution + id="org.eclipse.cdt.dsf.debug.ui.viewmodel.update.debugView.Refresh" + targetID="org.eclipse.debug.ui.DebugView"> + <action + class="org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.actions.RefreshActionDelegate" + icon="icons/refresh.gif" + id="org.eclipse.cdt.dsf.debug.ui.viewmodel.update.actions.Refresh" + label="%action.refresh.label" + toolbarPath="additions"> + </action> + </viewContribution> + </extension> + + <extension + point="org.eclipse.ui.menus"> + <!-- Debug view menu commands --> + <menuContribution + locationURI="menu:org.eclipse.debug.ui.DebugView?after=additions"> + <separator name="additions" visible="false"/> + <separator name="updatePolicy" visible="true"/> + </menuContribution> + <menuContribution + locationURI="menu:org.eclipse.debug.ui.DebugView?after=updatePolicy"> + <menu + id="org.eclipse.cdt.dsf.debug.ui.debugView_updatePolicies" + label="%menu.threadsUpdatePolicy"> + <visibleWhen checkEnabled="false"> + <reference definitionId="org.eclipse.cdt.dsf.debug.ui.testIsUpdateModesActionSetActive"/> + </visibleWhen> + <dynamic + id="org.eclipse.cdt.dsf.debug.ui.UpdatePolicies" + class="org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.actions.UpdatePoliciesContribution"> + </dynamic> + </menu> + </menuContribution> + + <!-- Registers view menu commands --> + <menuContribution + locationURI="menu:org.eclipse.debug.ui.RegisterView?after=additions"> + <separator name="additions" visible="false"/> + <separator name="formatting" visible="true"/> + <separator name="updatePolicy" visible="true"/> + </menuContribution> + <menuContribution + locationURI="menu:org.eclipse.debug.ui.RegisterView?after=updatePolicy"> + <menu + id="org.eclipse.cdt.dsf.debug.ui.registersView_updatePolicies" + label="%menu.updatePolicy"> + <visibleWhen checkEnabled="false"> + <reference definitionId="org.eclipse.cdt.dsf.debug.ui.testIsUpdateModesActionSetActive"/> + </visibleWhen> + <dynamic + id="org.eclipse.cdt.dsf.debug.ui.registersUpdatePolicies" + class="org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.actions.UpdatePoliciesContribution"> + </dynamic> + </menu> + <!-- bug 251769 Hide update scopes in 1.1 + <menu + id="org.eclipse.cdt.dsf.debug.ui.registersView_updateScopes" + label="%menu.updateScope"> + <visibleWhen checkEnabled="false"> + <reference definitionId="org.eclipse.cdt.dsf.debug.ui.testIsUpdateModesActionSetActive"/> + </visibleWhen> + <dynamic + id="org.eclipse.cdt.dsf.debug.ui.registersUpdateScopes" + class="org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.actions.UpdateScopesContribution"> + </dynamic> + </menu> + --> + </menuContribution> + <menuContribution + locationURI="menu:org.eclipse.debug.ui.RegisterView?after=formatting"> + <menu + id="org.eclipse.cdt.dsf.debug.ui.registersView_numberFormats" + label="%menu.numberFormat"> + <visibleWhen checkEnabled="false"> + <reference definitionId="org.eclipse.cdt.dsf.debug.ui.testAreNumberFormatsSupported"/> + </visibleWhen> + <dynamic + id="org.eclipse.cdt.dsf.debug.ui.registersNumberFormats" + class="org.eclipse.cdt.dsf.debug.ui.viewmodel.numberformat.NumberFormatsContribution"> + </dynamic> + </menu> + </menuContribution> + + <!-- Variables view menu commands --> + <menuContribution + locationURI="menu:org.eclipse.debug.ui.VariableView?after=additions"> + <separator name="additions" visible="false"/> + <separator name="formatting" visible="true"/> + <separator name="updatePolicy" visible="true"/> + </menuContribution> + <menuContribution + locationURI="menu:org.eclipse.debug.ui.VariableView?after=updatePolicy"> + <menu + id="org.eclipse.cdt.dsf.debug.ui.variablesView_updatePolicies" + label="%menu.updatePolicy"> + <visibleWhen checkEnabled="false"> + <reference definitionId="org.eclipse.cdt.dsf.debug.ui.testIsUpdateModesActionSetActive"/> + </visibleWhen> + <dynamic + id="org.eclipse.cdt.dsf.debug.ui.variablesUpdatePolicies" + class="org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.actions.UpdatePoliciesContribution"> + </dynamic> + </menu> + <!-- bug 251769 Hide update scopes in 1.1 + <menu + id="org.eclipse.cdt.dsf.debug.ui.variablesView_updateScopes" + label="%menu.updateScope"> + <visibleWhen checkEnabled="false"> + <reference definitionId="org.eclipse.cdt.dsf.debug.ui.testIsUpdateModesActionSetActive"/> + </visibleWhen> + <dynamic + id="org.eclipse.cdt.dsf.debug.ui.variablesUpdateScopes" + class="org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.actions.UpdateScopesContribution"> + </dynamic> + </menu> + --> + </menuContribution> + <menuContribution + locationURI="menu:org.eclipse.debug.ui.VariableView?after=formatting"> + <menu + id="org.eclipse.cdt.dsf.debug.ui.variablesView_numberFormats" + label="%menu.numberFormat"> + <visibleWhen checkEnabled="false"> + <reference definitionId="org.eclipse.cdt.dsf.debug.ui.testAreNumberFormatsSupported"/> + </visibleWhen> + <dynamic + id="org.eclipse.cdt.dsf.debug.ui.variablesNumberFormats" + class="org.eclipse.cdt.dsf.debug.ui.viewmodel.numberformat.NumberFormatsContribution"> + </dynamic> + </menu> + </menuContribution> + + <!-- Expressions view menu commands --> + <menuContribution + locationURI="menu:org.eclipse.debug.ui.ExpressionView?after=additions"> + <separator name="additions" visible="false"/> + <separator name="formatting" visible="true"/> + <separator name="updatePolicy" visible="true"/> + </menuContribution> + <menuContribution + locationURI="menu:org.eclipse.debug.ui.ExpressionView?after=updatePolicy"> + <menu + id="org.eclipse.cdt.dsf.debug.ui.expressionsView_updatePolicies" + label="%menu.updatePolicy"> + <visibleWhen checkEnabled="false"> + <reference definitionId="org.eclipse.cdt.dsf.debug.ui.testIsUpdateModesActionSetActive"/> + </visibleWhen> + <dynamic + id="org.eclipse.cdt.dsf.debug.ui.expressionUpdatePolicies" + class="org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.actions.UpdatePoliciesContribution"> + </dynamic> + </menu> + <!-- bug 251769 Hide update scopes in 1.1 + <menu + id="org.eclipse.cdt.dsf.debug.ui.expressionsView_updateScopes" + label="%menu.updateScope"> + <visibleWhen checkEnabled="false"> + <reference definitionId="org.eclipse.cdt.dsf.debug.ui.testIsUpdateModesActionSetActive"/> + </visibleWhen> + <dynamic + id="org.eclipse.cdt.dsf.debug.ui.expressionUpdateScopes" + class="org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.actions.UpdateScopesContribution"> + </dynamic> + </menu> + --> + </menuContribution> + <menuContribution + locationURI="menu:org.eclipse.debug.ui.ExpressionView?after=formatting"> + <menu + id="org.eclipse.cdt.dsf.debug.ui.expressionsView_numberFormats" + label="%menu.numberFormat"> + <visibleWhen checkEnabled="false"> + <reference definitionId="org.eclipse.cdt.dsf.debug.ui.testAreNumberFormatsSupported"/> + </visibleWhen> + <dynamic + id="org.eclipse.cdt.dsf.debug.ui.expressionNumberFormats" + class="org.eclipse.cdt.dsf.debug.ui.viewmodel.numberformat.NumberFormatsContribution"> + </dynamic> + </menu> + </menuContribution> + </extension> + + <extension point="org.eclipse.ui.handlers"> + <handler + class="org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.actions.RefreshHandler" + commandId="org.eclipse.ui.file.refresh"> + <activeWhen> + <reference definitionId="org.eclipse.cdt.dsf.debug.ui.testAreUpdatePoliciesSupported"/> + </activeWhen> + </handler> + </extension> + + <extension point="org.eclipse.core.expressions.definitions"> + <definition id="org.eclipse.cdt.dsf.debug.ui.testIsUpdateModesActionSetActive"> + <and> + <with variable="activeContexts"> + <iterate operator="or"> + <equals value="org.eclipse.cdt.dsf.debug.ui.updateModes"/> + </iterate> + </with> + </and> + </definition> + <definition id="org.eclipse.cdt.dsf.debug.ui.testAreUpdatePoliciesSupported"> + <and> + <with variable="org.eclipse.core.runtime.Platform"> + <test property="org.eclipse.core.runtime.bundleState" + args="org.eclipse.cdt.dsf.debug.ui" + value="ACTIVE"/> + </with> + <or> + <with variable="activePart"> + <test property="org.eclipse.cdt.dsf.debug.ui.areUpdatePoliciesSupported"/> + </with> + <with variable="selection"> + <test property="org.eclipse.cdt.dsf.debug.ui.areUpdatePoliciesSupported"/> + </with> + </or> + </and> + </definition> + <definition id="org.eclipse.cdt.dsf.debug.ui.testAreUpdateScopesSupported"> + <and> + <with variable="org.eclipse.core.runtime.Platform"> + <test property="org.eclipse.core.runtime.bundleState" + args="org.eclipse.cdt.dsf.debug.ui" + value="ACTIVE"/> + </with> + <or> + <with variable="activePart"> + <test property="org.eclipse.cdt.dsf.debug.ui.areUpdateScopesSupported"/> + </with> + <with variable="selection"> + <test property="org.eclipse.cdt.dsf.debug.ui.areUpdateScopesSupported"/> + </with> + </or> + </and> + </definition> + <definition + id="org.eclipse.cdt.dsf.debug.ui.testAreNumberFormatsSupported"> + <and> + <with variable="org.eclipse.core.runtime.Platform"> + <test property="org.eclipse.core.runtime.bundleState" + args="org.eclipse.cdt.dsf.debug.ui" + value="ACTIVE"/> + </with> + <or> + <with variable="activePart"> + <test property="org.eclipse.cdt.dsf.debug.ui.areNumberFormatsSupported"/> + </with> + <with variable="selection"> + <test property="org.eclipse.cdt.dsf.debug.ui.areNumberFormatsSupported"/> + </with> + </or> + </and> + </definition> + </extension> + + <extension + point="org.eclipse.core.expressions.propertyTesters"> + <propertyTester + class="org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.actions.UpdatePoliciesPropertyTester" + id="org.eclipse.cdt.dsf.debug.ui.selectionUpdatePoliciesTester" + namespace="org.eclipse.cdt.dsf.debug.ui" + properties="areUpdatePoliciesSupported,isUpdatePolicyAvailable,isUpdatePolicyActive" + type="org.eclipse.cdt.dsf.ui.viewmodel.IVMContext"> + </propertyTester> + <propertyTester + class="org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.actions.UpdatePoliciesPropertyTester" + id="org.eclipse.cdt.dsf.debug.ui.partUpdatePoliciesTester" + namespace="org.eclipse.cdt.dsf.debug.ui" + properties="areUpdatePoliciesSupported,isUpdatePolicyAvailable,isUpdatePolicyActive" + type="org.eclipse.debug.ui.IDebugView"> + </propertyTester> + + <propertyTester + class="org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.actions.UpdateScopesPropertyTester" + id="org.eclipse.cdt.dsf.debug.ui.selectionUpdateScopesTester" + namespace="org.eclipse.cdt.dsf.debug.ui" + properties="areUpdateScopesSupported,isUpdateScopeAvailable,isUpdateScopeActive" + type="org.eclipse.cdt.dsf.ui.viewmodel.IVMContext"> + </propertyTester> + <propertyTester + class="org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.actions.UpdateScopesPropertyTester" + id="org.eclipse.cdt.dsf.debug.ui.partUpdateScopesTester" + namespace="org.eclipse.cdt.dsf.debug.ui" + properties="areUpdateScopesSupported,isUpdateScopeAvailable,isUpdateScopeActive" + type="org.eclipse.debug.ui.IDebugView"> + </propertyTester> + + <propertyTester + class="org.eclipse.cdt.dsf.debug.ui.viewmodel.numberformat.NumberFormatsPropertyTester" + id="org.eclipse.cdt.dsf.debug.ui.selectionNumberFormatsTester" + namespace="org.eclipse.cdt.dsf.debug.ui" + properties="areNumberFormatsSupported,isNumberFormatAvailable,isNumberFormatActive" + type="org.eclipse.cdt.dsf.ui.viewmodel.IVMContext"> + </propertyTester> + <propertyTester + class="org.eclipse.cdt.dsf.debug.ui.viewmodel.numberformat.NumberFormatsPropertyTester" + id="org.eclipse.cdt.dsf.debug.ui.partNumberFormatsTester" + namespace="org.eclipse.cdt.dsf.debug.ui" + properties="areNumberFormatsSupported,isNumberFormatAvailable,isNumberFormatActive" + type="org.eclipse.debug.ui.IDebugView"> + </propertyTester> + </extension> + + <extension + point="org.eclipse.debug.ui.detailPaneFactories"> + <detailFactories + class="org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.numberformat.detail.NumberFormatDetailPaneFactory" + id="org.eclipse.cdt.dsf.debug.ui.viewmodel.detailPaneFactory"> + <enablement> + <with variable="selection"> + <iterate> + <instanceof value="org.eclipse.cdt.dsf.debug.ui.viewmodel.numberformat.IFormattedValueVMContext"/> + </iterate> + </with> + </enablement> + </detailFactories> + <detailFactories + class="org.eclipse.cdt.dsf.debug.ui.viewmodel.modules.detail.ModuleDetailPaneFactory" + id="org.eclipse.cdt.dsf.debug.ui.viewmodel.moduleDetailPaneFactory"> + <enablement> + <with variable="selection"> + <iterate> + <instanceof value="org.eclipse.cdt.dsf.debug.ui.viewmodel.modules.ModulesVMNode$ModuleVMContext"/> + </iterate> + </with> + </enablement> + </detailFactories> + </extension> + + <extension point="org.eclipse.ui.commands"> + <command + categoryId="org.eclipse.debug.ui.category.run" + description="%command.gotoPC.description" + id="org.eclipse.cdt.dsf.debug.ui.disassembly.commands.gotoPC" + name="%command.gotoPC.name"/> + <command + categoryId="org.eclipse.debug.ui.category.run" + description="%command.gotoAddress.description" + id="org.eclipse.cdt.dsf.debug.ui.disassembly.commands.gotoAddress" + name="%command.gotoAddress.name"/> + <command + categoryId="org.eclipse.debug.ui.category.run" + description="%command.gotoSymbol.description" + id="org.eclipse.cdt.dsf.debug.ui.disassembly.commands.gotoSymbol" + name="%command.gotoSymbol.name"/> + </extension> + + <extension point="org.eclipse.ui.bindings"> + <key sequence="HOME" + contextId="org.eclipse.cdt.dsf.debug.ui.disassembly.context" + commandId="org.eclipse.cdt.dsf.debug.ui.disassembly.commands.gotoPC" + schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"/> + <key sequence="M1+G" + contextId="org.eclipse.cdt.dsf.debug.ui.disassembly.context" + commandId="org.eclipse.cdt.dsf.debug.ui.disassembly.commands.gotoAddress" + schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"/> + <key sequence="M1+M2+G" + contextId="org.eclipse.cdt.dsf.debug.ui.disassembly.context" + commandId="org.eclipse.cdt.dsf.debug.ui.disassembly.commands.gotoSymbol" + schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"/> + </extension> + + <extension + point="org.eclipse.ui.contexts"> + <context + description="%commandContext.description" + id="org.eclipse.cdt.dsf.debug.ui.disassembly.context" + name="%commandContext.name" + parentId="org.eclipse.debug.ui.debugging"> + </context> + </extension> + + <extension + point="org.eclipse.core.runtime.preferences"> + <initializer class="org.eclipse.cdt.dsf.debug.internal.ui.disassembly.preferences.DisassemblyPreferenceConstants$Initializer"/> + </extension> + + <extension + point="org.eclipse.ui.views"> + <view + category="org.eclipse.debug.ui" + class="org.eclipse.cdt.dsf.debug.internal.ui.disassembly.DisassemblyView" + icon="icons/disassembly.gif" + id="org.eclipse.cdt.dsf.debug.ui.disassembly.view" + name="%disassemblyView.name"> + </view> + </extension> + + <extension + point="org.eclipse.ui.preferencePages"> + <page + category="org.eclipse.debug.ui.DebugPreferencePage" + class="org.eclipse.cdt.dsf.debug.internal.ui.preferences.DsfDebugPreferencePage" + id="org.eclipse.cdt.dsf.debug.ui.preferences" + name="%preferencePage.name"> + </page> + <page + class="org.eclipse.cdt.dsf.debug.internal.ui.disassembly.preferences.DisassemblyPreferencePage" + category="org.eclipse.cdt.dsf.debug.ui.preferences" + name="%disassemblyPreferencePage.name" + id="org.eclipse.cdt.dsf.debug.ui.disassembly.preferencePage"/> + </extension> + <extension + point="org.eclipse.ui.popupMenus"> + <viewerContribution + id="org.eclipse.cdt.dsf.debug.ui.disassemblyRulerActions" + targetID="#DisassemblyPartRulerContext"> + <action + class="org.eclipse.cdt.dsf.debug.internal.ui.disassembly.actions.BreakpointPropertiesRulerActionDelegate" + id="org.eclipse.cdt.dsf.debug.ui.disassembly.breakpointProperties" + label="%action.breakpointProperties.label" + menubarPath="debug"> + </action> + </viewerContribution> + </extension> + + + <!-- memory update policy --> + <extension + point="org.eclipse.ui.viewActions"> + <viewContribution + targetID="org.eclipse.debug.ui.MemoryView" + id="org.eclipse.debug.ui.memoryView.toolbar"> + <action + class="org.eclipse.cdt.dsf.debug.ui.memory.RefreshAction" + enablesFor="1" + icon="icons/refresh.gif" + id="org.eclipse.debug.ui.MemoryView.memoryViewRefresh" + label="Refresh" + toolbarPath="additions"> + </action> + </viewContribution> + </extension> + <extension + point="org.eclipse.ui.viewActions"> + <viewContribution + id="org.eclipse.debug.ui.MemoryView.updatepolicy" + targetID="org.eclipse.debug.ui.MemoryView"> + <action + class="org.eclipse.cdt.dsf.debug.ui.memory.SelectUpdatePolicyAction" + id="org.eclipse.debug.ui.MemoryView.updatepolicy" + label="Update Policy" + menubarPath="additions"> + </action> + </viewContribution> + </extension> + + <!-- Debug view context menu contributions --> + <extension point="org.eclipse.ui.popupMenus"> + <objectContribution + adaptable="false" + id="org.eclipse.cdt.dsf.debug.ui.objectContribution.incompleteStack" + objectClass="org.eclipse.cdt.dsf.debug.ui.viewmodel.launch.StackFramesVMNode$IncompleteStackVMContext"> + <action + class="org.eclipse.cdt.dsf.debug.ui.viewmodel.launch.actions.ExpandStackAction" + id="org.eclipse.cdt.dsf.debug.ui.action.expandStack" + label="%action.expandStack.label" + menubarPath="renderGroup"> + </action> + </objectContribution> + </extension> + <extension + point="org.eclipse.ui.actionSets"> + <actionSet + id="org.eclipse.cdt.dsf.debug.ui.updateModes" + label="Debug Update Modes"> + <action + class="org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.actions.RefreshAllRetargetAction" + definitionId="org.eclipse.cdt.dsf.debug.ui.refreshAll" + helpContextId="org.eclipse.cdt.dsf.debug.ui.refreshAll_context" + icon="icons/refreshall.gif" + id="org.eclipse.cdt.dsf.debug.ui.refreshAll" + label="%action.refreshAll.name" + menubarPath="window/additions" + toolbarPath="org.eclipse.ui.dd.dsf.debug.ui.actionSet.update_modes/debugUpdateModes"> + </action> + </actionSet> + </extension> + + <!-- Debug perspective extension --> + <extension + point="org.eclipse.ui.perspectiveExtensions"> + <perspectiveExtension + targetID="org.eclipse.debug.ui.DebugPerspective"> + <view + relative="org.eclipse.ui.views.ContentOutline" + visible="false" + relationship="stack" + id="org.eclipse.cdt.dsf.debug.ui.disassembly.view"> + </view> + <viewShortcut + id="org.eclipse.cdt.dsf.debug.ui.disassembly.view"> + </viewShortcut> + </perspectiveExtension> + </extension> + + +</plugin> diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/AbstractImageRegistry.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/AbstractImageRegistry.java new file mode 100644 index 00000000000..8150df5b347 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/AbstractImageRegistry.java @@ -0,0 +1,143 @@ +/******************************************************************************* + * 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.internal.ui.disassembly; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.HashMap; + +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Plugin; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.resource.ImageRegistry; +import org.eclipse.swt.SWTException; +import org.eclipse.swt.graphics.Image; +import org.osgi.framework.Bundle; + + +/** + * Abstract image registry that allows for defining fallback paths for images. + */ +public abstract class AbstractImageRegistry extends ImageRegistry { + private HashMap<String, String> fPlugins = new HashMap<String, String>(); + private HashMap<String, String[]> fLocations = new HashMap<String, String[]>(); + private URL fBaseUrl; + + protected AbstractImageRegistry(Plugin plugin) { + fBaseUrl = plugin.getBundle().getEntry("/"); //$NON-NLS-1$ + } + + /** + * Defines the key for a local image, that must be found below the icons directory + * in the plugin. + * @param key Key by which the image can be referred by. + * @param dir Directory relative to icons/ + * @param name The name of the file defining the icon. The name will be used as + * key. + */ + protected void localImage(String key, String dir, String name) { + if (dir== null || dir.equals(""))//$NON-NLS-1$ + fLocations.put(key, new String[] {"icons/" + name}); //$NON-NLS-1$ + else + fLocations.put(key, new String[] {"icons/" + dir + "/" + name}); //$NON-NLS-1$ //$NON-NLS-2$ + } + + /** + * Defines the key for a non-local image, that must be found below the icons directory + * of some plugin. + * @param key Key by which the image can be referred by. + * @param plugin The plugin id, where the icon is searched. + * @param dirs A couple of directories below icons/ in the plugin. If loading fails, + * the next dir will be taken as fallback. + * @param name The name of the file defining the icon. The name will be used as + * key. + */ + protected void externalImage(String key, String plugin, String[] dirs, String name) { + if (plugin != null) { + fPlugins.put(key, plugin); + } + String[] locations = new String[dirs.length]; + for (int i = 0; i < dirs.length; i++) { + String dir = dirs[i]; + if (dir== null || dir.equals(""))//$NON-NLS-1$ + locations[i] = "icons/" + name; //$NON-NLS-1$ + else + locations[i] = "icons/" + dir + "/" + name; //$NON-NLS-1$ //$NON-NLS-2$ + } + fLocations.put(key, locations); + } + + // overrider + @Override + final public Image get(String key) { + Image i = super.get(key); + if (i != null) { + return i; + } + + ImageDescriptor d = createFileImageDescriptor(key); + if (d != null) { + put(key, d); + return super.get(key); + } + return null; + } + + // overrider + @Override + final public ImageDescriptor getDescriptor(String key) { + ImageDescriptor d = super.getDescriptor(key); + if (d != null) { + return d; + } + + d = createFileImageDescriptor(key); + if (d != null) { + put(key, d); + return d; + } + return null; + } + + private ImageDescriptor createFileImageDescriptor(String key) { + URL url = fBaseUrl; + String pluginId = fPlugins.get(key); + if (pluginId != null) { + Bundle bundle= Platform.getBundle(pluginId); + if (bundle != null) { + url = bundle.getEntry("/"); //$NON-NLS-1$ + } + } + String[] locations= fLocations.get(key); + if (locations != null) { + for (int i = 0; i < locations.length; i++) { + String loc = locations[i]; + URL full; + try { + full = new URL(url, loc); + ImageDescriptor candidate = ImageDescriptor.createFromURL(full); + if (candidate != null && candidate.getImageData() != null) { + return candidate; + } + } catch (MalformedURLException e) { + DsfUIPlugin.getDefault().getLog().log(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, "Malformed Icon URL", e)); //$NON-NLS-1$ + } catch (SWTException e) { + // try the next one. + } + } + } + return null; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/AddressRulerColumn.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/AddressRulerColumn.java new file mode 100644 index 00000000000..54a4c31bc87 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/AddressRulerColumn.java @@ -0,0 +1,185 @@ +/******************************************************************************* + * 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.internal.ui.disassembly; + +import java.math.BigInteger; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.AddressRangePosition; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.DisassemblyDocument; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.SourceFileInfo; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.SourcePosition; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.source.IAnnotationHover; +import org.eclipse.jface.text.source.IAnnotationModel; +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.jface.text.source.IVerticalRulerInfo; +import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; +import org.eclipse.jface.text.source.IVerticalRulerListener; +import org.eclipse.swt.SWT; + +/** + * A vertical ruler column to display the instruction address. + */ +public class AddressRulerColumn extends DisassemblyRulerColumn implements IVerticalRulerInfo, IVerticalRulerInfoExtension, IAnnotationHover { + + private int fRadix; + private boolean fShowRadixPrefix; + private String fRadixPrefix; + private int fNumberOfDigits; + private int fAddressSize; + + /** + * Default constructor. + */ + public AddressRulerColumn() { + super(SWT.LEFT); + setShowRadixPrefix(true); + setAddressSize(32); + setRadix(16); + } + + @Override + protected String createDisplayString(int line) { + DisassemblyDocument doc = (DisassemblyDocument)getParentRuler().getTextViewer().getDocument(); + int offset; + try { + offset = doc.getLineOffset(line); + AddressRangePosition pos = doc.getDisassemblyPosition(offset); + if (pos != null && pos.length > 0 && pos.offset == offset) { + if (pos.fValid) { + return getAddressText(pos.fAddressOffset); + } else { + return DOTS.substring(0, computeNumberOfCharacters()); + } + } + SourcePosition srcPos = doc.getSourcePosition(offset); + if (srcPos != null && srcPos.fValid && srcPos.length > 0) { + int srcLine; + int nLines; + if (srcPos.fFileInfo.fSource == null) { + srcLine = srcPos.fLine; + nLines = srcLine+1; + } else { + int delta = offset-srcPos.offset; + int baseOffset = srcPos.fFileInfo.fSource.getLineOffset(srcPos.fLine); + srcLine = srcPos.fFileInfo.fSource.getLineOfOffset(baseOffset+delta); + nLines = srcPos.fFileInfo.fSource.getNumberOfLines(); + } + String digitStr = Integer.toString(srcLine+1); + int maxDigits = (int)(Math.log(nLines)/Math.log(10))+1; + return SPACES.substring(0, maxDigits-digitStr.length())+digitStr; + } + } catch (BadLocationException e) { + // silently ignored + } + return ""; //$NON-NLS-1$ + } + + @Override + protected int computeNumberOfCharacters() { + return fNumberOfDigits + (fRadixPrefix != null ? fRadixPrefix.length() : 0) + 1; + } + + public void setAddressSize(int bits) { + fAddressSize= bits; + calculateNumberOfDigits(); + } + + public void setRadix(int radix) { + fRadix= radix; + calculateNumberOfDigits(); + setShowRadixPrefix(fShowRadixPrefix); + } + + private void calculateNumberOfDigits() { + fNumberOfDigits= BigInteger.ONE.shiftLeft(fAddressSize).subtract(BigInteger.ONE).toString(fRadix).length(); + } + + public void setShowRadixPrefix(boolean showRadixPrefix) { + fShowRadixPrefix = showRadixPrefix; + if (!fShowRadixPrefix) { + fRadixPrefix = null; + } else if (fRadix == 16) { + fRadixPrefix = "0x"; //$NON-NLS-1$ + } else if (fRadix == 8) { + fRadixPrefix = "0"; //$NON-NLS-1$ + } else { + fRadixPrefix = null; + } + } + + private String getAddressText(BigInteger address) { + StringBuffer buf = new StringBuffer(fNumberOfDigits + 3); + if (fRadixPrefix != null) { + buf.append(fRadixPrefix); + } + String str = address.toString(fRadix); + for (int i=str.length(); i<fNumberOfDigits; ++i) + buf.append('0'); + buf.append(str); + buf.append(':'); + return buf.toString(); + } + + /* + * @see org.eclipse.jface.text.source.IVerticalRulerInfo#getLineOfLastMouseButtonActivity() + */ + public int getLineOfLastMouseButtonActivity() { + return getParentRuler().getLineOfLastMouseButtonActivity(); + } + + /* + * @see org.eclipse.jface.text.source.IVerticalRulerInfo#toDocumentLineNumber(int) + */ + public int toDocumentLineNumber(int y_coordinate) { + return getParentRuler().toDocumentLineNumber(y_coordinate); + } + + /* + * @see org.eclipse.jface.text.source.IVerticalRulerInfoExtension#getHover() + */ + public IAnnotationHover getHover() { + return this; + } + + /* + * @see org.eclipse.jface.text.source.IVerticalRulerInfoExtension#getModel() + */ + public IAnnotationModel getModel() { + return null; + } + + /* + * @see org.eclipse.jface.text.source.IVerticalRulerInfoExtension#addVerticalRulerListener(org.eclipse.jface.text.source.IVerticalRulerListener) + */ + public void addVerticalRulerListener(IVerticalRulerListener listener) { + } + + /* + * @see org.eclipse.jface.text.source.IVerticalRulerInfoExtension#removeVerticalRulerListener(org.eclipse.jface.text.source.IVerticalRulerListener) + */ + public void removeVerticalRulerListener(IVerticalRulerListener listener) { + } + + /* + * @see org.eclipse.jface.text.source.IAnnotationHover#getHoverInfo(org.eclipse.jface.text.source.ISourceViewer, int) + */ + public String getHoverInfo(ISourceViewer sourceViewer, int line) { + DisassemblyDocument doc = (DisassemblyDocument)getParentRuler().getTextViewer().getDocument(); + BigInteger address = doc.getAddressOfLine(line); + SourceFileInfo info = doc.getSourceInfo(address); + if (info != null) { + return info.fFile.getFullPath().toOSString(); + } + return null; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyDropAdapter.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyDropAdapter.java new file mode 100644 index 00000000000..cf6f4de052c --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyDropAdapter.java @@ -0,0 +1,121 @@ +/******************************************************************************* + * 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.internal.ui.disassembly; + +import org.eclipse.swt.dnd.*; + +/** + * DisassemblyDropAdapter + */ +public class DisassemblyDropAdapter extends DropTargetAdapter { + + private DisassemblyPart fDisassembly; + + /** + * New DisassemblyDropAdapter. + */ + public DisassemblyDropAdapter(DisassemblyPart disassembly) { + super(); + fDisassembly = disassembly; + } + + /* + * @see org.eclipse.swt.dnd.DropTargetListener#drop(org.eclipse.swt.dnd.DropTargetEvent) + */ + @Override + public void drop(final DropTargetEvent event) { + TransferData dataType = event.currentDataType; + if (isFileDataType(dataType)) { + // event.data is an array of strings which represent the absolute file pathes + assert event.data instanceof String[]; + String fileNames[] = (String[])event.data; + dropFiles(fileNames); + } else if (isTextDataType(dataType)) { + // event.data is a string + assert event.data instanceof String; + String text = (String)event.data; + if (text.indexOf('/') != -1 || text.indexOf('.') != -1) { + dropFiles(new String[] { text }); + } else { + dropText(text); + } + } + } + + /* + * @see org.eclipse.swt.dnd.DropTargetListener#dragEnter(org.eclipse.swt.dnd.DropTargetEvent) + */ + @Override + public void dragEnter(DropTargetEvent event) { + event.detail = DND.DROP_COPY; + event.feedback = DND.FEEDBACK_NONE; + } + + /* + * @see org.eclipse.swt.dnd.DropTargetListener#dragOver(org.eclipse.swt.dnd.DropTargetEvent) + */ + @Override + public void dragOver(DropTargetEvent event) { + event.detail = DND.DROP_COPY; + event.feedback = DND.FEEDBACK_NONE; + } + + private static boolean isFileDataType(TransferData dataType) { + return FileTransfer.getInstance().isSupportedType(dataType); + } + private static boolean isTextDataType(TransferData dataType) { + return TextTransfer.getInstance().isSupportedType(dataType); + } + + /* + * @see org.eclipse.swt.dnd.DropTargetListener#dropAccept(org.eclipse.swt.dnd.DropTargetEvent) + */ + @Override + public void dropAccept(DropTargetEvent event) { + } + + /* + * @see org.eclipse.swt.dnd.DropTargetListener#dragOperationChanged(org.eclipse.swt.dnd.DropTargetEvent) + */ + @Override + public void dragOperationChanged(DropTargetEvent event) { + event.detail = DND.DROP_COPY; + event.feedback = DND.FEEDBACK_NONE; + } + + /* + * @see org.eclipse.swt.dnd.DropTargetListener#dragLeave(org.eclipse.swt.dnd.DropTargetEvent) + */ + @Override + public void dragLeave(DropTargetEvent event) { + } + + /** + * Drop files. + * @param fileNames + */ + private void dropFiles(String[] fileNames) { + // open all the files + for (int i = 0; i < fileNames.length; i++) { + // get disassembly for file + fDisassembly.retrieveDisassembly(fileNames[i], 100, true); + } + } + + /** + * Drop text. + * @param text + */ + private void dropText(String text) { + fDisassembly.gotoSymbol(text.trim()); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyEditor.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyEditor.java new file mode 100644 index 00000000000..8db2f6a9b83 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyEditor.java @@ -0,0 +1,111 @@ +/******************************************************************************* + * 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.internal.ui.disassembly; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.ui.IActionBars; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IEditorSite; +import org.eclipse.ui.PartInitException; + +/** + * DisassemblyEditor + */ +public class DisassemblyEditor extends DisassemblyPart implements IEditorPart { + + private IEditorInput fInput; + + /** + * + */ + public DisassemblyEditor() { + super(); + } + + @Override + protected IActionBars getActionBars() { + return getEditorSite().getActionBars(); + } + + /* + * @see org.eclipse.ui.IEditorPart#getEditorInput() + */ + public IEditorInput getEditorInput() { + return fInput; + } + + /* + * @see org.eclipse.ui.IEditorPart#getEditorSite() + */ + public IEditorSite getEditorSite() { + return (IEditorSite)getSite(); + } + + /* + * @see org.eclipse.ui.IEditorPart#init(org.eclipse.ui.IEditorSite, org.eclipse.ui.IEditorInput) + */ + public void init(IEditorSite site, IEditorInput input) throws PartInitException { + setSite(site); + setInput(input); + } + + /* + * @see org.eclipse.ui.ISaveablePart#doSave(org.eclipse.core.runtime.IProgressMonitor) + */ + public void doSave(IProgressMonitor monitor) { + } + + /* + * @see org.eclipse.ui.ISaveablePart#doSaveAs() + */ + public void doSaveAs() { + } + + /* + * @see org.eclipse.ui.ISaveablePart#isDirty() + */ + public boolean isDirty() { + return false; + } + + /* + * @see org.eclipse.ui.ISaveablePart#isSaveAsAllowed() + */ + public boolean isSaveAsAllowed() { + return false; + } + + /* + * @see org.eclipse.ui.ISaveablePart#isSaveOnCloseNeeded() + */ + public boolean isSaveOnCloseNeeded() { + return false; + } + + // + // IReusableEditor interface + // + + /* + * @see org.eclipse.ui.IReusableEditor#setInput(org.eclipse.ui.IEditorInput) + */ + public void setInput(IEditorInput input) { + fInput = input; + // TLETODO [disassembly] initialization based on input + } + + @Override + protected void closePart() { + getEditorSite().getPage().closeEditor(this, false); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyHyperlinkDetector.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyHyperlinkDetector.java new file mode 100644 index 00000000000..8e9a126ac20 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyHyperlinkDetector.java @@ -0,0 +1,101 @@ +/******************************************************************************* + * 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.internal.ui.disassembly; + +import org.eclipse.cdt.internal.ui.text.CWordFinder; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.hyperlink.AbstractHyperlinkDetector; +import org.eclipse.jface.text.hyperlink.IHyperlink; + +/** + * A hyperlink detector detecting words and numbers to support navigation + * to a symbolic address. + */ +@SuppressWarnings("restriction") +class DisassemblyHyperlinkDetector extends AbstractHyperlinkDetector { + + public class DisassemblyHyperlink implements IHyperlink { + + private String fSymbol; + private IRegion fRegion; + + /** + * @param symbol + * @param region + */ + public DisassemblyHyperlink(String symbol, IRegion region) { + fSymbol= symbol; + fRegion= region; + } + + /* + * @see org.eclipse.jface.text.hyperlink.IHyperlink#getHyperlinkRegion() + */ + public IRegion getHyperlinkRegion() { + return fRegion; + } + + /* + * @see org.eclipse.jface.text.hyperlink.IHyperlink#getHyperlinkText() + */ + public String getHyperlinkText() { + return null; + } + + /* + * @see org.eclipse.jface.text.hyperlink.IHyperlink#getTypeLabel() + */ + public String getTypeLabel() { + return null; + } + + /* + * @see org.eclipse.jface.text.hyperlink.IHyperlink#open() + */ + public void open() { + if (fPart != null) { + fPart.gotoSymbol(fSymbol); + } + } + + } + + private DisassemblyPart fPart; + + public DisassemblyHyperlinkDetector(DisassemblyPart part) { + fPart= part; + } + + /* + * @see org.eclipse.jface.text.hyperlink.IHyperlinkDetector#detectHyperlinks(org.eclipse.jface.text.ITextViewer, org.eclipse.jface.text.IRegion, boolean) + */ + public IHyperlink[] detectHyperlinks(ITextViewer textViewer, + IRegion region, boolean canShowMultipleHyperlinks) { + IDocument document= textViewer.getDocument(); + if (document == null) { + return null; + } + IRegion wordRegion = CWordFinder.findWord(document, region.getOffset()); + if (wordRegion != null && wordRegion.getLength() != 0) { + String word; + try { + word= document.get(wordRegion.getOffset(), wordRegion.getLength()); + return new IHyperlink[] { new DisassemblyHyperlink(word, wordRegion) }; + } catch (BadLocationException e) { + } + } + return null; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyImageRegistry.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyImageRegistry.java new file mode 100644 index 00000000000..c6bf4e5668e --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyImageRegistry.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * 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.internal.ui.disassembly; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.core.runtime.Plugin; +import org.eclipse.jface.resource.ImageDescriptor; + +/** + * DisassemblyImageRegistry + */ +public class DisassemblyImageRegistry extends AbstractImageRegistry { + private static List<Object[]> fStore = new ArrayList<Object[]>(); + + private static String add(String plugin, String[] dirs, String name) { + String key = plugin+'/'+dirs[0]+'/'+name; + fStore.add(new Object[] {key, plugin, dirs, name}); + return key; + } + + private static final String ORG_ECLIPSE_DEBUG_UI_PLUGIN_ID = "org.eclipse.debug.ui"; //$NON-NLS-1$ + private static final String ORG_ECLIPSE_UI_PLUGIN_ID = "org.eclipse.ui"; //$NON-NLS-1$ + + public static final String ICON_ToggleBreakpoint = add(ORG_ECLIPSE_DEBUG_UI_PLUGIN_ID, new String[] { "full/obj16"}, "brkp_obj.gif"); //$NON-NLS-1$ //$NON-NLS-2$ + public static final String ICON_Refresh_enabled = add(ORG_ECLIPSE_UI_PLUGIN_ID, new String[] {"full/elcl16"}, "refresh_nav.gif"); //$NON-NLS-1$ //$NON-NLS-2$ + public static final String ICON_Refresh_disabled = add(ORG_ECLIPSE_UI_PLUGIN_ID, new String[] {"full/dlcl16"}, "refresh_nav.gif"); //$NON-NLS-1$ //$NON-NLS-2$ + public static final String ICON_Copy_enabled = add(ORG_ECLIPSE_UI_PLUGIN_ID, new String[] {"full/etool16"}, "copy_edit.gif"); //$NON-NLS-1$ //$NON-NLS-2$ + public static final String ICON_Copy_disabled = add(ORG_ECLIPSE_UI_PLUGIN_ID, new String[] {"full/dtool16"}, "copy_edit.gif"); //$NON-NLS-1$ //$NON-NLS-2$ + + private static DisassemblyImageRegistry INSTANCE= new DisassemblyImageRegistry(DsfUIPlugin.getDefault()); + + DisassemblyImageRegistry(Plugin plugin) { + super(plugin); + initialize(); + } + + void initialize() { + for (Iterator<Object[]> iter = fStore.iterator(); iter.hasNext();) { + Object[] element = iter.next(); + if (element.length == 2) { + String dir= (String) element[0]; + String name= (String) element[1]; + localImage(name, dir, name); + } else { + String key = (String) element[0]; + String plugin= (String) element[1]; + String[] dirs= (String[]) element[2]; + String name= (String) element[3]; + externalImage(key, plugin, dirs, name); + } + } + } + + public static ImageDescriptor getImageDescriptor(String key) { + return INSTANCE.getDescriptor(key); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyMessages.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyMessages.java new file mode 100644 index 00000000000..3091a363909 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyMessages.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * 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.internal.ui.disassembly; + +import org.eclipse.osgi.util.NLS; + +public final class DisassemblyMessages extends NLS { + + private static final String BUNDLE_NAME = "org.eclipse.cdt.dsf.debug.internal.ui.disassembly.DisassemblyMessages";//$NON-NLS-1$ + + private DisassemblyMessages() { + // Do not instantiate + } + + public static String Disassembly_action_ShowAddresses_label; + public static String Disassembly_action_ShowFunctionOffsets_label; + public static String Disassembly_action_ShowDisassembly_label; + public static String Disassembly_action_ShowSource_label; + public static String Disassembly_action_ShowSymbols_label; + public static String Disassembly_action_ShowSimplified_label; + public static String Disassembly_action_SourceSteppingMode_error; + public static String Disassembly_action_SourceSteppingMode_label; + public static String Disassembly_action_AssemblySteppingMode_label; + public static String Disassembly_action_RunToHere_label; + public static String Disassembly_action_SetPCToHere_label; + public static String Disassembly_action_GotoPC_label; + public static String Disassembly_action_GotoPC_tooltip; + public static String Disassembly_action_GotoAddress_label; + public static String Disassembly_action_GotoSymbol_label; + public static String Disassembly_action_Copy_label; + public static String Disassembly_action_SelectAll_label; + public static String Disassembly_action_BreakpointProperties_label; + public static String Disassembly_action_RemoveBreakpoint_label; + public static String Disassembly_action_AddBreakpoint_label; + public static String Disassembly_action_AddHWBreakpoint_label; + public static String Disassembly_action_AddTracepoint_label; + public static String Disassembly_action_DisableBreakpoint_label; + public static String Disassembly_action_EnableBreakpoint_label; + public static String Disassembly_action_WatchExpression_label; + public static String Disassembly_action_ShowInMemory_label; + public static String Disassembly_action_RefreshView_label; + public static String Disassembly_action_OpenPreferences_label; + public static String Disassembly_GotoAddressDialog_title; + public static String Disassembly_GotoAddressDialog_label; + public static String Disassembly_GotoAddressDialog_error_invalid_address; + public static String Disassembly_GotoAddressDialog_error_not_a_number; + public static String Disassembly_GotoSymbolDialog_title; + public static String Disassembly_GotoSymbolDialog_label; + public static String Disassembly_message_notConnected; + public static String Disassembly_log_error_retrieveFrameAddress; + public static String Disassembly_log_error_locateFile; + public static String Disassembly_log_error_accessLineInfo; + public static String Disassembly_log_error_noFileInfo; + public static String Disassembly_log_error_fileTooLarge; + public static String Disassembly_log_error_readFile; + public static String Disassembly_log_error_createVersion; + public static String Disassembly_log_error_retrieveDisassembly; + public static String Disassembly_log_error_showDisassembly; + public static String Disassembly_log_error_invalidSymbol; + public static String DisassemblyPreferencePage_startAddress; + public static String DisassemblyPreferencePage_endAddress; + public static String DisassemblyPreferencePage_addressRadix; + public static String DisassemblyPreferencePage_instructionRadix; + public static String DisassemblyPreferencePage_showAddressRadix; + public static String DisassemblyPreferencePage_showSource; + public static String DisassemblyPreferencePage_showSymbols; + public static String DisassemblyPreferencePage_simplifiedMnemonics; + public static String DisassemblyPreferencePage_error_not_a_number; + public static String DisassemblyPreferencePage_error_negative_number; + public static String DisassemblyPreferencePage_radix_octal; + public static String DisassemblyPreferencePage_radix_decimal; + public static String DisassemblyPreferencePage_radix_hexadecimal; + public static String DisassemblyPreferencePage_showFunctionOffsets; + public static String DisassemblyPreferencePage_showAddress; + public static String DisassemblyPreferencePage_useSourceOnlyMode; + public static String DisassemblyPreferencePage_useSourceOnlyMode_noteTtitle; + public static String DisassemblyPreferencePage_useSourceOnlyMode_noteMessage; + public static String DisassemblyPreferencePage_avoidReadBeforePC; + public static String DisassemblyIPAnnotation_primary; + public static String DisassemblyIPAnnotation_secondary; + public static String SourceReadingJob_name; + public static String SourceColorerJob_name; + public static String EditionFinderJob_name; + public static String EditionFinderJob_task_get_timestamp; + public static String EditionFinderJob_task_search_history; + + static { + NLS.initializeMessages(BUNDLE_NAME, DisassemblyMessages.class); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyMessages.properties b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyMessages.properties new file mode 100644 index 00000000000..479808236de --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyMessages.properties @@ -0,0 +1,89 @@ +########################################################################## +# 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 +########################################################################## + +Disassembly_action_ShowAddresses_label=Show Addresses +Disassembly_action_ShowFunctionOffsets_label=Show Function Offsets +Disassembly_action_ShowDisassembly_label=Show Disassembly of Source +Disassembly_action_ShowSource_label=Show Source +Disassembly_action_ShowSymbols_label=Show Symbols +Disassembly_action_ShowSimplified_label=Show Simplified Instructions +Disassembly_action_SourceSteppingMode_error=Cannot open editor for file +Disassembly_action_SourceSteppingMode_label=Source Stepping Mode +Disassembly_action_AssemblySteppingMode_label=Assembly Stepping Mode +Disassembly_action_RunToHere_label=Run to Here +Disassembly_action_SetPCToHere_label=Set PC to Here +Disassembly_action_GotoPC_label=Go to Program Counter +Disassembly_action_GotoPC_tooltip=Go to Current Program Counter +Disassembly_action_GotoAddress_label=Go to Address... +Disassembly_action_GotoSymbol_label=Go to Symbol... +Disassembly_action_Copy_label=&Copy +Disassembly_action_SelectAll_label=Select &All +Disassembly_action_BreakpointProperties_label=Breakpoint Properties... +Disassembly_action_RemoveBreakpoint_label=Remove Breakpoint +Disassembly_action_AddBreakpoint_label=Add Breakpoint +Disassembly_action_AddHWBreakpoint_label=Add Hardware Breakpoint +Disassembly_action_AddTracepoint_label=Add Tracepoint... +Disassembly_action_DisableBreakpoint_label=Disable Breakpoint +Disassembly_action_EnableBreakpoint_label=Enable Breakpoint +Disassembly_action_WatchExpression_label=Watch Expression +Disassembly_action_ShowInMemory_label=Show In Memory View +Disassembly_action_RefreshView_label=Re&fresh View +Disassembly_action_OpenPreferences_label=&Preferences... + +Disassembly_GotoAddressDialog_title=Go to Address +Disassembly_GotoAddressDialog_label=Address: +Disassembly_GotoAddressDialog_error_invalid_address=Invalid address +Disassembly_GotoAddressDialog_error_not_a_number=Not a valid number format + +Disassembly_GotoSymbolDialog_title=Go to Symbol +Disassembly_GotoSymbolDialog_label=Symbol: + +Disassembly_message_notConnected=No debug context + +Disassembly_log_error_retrieveFrameAddress=Error retrieving frame address +Disassembly_log_error_locateFile=Unable to locate file:\ +Disassembly_log_error_accessLineInfo=Error accessing line information for:\ +Disassembly_log_error_noFileInfo=Debug information not found for:\ +Disassembly_log_error_fileTooLarge=Source file is too large to retrieve line information:\ +Disassembly_log_error_readFile=Cannot read source file:\ +Disassembly_log_error_createVersion=Cannot create version +Disassembly_log_error_retrieveDisassembly=Cannot retrieve disassembly for:\ +Disassembly_log_error_showDisassembly=Error opening Disassembly +Disassembly_log_error_invalidSymbol=Cannot evaluate symbolic address ''{0}'' + +DisassemblyPreferencePage_startAddress=Start address +DisassemblyPreferencePage_endAddress=End address +DisassemblyPreferencePage_addressRadix=Address display format +DisassemblyPreferencePage_instructionRadix=Instruction display format +DisassemblyPreferencePage_showAddressRadix=Force radix prefixes +DisassemblyPreferencePage_showSource=Show source +DisassemblyPreferencePage_showSymbols=Show symbols +DisassemblyPreferencePage_simplifiedMnemonics=Use simplified instruction mnemonics +DisassemblyPreferencePage_error_not_a_number=Not a valid number format +DisassemblyPreferencePage_error_negative_number=Address cannot be negative +DisassemblyPreferencePage_radix_octal=Octal +DisassemblyPreferencePage_radix_decimal=Decimal +DisassemblyPreferencePage_radix_hexadecimal=Hexadecimal +DisassemblyPreferencePage_showFunctionOffsets=Show function offsets +DisassemblyPreferencePage_showAddress=Show instruction address +DisassemblyPreferencePage_useSourceOnlyMode=Use Disassembly Viewer when debugging in source stepping mode +DisassemblyPreferencePage_useSourceOnlyMode_noteTtitle=Note: +DisassemblyPreferencePage_useSourceOnlyMode_noteMessage=When this option is enabled, the Disassembly Viewer will be used to display source locations in a special "source-only" mode, instead of using the normal Source Editor. +DisassemblyPreferencePage_avoidReadBeforePC=Do not disassemble code before current program counter + +DisassemblyIPAnnotation_primary=Debug Current Instruction Pointer +DisassemblyIPAnnotation_secondary=Debug Call Stack + +SourceReadingJob_name=Reading source file +SourceColorerJob_name=Coloring source file +EditionFinderJob_name=Finding best match for source file +EditionFinderJob_task_get_timestamp=Retrieving module timestamp +EditionFinderJob_task_search_history=Searching local history diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyPart.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyPart.java new file mode 100644 index 00000000000..a8076def62c --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyPart.java @@ -0,0 +1,3441 @@ +/******************************************************************************* + * 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.internal.ui.disassembly; + +import java.io.File; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +import org.eclipse.cdt.core.IAddress; +import org.eclipse.cdt.debug.core.CDIDebugModel; +import org.eclipse.cdt.debug.core.model.ICBreakpoint; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +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.disassembly.actions.AbstractDisassemblyAction; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.actions.ActionGotoAddress; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.actions.ActionGotoProgramCounter; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.actions.ActionGotoSymbol; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.actions.ActionOpenPreferences; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.actions.TextOperationAction; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.Addr2Line; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.AddressRangePosition; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.BreakpointsAnnotationModel; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.DisassemblyDocument; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.DisassemblyPosition; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.ErrorPosition; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.LabelPosition; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.SourceFileInfo; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.SourcePosition; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.preferences.DisassemblyPreferenceConstants; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.presentation.DisassemblyIPAnnotation; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.util.HSL; +import org.eclipse.cdt.dsf.debug.service.IDisassembly; +import org.eclipse.cdt.dsf.debug.service.IExpressions; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues; +import org.eclipse.cdt.dsf.debug.service.IInstruction; +import org.eclipse.cdt.dsf.debug.service.IMixedInstruction; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.ISourceLookup; +import org.eclipse.cdt.dsf.debug.service.IStack; +import org.eclipse.cdt.dsf.debug.service.IDisassembly.IDisassemblyDMContext; +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; +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.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExitedDMEvent; +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.ISourceLookup.ISourceLookupDMContext; +import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; +import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMData; +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.service.DsfSession.SessionEndedListener; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; +import org.eclipse.cdt.internal.ui.dnd.TextViewerDragAdapter; +import org.eclipse.cdt.utils.Addr64; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IStorage; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.ListenerList; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.IBreakpointManager; +import org.eclipse.debug.core.model.IBreakpoint; +import org.eclipse.debug.core.model.ISuspendResume; +import org.eclipse.debug.core.sourcelookup.containers.LocalFileStorage; +import org.eclipse.debug.ui.DebugUITools; +import org.eclipse.debug.ui.actions.IRunToLineTarget; +import org.eclipse.debug.ui.actions.IToggleBreakpointsTarget; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.GroupMarker; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.action.IMenuListener; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.action.IToolBarManager; +import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.action.Separator; +import org.eclipse.jface.commands.ActionHandler; +import org.eclipse.jface.dialogs.ErrorDialog; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.preference.PreferenceConverter; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.BadPositionCategoryException; +import org.eclipse.jface.text.IFindReplaceTarget; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITextOperationTarget; +import org.eclipse.jface.text.ITextPresentationListener; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.jface.text.ITextViewerExtension; +import org.eclipse.jface.text.IViewportListener; +import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.Region; +import org.eclipse.jface.text.TextPresentation; +import org.eclipse.jface.text.source.Annotation; +import org.eclipse.jface.text.source.AnnotationModel; +import org.eclipse.jface.text.source.AnnotationRulerColumn; +import org.eclipse.jface.text.source.CompositeRuler; +import org.eclipse.jface.text.source.IAnnotationAccess; +import org.eclipse.jface.text.source.IAnnotationModel; +import org.eclipse.jface.text.source.IAnnotationModelExtension; +import org.eclipse.jface.text.source.IOverviewRuler; +import org.eclipse.jface.text.source.ISharedTextColors; +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.jface.text.source.IVerticalRuler; +import org.eclipse.jface.text.source.IVerticalRulerColumn; +import org.eclipse.jface.text.source.IVerticalRulerExtension; +import org.eclipse.jface.text.source.IVerticalRulerInfo; +import org.eclipse.jface.text.source.OverviewRuler; +import org.eclipse.jface.text.source.SourceViewerConfiguration; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StyleRange; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.dnd.DND; +import org.eclipse.swt.dnd.DragSource; +import org.eclipse.swt.dnd.DropTarget; +import org.eclipse.swt.dnd.FileTransfer; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; +import org.eclipse.swt.events.MouseAdapter; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.ui.IActionBars; +import org.eclipse.ui.IPartListener2; +import org.eclipse.ui.IWorkbenchActionConstants; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchPartReference; +import org.eclipse.ui.IWorkbenchPartSite; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.actions.ActionFactory; +import org.eclipse.ui.contexts.IContextActivation; +import org.eclipse.ui.contexts.IContextService; +import org.eclipse.ui.editors.text.EditorsUI; +import org.eclipse.ui.handlers.IHandlerActivation; +import org.eclipse.ui.handlers.IHandlerService; +import org.eclipse.ui.ide.IGotoMarker; +import org.eclipse.ui.part.WorkbenchPart; +import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants; +import org.eclipse.ui.texteditor.AnnotationPreference; +import org.eclipse.ui.texteditor.ChainedPreferenceStore; +import org.eclipse.ui.texteditor.DefaultMarkerAnnotationAccess; +import org.eclipse.ui.texteditor.ITextEditorActionConstants; +import org.eclipse.ui.texteditor.IUpdate; +import org.eclipse.ui.texteditor.IWorkbenchActionDefinitionIds; +import org.eclipse.ui.texteditor.MarkerAnnotationPreferences; +import org.eclipse.ui.texteditor.SimpleMarkerAnnotation; +import org.eclipse.ui.texteditor.SourceViewerDecorationSupport; + +/** + * DisassemblyPart + */ +@SuppressWarnings("restriction") +public abstract class DisassemblyPart extends WorkbenchPart implements IDisassemblyPart, IViewportListener, ITextPresentationListener, SessionEndedListener { + + private final static boolean DEBUG = "true".equals(Platform.getDebugOption("org.eclipse.cdt.dsf.ui/debug/disassembly")); //$NON-NLS-1$//$NON-NLS-2$ + + /** + * Annotation model attachment key for breakpoint annotations. + */ + private final static String BREAKPOINT_ANNOTATIONS= "breakpoints"; //$NON-NLS-1$ + + private final static BigInteger PC_UNKNOWN = BigInteger.valueOf(-1); + private final static BigInteger PC_RUNNING = BigInteger.valueOf(-2); + + /** Preference key for highlighting current line. */ + private final static String CURRENT_LINE = AbstractDecoratedTextEditorPreferenceConstants.EDITOR_CURRENT_LINE; + /** Preference key for highlight color of current line. */ + private final static String CURRENT_LINE_COLOR = AbstractDecoratedTextEditorPreferenceConstants.EDITOR_CURRENT_LINE_COLOR; + + /** The width of the vertical ruler. */ + protected final static int VERTICAL_RULER_WIDTH = 12; + + /** High water mark for cache */ + private final static int fgHighWaterMark = 500; + /** Low water mark for cache */ + private final static int fgLowWaterMark = 100; + + private static final String COMMAND_ID_GOTO_ADDRESS = "org.eclipse.cdt.dsf.debug.ui.disassembly.commands.gotoAddress"; //$NON-NLS-1$ + private static final String COMMAND_ID_GOTO_PC = "org.eclipse.cdt.dsf.debug.ui.disassembly.commands.gotoPC"; //$NON-NLS-1$ + private static final String COMMAND_ID_GOTO_SYMBOL = "org.eclipse.cdt.dsf.debug.ui.disassembly.commands.gotoSymbol"; //$NON-NLS-1$ +// private static final String COMMAND_ID_TOGGLE_BREAKPOINT = "org.eclipse.debug.ui.commands.ToggleBreakpoint"; //$NON-NLS-1$ +// private static final String COMMAND_ID_RUN_TO_LINE = "org.eclipse.debug.ui.commands.RunToLine"; //$NON-NLS-1$ +// private static final String COMMAND_ID_TOGGLE_STEPPING_MODE = "org.eclipse.cdt.dsf.debug.ui.debug.ui.menu.showDisassemblyAction"; //$NON-NLS-1$ + + private static final String KEY_BINDING_CONTEXT_DISASSEMBLY = "org.eclipse.cdt.dsf.debug.ui.disassembly.context"; //$NON-NLS-1$ + + protected DisassemblyViewer fViewer; + + protected AbstractDisassemblyAction fActionGotoPC; + protected AbstractDisassemblyAction fActionGotoAddress; + private AbstractDisassemblyAction fActionGotoSymbol; + private AbstractDisassemblyAction fActionToggleBreakpoint; + protected AbstractDisassemblyAction fActionToggleSource; + private AbstractDisassemblyAction fActionToggleFunctionColumn; + private AbstractDisassemblyAction fActionToggleSymbols; + private AbstractDisassemblyAction fActionRefreshView; + private Action fActionOpenPreferences; + private AbstractDisassemblyAction fActionToggleAddressColumn; + private AbstractDisassemblyAction fActionToggleBreakpointEnablement; + + protected DisassemblyDocument fDocument; + private IAnnotationAccess fAnnotationAccess; + private AnnotationRulerColumn fAnnotationRulerColumn; + private MarkerAnnotationPreferences fAnnotationPreferences; + private IPreferenceStore fPreferenceStore; + private IOverviewRuler fOverviewRuler; + private final ListenerList fRulerContextMenuListeners= new ListenerList(ListenerList.IDENTITY); + private SourceViewerDecorationSupport fDecorationSupport; + private Font fFont; + private IVerticalRuler fVerticalRuler; + private IFindReplaceTarget fFindReplaceTarget; + private IPropertyChangeListener fPropertyChangeListener= new PropertyChangeListener(); + private Color fInstructionColor; + private Color fErrorColor; + private Color fSourceColor; + private Color fLabelColor; + private Control fRedrawControl; + private RGB fPCAnnotationRGB; + private Composite fComposite; + + private DropTarget fDropTarget; + private DragSource fDragSource; + private TextViewerDragAdapter fDragSourceAdapter; + private DisassemblyDropAdapter fDropTargetAdapter; + + private FunctionOffsetRulerColumn fOpcodeRulerColumn; + private AddressRulerColumn fAddressRulerColumn; + + private BigInteger fStartAddress; + private BigInteger fEndAddress; + private int fAddressSize= 32; + + private volatile boolean fUpdatePending; + private BigInteger fPCAddress; + private BigInteger fGotoAddressPending= PC_UNKNOWN; + private BigInteger fFocusAddress= PC_UNKNOWN; + private int fBufferZone; + private IExecutionDMContext fTargetContext; + private String fDebugSessionId; + private int fTargetFrame; + private DisassemblyIPAnnotation fPCAnnotation; + private DisassemblyIPAnnotation fSecondaryPCAnnotation; + private boolean fPCAnnotationUpdatePending; + private ArrayList<BigInteger> fPendingPCUpdates = new ArrayList<BigInteger>(5); + private Position fScrollPos; + private int fScrollLine; + private Position fFocusPos; + private BigInteger fFrameAddress= PC_UNKNOWN; + protected Map<String, Action> fGlobalActions = new HashMap<String, Action>(); + private List<Action> fSelectionActions = new ArrayList<Action>(); + private List<AbstractDisassemblyAction> fStateDependentActions = new ArrayList<AbstractDisassemblyAction>(); + private boolean fSourceOnlyMode; + private boolean fShowSource; + private boolean fShowOpcodes; + private boolean fShowSymbols; + private Map<String, Object> fFile2Storage = new HashMap<String, Object>(); + private boolean fShowDisassembly; + private LinkedList<AddressRangePosition> fPCHistory = new LinkedList<AddressRangePosition>(); + private int fPCHistorySizeMax = 4; + private boolean fGotoFramePending; + + private String fPCAnnotationColorKey; + + private ArrayList<Runnable> fRunnableQueue = new ArrayList<Runnable>(); + + protected IPartListener2 fPartListener = + new IPartListener2() { + public void partActivated(IWorkbenchPartReference partRef) { + } + public void partBroughtToTop(IWorkbenchPartReference partRef) { + } + public void partClosed(IWorkbenchPartReference partRef) { + } + public void partDeactivated(IWorkbenchPartReference partRef) { + } + public void partOpened(IWorkbenchPartReference partRef) { + } + public void partHidden(IWorkbenchPartReference partRef) { + if (partRef.getPart(false) == DisassemblyPart.this) { + setActive(false); + } + } + public void partVisible(IWorkbenchPartReference partRef) { + if (partRef.getPart(false) == DisassemblyPart.this) { + setActive(true); + } + } + public void partInputChanged(IWorkbenchPartReference partRef) { + } + }; + + private boolean fActive = true; + private boolean fDoPendingPosted; + private boolean fUpdateBeforeFocus; + + private boolean fRefreshAll; + private IMarker fGotoMarkerPending; + private boolean fUpdateTitlePending; + private boolean fRefreshViewPending; + private boolean fUpdateSourcePending; + + private ArrayList<IHandlerActivation> fHandlerActivations; + private IContextActivation fContextActivation; + + private DsfServicesTracker fServicesTracker; + private IFrameDMContext fTargetFrameContext; + protected IFrameDMData fTargetFrameData; + + + private final class ActionRefreshView extends AbstractDisassemblyAction { + public ActionRefreshView() { + super(DisassemblyPart.this); + setText(DisassemblyMessages.Disassembly_action_RefreshView_label); + setImageDescriptor(DisassemblyImageRegistry.getImageDescriptor(DisassemblyImageRegistry.ICON_Refresh_enabled)); + setDisabledImageDescriptor(DisassemblyImageRegistry.getImageDescriptor(DisassemblyImageRegistry.ICON_Refresh_disabled)); + } + @Override + public void run() { + refreshView(10); + } + } + + private final class ActionToggleAddressColumn extends AbstractDisassemblyAction { + ActionToggleAddressColumn () { + super(DisassemblyPart.this); + setText(DisassemblyMessages.Disassembly_action_ShowAddresses_label); + } + @Override + public void run() { + IPreferenceStore store = DsfUIPlugin.getDefault().getPreferenceStore(); + store.setValue(DisassemblyPreferenceConstants.SHOW_ADDRESS_RULER, !isAddressRulerVisible()); + } + @Override + public void update() { + setChecked(isAddressRulerVisible()); + } + } + + private final class ActionToggleFunctionColumn extends AbstractDisassemblyAction { + ActionToggleFunctionColumn() { + super(DisassemblyPart.this); + setText(DisassemblyMessages.Disassembly_action_ShowFunctionOffsets_label); + } + @Override + public void run() { + IPreferenceStore store = DsfUIPlugin.getDefault().getPreferenceStore(); + store.setValue(DisassemblyPreferenceConstants.SHOW_FUNCTION_OFFSETS, !isOpcodeRulerVisible()); + } + @Override + public void update() { + setChecked(isOpcodeRulerVisible()); + } + } + + private final class ActionToggleBreakpoint extends AbstractDisassemblyAction { + private IBreakpoint fBreakpoint; + private int fLine; + public ActionToggleBreakpoint() { + super(DisassemblyPart.this); + setText(DisassemblyMessages.Disassembly_action_AddBreakpoint_label); + setImageDescriptor(DisassemblyImageRegistry.getImageDescriptor(DisassemblyImageRegistry.ICON_ToggleBreakpoint)); + } + @Override + public void run() { + try { + if (fBreakpoint != null) { + fBreakpoint.delete(); + } else { + insertBreakpoint(fLine, false); + } + } catch (CoreException e) { + DsfUIPlugin.getDefault().getLog().log(e.getStatus()); + } + } + @Override + public void update() { + super.update(); + if (isEnabled()) { + fLine = fVerticalRuler.getLineOfLastMouseButtonActivity(); + IBreakpoint[] bps = getBreakpointsAtLine(fLine); + if (bps == null) { + fBreakpoint = null; + setText(DisassemblyMessages.Disassembly_action_AddBreakpoint_label); + } else { + fBreakpoint = bps[0]; + setText(DisassemblyMessages.Disassembly_action_RemoveBreakpoint_label); + } + } + } + } + + private final class ActionToggleBreakpointEnablement extends AbstractDisassemblyAction { + private IBreakpoint fBreakpoint; + public ActionToggleBreakpointEnablement() { + super(DisassemblyPart.this); + setText(DisassemblyMessages.Disassembly_action_EnableBreakpoint_label); + } + @Override + public void run() { + try { + fBreakpoint.setEnabled(!fBreakpoint.isEnabled()); + } catch (CoreException e) { + internalError(e); + } + } + @Override + public void update() { + super.update(); + if (isEnabled()) { + int line = fVerticalRuler.getLineOfLastMouseButtonActivity(); + IBreakpoint[] bps = getBreakpointsAtLine(line); + if (bps == null || bps.length == 0) { + setEnabled(false); + } else { + fBreakpoint = bps[0]; + try { + if (fBreakpoint.isEnabled()) { + setText(DisassemblyMessages.Disassembly_action_DisableBreakpoint_label); + } else { + setText(DisassemblyMessages.Disassembly_action_EnableBreakpoint_label); + } + } catch (CoreException e) { + setEnabled(false); + } + } + } + } + } + + private final class ActionToggleSource extends AbstractDisassemblyAction { + public ActionToggleSource() { + super(DisassemblyPart.this); + setText(DisassemblyMessages.Disassembly_action_ShowSource_label); + } + @Override + public void run() { + IPreferenceStore store = DsfUIPlugin.getDefault().getPreferenceStore(); + boolean showSourceEnabled = store.getBoolean(DisassemblyPreferenceConstants.SHOW_SOURCE); + if (showSourceEnabled == fShowSource) { + store.setValue(DisassemblyPreferenceConstants.SHOW_SOURCE, !fShowSource); + } else { + sourceModeChanged(!fShowSource); + } + } + @Override + public void update() { + super.update(); + if (isEnabled()) { + setEnabled(fShowDisassembly); + } + setChecked(fShowSource); + } + } + + private final class ActionToggleSymbols extends AbstractDisassemblyAction { + public ActionToggleSymbols() { + super(DisassemblyPart.this); + setText(DisassemblyMessages.Disassembly_action_ShowSymbols_label); + } + @Override + public void run() { + IPreferenceStore store = DsfUIPlugin.getDefault().getPreferenceStore(); + store.setValue(DisassemblyPreferenceConstants.SHOW_SYMBOLS, !fShowSymbols); + } + @Override + public void update() { + super.update(); + setChecked(fShowSymbols); + } + } + + /** + * Internal property change listener for handling changes in the + * preferences. + */ + class PropertyChangeListener implements IPropertyChangeListener { + /* + * @see IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent) + */ + public void propertyChange(PropertyChangeEvent event) { + handlePreferenceStoreChanged(event); + } + } + + + /** + * The constructor. + */ + public DisassemblyPart() { + fAnnotationPreferences = new MarkerAnnotationPreferences(); + setPreferenceStore(new ChainedPreferenceStore(new IPreferenceStore[] { + DsfUIPlugin.getDefault().getPreferenceStore(), EditorsUI.getPreferenceStore() })); + fPCAddress = fFrameAddress = PC_UNKNOWN; + fTargetFrame = -1; + fBufferZone = 32; + fPCAnnotation = new DisassemblyIPAnnotation(true, 0); + fSecondaryPCAnnotation = new DisassemblyIPAnnotation(false, 0); + IPreferenceStore prefs = getPreferenceStore(); + fStartAddress = new BigInteger(prefs.getString(DisassemblyPreferenceConstants.START_ADDRESS)); + String endAddressString = prefs.getString(DisassemblyPreferenceConstants.END_ADDRESS); + if(endAddressString.startsWith("0x")) //$NON-NLS-1$ + fEndAddress = new BigInteger(endAddressString.substring(2), 16); + else + fEndAddress = new BigInteger(endAddressString, 16); + // TLETODO [disassembly[ source only mode + fSourceOnlyMode = false; //prefs.getBoolean(DisassemblyPreferenceConstants.USE_SOURCE_ONLY_MODE); + fShowSource = fSourceOnlyMode || prefs.getBoolean(DisassemblyPreferenceConstants.SHOW_SOURCE); + fShowDisassembly = !fSourceOnlyMode || !fShowSource; + fShowOpcodes = prefs.getBoolean(DisassemblyPreferenceConstants.SHOW_FUNCTION_OFFSETS); + fShowSymbols = prefs.getBoolean(DisassemblyPreferenceConstants.SHOW_SYMBOLS); + fUpdateBeforeFocus = !prefs.getBoolean(DisassemblyPreferenceConstants.AVOID_READ_BEFORE_PC); + fPCHistorySizeMax = prefs.getInt(DisassemblyPreferenceConstants.PC_HISTORY_SIZE); + } + + public void logWarning(String message, Throwable error) { + DsfUIPlugin.getDefault().getLog().log(new Status(IStatus.WARNING, DsfUIPlugin.PLUGIN_ID, message, error)); + } + + /* + * @see IAdaptable#getAdapter(java.lang.Class) + */ + @Override + @SuppressWarnings("unchecked") + public Object getAdapter(Class required) { + if (IVerticalRulerInfo.class.equals(required)) { + if (fVerticalRuler != null) { + return fVerticalRuler; + } + } else if (IDisassemblyPart.class.equals(required)) { + return this; + } else if (IFindReplaceTarget.class.equals(required)) { + if (fFindReplaceTarget == null) { + fFindReplaceTarget = (fViewer == null ? null : fViewer.getFindReplaceTarget()); + } + return fFindReplaceTarget; + } else if (ITextOperationTarget.class.equals(required)) { + return (fViewer == null ? null : fViewer.getTextOperationTarget()); + } else if (Control.class.equals(required)) { + return fViewer != null ? fViewer.getTextWidget() : null; + } else if (IGotoMarker.class.equals(required)) { + return new IGotoMarker() { + public void gotoMarker(IMarker marker) { + DisassemblyPart.this.gotoMarker(marker); + }}; + } else if (IToggleBreakpointsTarget.class.equals(required)) { + return new IToggleBreakpointsTarget() { + public void toggleLineBreakpoints(IWorkbenchPart part, ISelection selection) throws CoreException { + ITextSelection textSelection = (ITextSelection)selection; + int line = textSelection.getStartLine(); + IBreakpoint[] bp = getBreakpointsAtLine(line); + if (bp == null || bp.length == 0) { + insertBreakpoint(line, false); + } else { + for (int i = 0; i < bp.length; i++) { + bp[i].delete(); + } + } + } + public boolean canToggleLineBreakpoints(IWorkbenchPart part, ISelection selection) { + return fDebugSessionId != null; + } + public void toggleMethodBreakpoints(IWorkbenchPart part, ISelection selection) throws CoreException { + } + public boolean canToggleMethodBreakpoints(IWorkbenchPart part, ISelection selection) { + return false; + } + public void toggleWatchpoints(IWorkbenchPart part, ISelection selection) throws CoreException { + } + public boolean canToggleWatchpoints(IWorkbenchPart part, ISelection selection) { + return false; + }}; + } else if (IRunToLineTarget.class.equals(required)) { + return new IRunToLineTarget() { + public void runToLine(IWorkbenchPart part, ISelection selection, ISuspendResume target) throws CoreException { +// ITextSelection textSelection = (ITextSelection)selection; +// int line = textSelection.getStartLine(); +// BigInteger address = getAddressOfLine(line); + // TLETODO [disassembly] run to line +// getRunControl().runUntil(...); + } + public boolean canRunToLine(IWorkbenchPart part, ISelection selection, ISuspendResume target) { + return fTargetContext != null && isSuspended(fTargetContext) ; + }}; + } + return super.getAdapter(required); + } + + private void setPreferenceStore(IPreferenceStore store) { + if (fPreferenceStore != null) { + fPreferenceStore.removePropertyChangeListener(fPropertyChangeListener); + } + + fPreferenceStore = store; + + if (fPreferenceStore != null) { + fPreferenceStore.addPropertyChangeListener(fPropertyChangeListener); + } + } + + /** + * Handles a property change event describing a change of the editor's + * preference store and updates the preference related editor properties. + * <p> + * Subclasses may extend. + * </p> + * + * @param event + * the property change event + */ + protected void handlePreferenceStoreChanged(PropertyChangeEvent event) { + + if (fViewer == null) + return; + + String property = event.getProperty(); + IPreferenceStore store = getPreferenceStore(); + + if (getFontPropertyPreferenceKey().equals(property)) { + initializeViewerFont(fViewer); + } else if (property.equals(DisassemblyPreferenceConstants.SHOW_ADDRESS_RULER)) { + fActionToggleAddressColumn.update(); + if (isAddressRulerVisible()) { + showAddressRuler(); + } else { + hideAddressRuler(); + } + } else if (property.equals(DisassemblyPreferenceConstants.ADDRESS_RADIX)) { + if (fAddressRulerColumn != null) { + hideAddressRuler(); + showAddressRuler(); + } + } else if (property.equals(DisassemblyPreferenceConstants.SHOW_ADDRESS_RADIX)) { + if (fAddressRulerColumn != null) { + hideAddressRuler(); + showAddressRuler(); + } + } else if (property.equals(DisassemblyPreferenceConstants.SHOW_SOURCE)) { + sourceModeChanged(store.getBoolean(property)); + } else if (property.equals(DisassemblyPreferenceConstants.INSTRUCTION_RADIX)) { + Runnable doit = new Runnable() { + public void run() { + fDocument.invalidateAddressRange(fStartAddress, fEndAddress, true); + if (!fShowDisassembly) { + fDocument.invalidateDisassemblyWithSource(true); + } + fDocument.setMaxOpcodeLength(0); + fGotoFramePending = true; + }}; + doScrollLocked(doit); + } else if (property.equals(DisassemblyPreferenceConstants.SHOW_SYMBOLS)) { + boolean showSymbols = store.getBoolean(property); + if (fShowSymbols == showSymbols) { + return; + } + fShowSymbols = showSymbols; + Runnable doit = new Runnable() { + public void run() { + fDocument.invalidateAddressRange(fStartAddress, fEndAddress, true); + if (!fShowDisassembly) { + fDocument.invalidateDisassemblyWithSource(true); + } + fGotoFramePending = true; + }}; + doScrollLocked(doit); + } else if (property.equals(DisassemblyPreferenceConstants.USE_SOURCE_ONLY_MODE)) { + fSourceOnlyMode = store.getBoolean(property); + if (fDebugSessionId != null) { + disassemblyModeChanged(isDissemblyMixedModeOn()); + } + } else if (property.equals(DisassemblyPreferenceConstants.SHOW_FUNCTION_OFFSETS)) { + fShowOpcodes = store.getBoolean(property); + fActionToggleFunctionColumn.update(); + if (isOpcodeRulerVisible()) { + showOpcodeRuler(); + } else { + hideOpcodeRuler(); + } + } else if (property.equals(DisassemblyPreferenceConstants.AVOID_READ_BEFORE_PC)) { + fUpdateBeforeFocus = !store.getBoolean(property); + updateVisibleArea(); + } else if (property.equals(fPCAnnotationColorKey)) { + fPCAnnotationRGB = PreferenceConverter.getColor(store, fPCAnnotationColorKey); + // redraw + for (Iterator<AddressRangePosition> it=fPCHistory.iterator(); it.hasNext();) { + AddressRangePosition pos = it.next(); + fViewer.invalidateTextPresentation(pos.offset, pos.length); + } + } else if (property.equals(DisassemblyPreferenceConstants.PC_HISTORY_SIZE)) { + fPCHistorySizeMax = store.getInt(property); + } + } + + /** + * This is a callback that will allow us to create the viewer and initialize + * it. + */ + @Override + public void createPartControl(Composite parent) { + fComposite = parent; + FillLayout layout = new FillLayout(); + layout.marginHeight = 2; + parent.setLayout(layout); + fVerticalRuler = createVerticalRuler(); + int styles = SWT.V_SCROLL | SWT.H_SCROLL | SWT.MULTI | SWT.BORDER | SWT.FULL_SELECTION; + fViewer = new DisassemblyViewer(parent, fVerticalRuler, getOverviewRuler(), true, styles); + SourceViewerConfiguration sourceViewerConfig = new DisassemblyViewerConfiguration(this); + fViewer.addTextPresentationListener(this); + fViewer.configure(sourceViewerConfig); + fDecorationSupport = new SourceViewerDecorationSupport(fViewer, getOverviewRuler(), getAnnotationAccess(), + getSharedColors()); + configureSourceViewerDecorationSupport(fDecorationSupport); + fDecorationSupport.install(getPreferenceStore()); + if (fPCAnnotationColorKey != null) { + fPCAnnotationRGB = PreferenceConverter.getColor(getPreferenceStore(), fPCAnnotationColorKey); + } else { + fPCAnnotationRGB = parent.getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION).getRGB(); + } + + initializeViewerFont(fViewer); + createActions(); + hookRulerContextMenu(); + hookContextMenu(); + contributeToActionBars(); + + fViewer.addSelectionChangedListener(new ISelectionChangedListener() { + public void selectionChanged(SelectionChangedEvent event) { + updateSelectionDependentActions(); + } + }); + + fDocument = createDocument(); + fViewer.setDocument(fDocument, new AnnotationModel()); + JFaceResources.getFontRegistry().addListener(fPropertyChangeListener); + + fErrorColor = getSharedColors().getColor(new RGB(96, 0, 0)); + fInstructionColor = getSharedColors().getColor(new RGB(0, 0, 96)); + fSourceColor = getSharedColors().getColor(new RGB(64, 0, 80)); + fLabelColor = getSharedColors().getColor(new RGB(0, 0, 96)); + + if (isAddressRulerVisible()) { + showAddressRuler(); + } + if (isOpcodeRulerVisible()) { + showOpcodeRuler(); + } + initDragAndDrop(); + PlatformUI.getWorkbench().getHelpSystem().setHelp(fViewer.getControl(), IDisassemblyHelpContextIds.DISASSEMBLY_VIEW); + updateTitle(); + updateStateDependentActions(); + + if (fDebugSessionId != null) { + debugContextChanged(); + } else { + updateDebugContext(); + } + DsfSession.addSessionEndedListener(this); + } + + /* + * @see org.eclipse.ui.part.WorkbenchPart#setSite(org.eclipse.ui.IWorkbenchPartSite) + */ + @Override + protected void setSite(IWorkbenchPartSite site) { + super.setSite(site); + site.getPage().addPartListener(fPartListener); + } + + private DisassemblyDocument createDocument() { + DisassemblyDocument doc = new DisassemblyDocument(); + return doc; + } + + /* + * @see org.eclipse.ui.IWorkbenchPart#dispose() + */ + @Override + public void dispose() { + IWorkbenchPartSite site = getSite(); + site.setSelectionProvider(null); + site.getPage().removePartListener(fPartListener); + if (fHandlerActivations != null) { + IHandlerService handlerService = (IHandlerService)site.getService(IHandlerService.class); + handlerService.deactivateHandlers(fHandlerActivations); + fHandlerActivations = null; + } + if (fContextActivation != null) { + IContextService ctxService = (IContextService)site.getService(IContextService.class); + ctxService.deactivateContext(fContextActivation); + } + fViewer = null; + setDebugContext(null); + DsfSession.removeSessionEndedListener(this); + + fAnnotationAccess = null; + fAnnotationPreferences = null; + fAnnotationRulerColumn = null; + fComposite = null; + if (fDecorationSupport != null) { + fDecorationSupport.uninstall(); + fDecorationSupport = null; + } + if (fFont != null) { + fFont.dispose(); + fFont = null; + } + if (fDropTarget != null) { + fDropTarget.dispose(); + fDropTarget = null; + fDragSource.dispose(); + fDragSource = null; + } + if (fPropertyChangeListener != null) { + if (fPreferenceStore != null) { + fPreferenceStore.removePropertyChangeListener(fPropertyChangeListener); + fPreferenceStore = null; + } + fPropertyChangeListener = null; + } + + fDocument.dispose(); + fDocument = null; + super.dispose(); + } + + private void initDragAndDrop() { + if (fDropTarget == null) { + Transfer[] dropTypes = new Transfer[] { FileTransfer.getInstance(), TextTransfer.getInstance() }; + Transfer[] dragTypes = new Transfer[] { TextTransfer.getInstance() }; + Control dropControl = getSourceViewer().getTextWidget(); + Control dragControl = dropControl; + int dropOps = DND.DROP_COPY | DND.DROP_DEFAULT; + int dragOps = DND.DROP_COPY | DND.DROP_DEFAULT; + + fDropTarget = new DropTarget(dropControl, dropOps); + fDropTarget.setTransfer(dropTypes); + fDropTargetAdapter = new DisassemblyDropAdapter(this); + fDropTarget.addDropListener(fDropTargetAdapter); + + fDragSource = new DragSource(dragControl, dragOps); + fDragSource.setTransfer(dragTypes); + fDragSourceAdapter = new TextViewerDragAdapter(getSourceViewer()); + fDragSource.addDragListener(fDragSourceAdapter); + } + } + + private ISourceViewer getSourceViewer() { + return fViewer; + } + + protected void configureSourceViewerDecorationSupport(SourceViewerDecorationSupport support) { + Iterator<?> e = fAnnotationPreferences.getAnnotationPreferences().iterator(); + while (e.hasNext()) { + AnnotationPreference pref = (AnnotationPreference)e.next(); + support.setAnnotationPreference(pref); + if (pref.getAnnotationType().equals(fPCAnnotation.getType())) { + fPCAnnotationColorKey = pref.getColorPreferenceKey(); + } + } + support.setCursorLinePainterPreferenceKeys(CURRENT_LINE, CURRENT_LINE_COLOR); + support.setSymbolicFontName(getFontPropertyPreferenceKey()); + } + + /** + * Returns the symbolic font name for this view as defined in XML. + * + * @return a String with the symbolic font name or <code>null</code> if + * none is defined + */ + private String getSymbolicFontName() { + if (getConfigurationElement() != null) + return getConfigurationElement().getAttribute("symbolicFontName"); //$NON-NLS-1$ + else + return null; + } + + protected final String getFontPropertyPreferenceKey() { + String symbolicFontName = getSymbolicFontName(); + + if (symbolicFontName != null) + return symbolicFontName; + else + return JFaceResources.TEXT_FONT; + } + + /** + * Initializes the given viewer's font. + * + * @param viewer + * the viewer + */ + private void initializeViewerFont(ISourceViewer viewer) { + + boolean isSharedFont = true; + Font font = null; + String symbolicFontName = getSymbolicFontName(); + + if (symbolicFontName != null) + font = JFaceResources.getFont(symbolicFontName); + else if (fPreferenceStore != null) { + // Backward compatibility + if (fPreferenceStore.contains(JFaceResources.TEXT_FONT) + && !fPreferenceStore.isDefault(JFaceResources.TEXT_FONT)) { + FontData data = PreferenceConverter.getFontData(fPreferenceStore, JFaceResources.TEXT_FONT); + + if (data != null) { + isSharedFont = false; + font = new Font(viewer.getTextWidget().getDisplay(), data); + } + } + } + if (font == null) + font = JFaceResources.getTextFont(); + + setFont(viewer, font); + + if (fFont != null) { + fFont.dispose(); + fFont = null; + } + + if (!isSharedFont) + fFont = font; + } + + /** + * Sets the font for the given viewer sustaining selection and scroll + * position. + * + * @param sourceViewer + * the source viewer + * @param font + * the font + */ + private void setFont(ISourceViewer sourceViewer, Font font) { + if (sourceViewer.getDocument() != null) { + + Point selection = sourceViewer.getSelectedRange(); + int topIndex = sourceViewer.getTopIndex(); + + StyledText styledText = sourceViewer.getTextWidget(); + Control parent = styledText; + if (sourceViewer instanceof ITextViewerExtension) { + ITextViewerExtension extension = (ITextViewerExtension) sourceViewer; + parent = extension.getControl(); + } + + parent.setRedraw(false); + + styledText.setFont(font); + + if (fVerticalRuler instanceof IVerticalRulerExtension) { + IVerticalRulerExtension e = (IVerticalRulerExtension) fVerticalRuler; + e.setFont(font); + } + + sourceViewer.setSelectedRange(selection.x, selection.y); + sourceViewer.setTopIndex(topIndex); + + if (parent instanceof Composite) { + Composite composite = (Composite) parent; + composite.layout(true); + } + + parent.setRedraw(true); + + } else { + + StyledText styledText = sourceViewer.getTextWidget(); + styledText.setFont(font); + + if (fVerticalRuler instanceof IVerticalRulerExtension) { + IVerticalRulerExtension e = (IVerticalRulerExtension) fVerticalRuler; + e.setFont(font); + } + } + } + + protected IVerticalRuler createVerticalRuler() { + CompositeRuler ruler = createCompositeRuler(); + IPreferenceStore store = getPreferenceStore(); + if (ruler != null && store != null) { + for (Iterator<?> iter = ruler.getDecoratorIterator(); iter.hasNext();) { + IVerticalRulerColumn column = (IVerticalRulerColumn) iter.next(); + if (column instanceof AnnotationRulerColumn) { + fAnnotationRulerColumn = (AnnotationRulerColumn) column; + for (Iterator<?> iter2 = fAnnotationPreferences.getAnnotationPreferences().iterator(); iter2.hasNext();) { + AnnotationPreference preference = (AnnotationPreference) iter2.next(); + String key = preference.getVerticalRulerPreferenceKey(); + boolean showAnnotation = true; + if (key != null && store.contains(key)) + showAnnotation = store.getBoolean(key); + if (showAnnotation) + fAnnotationRulerColumn.addAnnotationType(preference.getAnnotationType()); + } + fAnnotationRulerColumn.addAnnotationType(Annotation.TYPE_UNKNOWN); + break; + } + } + } + return ruler; + } + + /** + * Returns the vertical ruler. + * + * @return the vertical ruler + */ + protected IVerticalRuler getVerticalRuler() { + return fVerticalRuler; + } + + /** + * Returns the overview ruler. + * + * @return the overview ruler + */ + protected IOverviewRuler getOverviewRuler() { + if (fOverviewRuler == null) + fOverviewRuler = createOverviewRuler(getSharedColors()); + return fOverviewRuler; + } + + protected ISharedTextColors getSharedColors() { + return EditorsUI.getSharedTextColors(); + } + + protected IOverviewRuler createOverviewRuler(ISharedTextColors sharedColors) { + IOverviewRuler ruler = new OverviewRuler(getAnnotationAccess(), VERTICAL_RULER_WIDTH, sharedColors); + Iterator<?> e = fAnnotationPreferences.getAnnotationPreferences().iterator(); + while (e.hasNext()) { + AnnotationPreference preference = (AnnotationPreference) e.next(); + if (preference.contributesToHeader()) + ruler.addHeaderAnnotationType(preference.getAnnotationType()); + } + return ruler; + } + + /** + * Creates a new address ruler column that is appropriately initialized. + * + * @return the created line number column + */ + protected IVerticalRulerColumn createAddressRulerColumn() { + fAddressRulerColumn= new AddressRulerColumn(); + initializeRulerColumn(fAddressRulerColumn, DisassemblyPreferenceConstants.ADDRESS_COLOR); + IPreferenceStore prefs = getPreferenceStore(); + fAddressRulerColumn.setRadix(prefs.getInt(DisassemblyPreferenceConstants.ADDRESS_RADIX)); + fAddressRulerColumn.setShowRadixPrefix(prefs.getBoolean(DisassemblyPreferenceConstants.SHOW_ADDRESS_RADIX)); + return fAddressRulerColumn; + } + + /** + * Creates a new ruler column that is appropriately initialized. + * + * @return the created line number column + */ + protected IVerticalRulerColumn createOpcodeRulerColumn() { + fOpcodeRulerColumn= new FunctionOffsetRulerColumn(); + initializeRulerColumn(fOpcodeRulerColumn, DisassemblyPreferenceConstants.OPCODE_COLOR); + return fOpcodeRulerColumn; + } + + /** + * Initializes the given address ruler column from the preference store. + * + * @param rulerColumn the ruler column to be initialized + */ + protected void initializeRulerColumn(DisassemblyRulerColumn rulerColumn, String colorPrefKey) { + ISharedTextColors sharedColors= getSharedColors(); + IPreferenceStore store= getPreferenceStore(); + if (store != null) { + + RGB rgb= null; + // foreground color + if (store.contains(colorPrefKey)) { + if (store.isDefault(colorPrefKey)) + rgb= PreferenceConverter.getDefaultColor(store, colorPrefKey); + else + rgb= PreferenceConverter.getColor(store, colorPrefKey); + } + if (rgb == null) + rgb= new RGB(0, 0, 0); + rulerColumn.setForeground(sharedColors.getColor(rgb)); + + rgb= null; + + rulerColumn.redraw(); + } + } + + + /** + * @return the preference store + */ + private IPreferenceStore getPreferenceStore() { + return fPreferenceStore; + } + + /** + * Creates a composite ruler to be used as the vertical ruler by this + * editor. Subclasses may re-implement this method. + * + * @return the vertical ruler + */ + protected CompositeRuler createCompositeRuler() { + CompositeRuler ruler = new CompositeRuler(); + ruler.addDecorator(0, new AnnotationRulerColumn(VERTICAL_RULER_WIDTH, getAnnotationAccess())); + return ruler; + } + + private boolean isAddressRulerVisible() { + return getPreferenceStore().getBoolean(DisassemblyPreferenceConstants.SHOW_ADDRESS_RULER); + } + + /** + * Shows the address ruler column. + */ + private void showAddressRuler() { + if (fAddressRulerColumn == null) { + IVerticalRuler v= getVerticalRuler(); + if (v instanceof CompositeRuler) { + CompositeRuler c= (CompositeRuler) v; + c.addDecorator(1, createAddressRulerColumn()); + } + } + } + + /** + * Hides the address ruler column. + */ + private void hideAddressRuler() { + if (fAddressRulerColumn != null) { + IVerticalRuler v= getVerticalRuler(); + if (v instanceof CompositeRuler) { + CompositeRuler c= (CompositeRuler) v; + c.removeDecorator(fAddressRulerColumn); + } + fAddressRulerColumn = null; + } + } + + private boolean isOpcodeRulerVisible() { + return fShowOpcodes; + } + + /** + * Shows the opcode ruler column. + */ + private void showOpcodeRuler() { + if (fOpcodeRulerColumn == null) { + IVerticalRuler v= getVerticalRuler(); + if (v instanceof CompositeRuler) { + CompositeRuler c= (CompositeRuler) v; + c.addDecorator(2, createOpcodeRulerColumn()); + } + } + } + + /** + * Hides the opcode ruler column. + */ + private void hideOpcodeRuler() { + if (fOpcodeRulerColumn != null) { + IVerticalRuler v= getVerticalRuler(); + if (v instanceof CompositeRuler) { + CompositeRuler c= (CompositeRuler) v; + c.removeDecorator(fOpcodeRulerColumn); + } + fOpcodeRulerColumn = null; + } + } + + /** + * Returns the annotation access. + * + * @return the annotation access + */ + protected IAnnotationAccess getAnnotationAccess() { + if (fAnnotationAccess == null) + fAnnotationAccess = createAnnotationAccess(); + return fAnnotationAccess; + } + + /** + * Creates the annotation access for this editor. + * + * @return the created annotation access + */ + protected IAnnotationAccess createAnnotationAccess() { + return new DefaultMarkerAnnotationAccess(); + } + + private void hookContextMenu() { + String id = "#DisassemblyPartContext"; //$NON-NLS-1$ + MenuManager menuMgr = new MenuManager(id, id); + menuMgr.setRemoveAllWhenShown(true); + menuMgr.addMenuListener(new IMenuListener() { + public void menuAboutToShow(IMenuManager manager) { + DisassemblyPart.this.fillContextMenu(manager); + } + }); + Menu menu = menuMgr.createContextMenu(fViewer.getTextWidget()); + fViewer.getTextWidget().setMenu(menu); + getSite().registerContextMenu(id, menuMgr, fViewer); + } + + private void hookRulerContextMenu() { + String id = "#DisassemblyPartRulerContext"; //$NON-NLS-1$ + MenuManager menuMgr = new MenuManager(id, id); + menuMgr.setRemoveAllWhenShown(true); + menuMgr.addMenuListener(new IMenuListener() { + public void menuAboutToShow(IMenuManager manager) { + DisassemblyPart.this.fillRulerContextMenu(manager); + } + }); + Menu menu = menuMgr.createContextMenu(fVerticalRuler.getControl()); + fVerticalRuler.getControl().setMenu(menu); + getSite().registerContextMenu(id, menuMgr, fViewer); + } + + private void contributeToActionBars() { + IWorkbenchPartSite site = getSite(); + site.setSelectionProvider(fViewer); + IContextService ctxService = (IContextService)site.getService(IContextService.class); + fContextActivation = ctxService.activateContext(KEY_BINDING_CONTEXT_DISASSEMBLY); + contributeToActionBars(getActionBars()); + } + + protected abstract IActionBars getActionBars(); + + protected void contributeToActionBars(IActionBars bars) { + for (Iterator<String> iter = fGlobalActions.keySet().iterator(); iter.hasNext();) { + String key = iter.next(); + IAction action = fGlobalActions.get(key); + bars.setGlobalActionHandler(key, action); + } + IMenuManager menu = bars.getMenuManager(); + IMenuManager navigateMenu= menu.findMenuUsingPath(IWorkbenchActionConstants.M_NAVIGATE); + if (navigateMenu != null) { + navigateMenu.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, fActionGotoPC); + navigateMenu.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, fActionGotoAddress); + navigateMenu.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, fActionGotoSymbol); + } + bars.updateActionBars(); + } + + protected void fillContextMenu(IMenuManager manager) { + Point cursorLoc = getSite().getShell().getDisplay().getCursorLocation(); + fViewer.getTextWidget().toControl(cursorLoc); + fActionToggleSource.update(); + fActionToggleSymbols.update(); + manager.add(new GroupMarker("group.top")); // ICommonMenuConstants.GROUP_TOP //$NON-NLS-1$ + manager.add(new Separator("group.breakpoints")); //$NON-NLS-1$ + manager.add(new Separator(IWorkbenchActionConstants.GO_TO)); + manager.add(fActionGotoPC); + manager.add(fActionGotoAddress); + manager.add(fActionGotoSymbol); + manager.add(new Separator("group.debug")); //$NON-NLS-1$ + manager.add(new Separator(ITextEditorActionConstants.GROUP_EDIT)); + manager.appendToGroup(ITextEditorActionConstants.GROUP_EDIT, fGlobalActions.get(ITextEditorActionConstants.COPY)); + manager.appendToGroup(ITextEditorActionConstants.GROUP_EDIT, fGlobalActions.get(ITextEditorActionConstants.SELECT_ALL)); + manager.add(new Separator(ITextEditorActionConstants.GROUP_SETTINGS)); + manager.add(fActionToggleSource); + manager.add(fActionToggleSymbols); + manager.add(fActionOpenPreferences); + manager.add(new Separator()); + manager.add(fActionRefreshView); + // Other plug-ins can contribute their actions here + manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); + } + + protected void fillRulerContextMenu(IMenuManager manager) { + fActionToggleBreakpoint.update(); + fActionToggleBreakpointEnablement.update(); + fActionToggleAddressColumn.update(); + fActionToggleFunctionColumn.update(); + + manager.add(new GroupMarker("group.top")); // ICommonMenuConstants.GROUP_TOP //$NON-NLS-1$ + manager.add(new Separator("group.breakpoints")); //$NON-NLS-1$ + manager.add(fActionToggleBreakpoint); + manager.add(fActionToggleBreakpointEnablement); + manager.add(new GroupMarker("debug")); //$NON-NLS-1$ + manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); + manager.add(new GroupMarker(ITextEditorActionConstants.GROUP_RESTORE)); + manager.add(new Separator("add")); //$NON-NLS-1$ + manager.add(new Separator(ITextEditorActionConstants.GROUP_RULERS)); + manager.add(fActionToggleAddressColumn); + manager.add(fActionToggleFunctionColumn); + manager.add(new Separator(ITextEditorActionConstants.GROUP_REST)); + + for (Object listener : fRulerContextMenuListeners.getListeners()) + ((IMenuListener) listener).menuAboutToShow(manager); + + manager.add(new Separator(ITextEditorActionConstants.GROUP_EDIT)); + manager.appendToGroup(ITextEditorActionConstants.GROUP_EDIT, fGlobalActions.get(ITextEditorActionConstants.COPY)); + } + + protected void fillLocalToolBar(IToolBarManager manager) { + manager.add(fActionGotoPC); + manager.add(fActionGotoAddress); + } + + protected void updateSelectionDependentActions() { + Iterator<Action> iterator= fSelectionActions.iterator(); + while (iterator.hasNext()) { + IUpdate action = (IUpdate)iterator.next(); + action.update(); + } + } + + protected void updateStateDependentActions() { + Iterator<AbstractDisassemblyAction> iterator= fStateDependentActions.iterator(); + while (iterator.hasNext()) { + IUpdate action = iterator.next(); + action.update(); + } + } + + protected void createActions() { + Action action; + action= new TextOperationAction(fViewer, ITextOperationTarget.COPY); + action.setText(DisassemblyMessages.Disassembly_action_Copy_label); + action.setImageDescriptor(DisassemblyImageRegistry.getImageDescriptor(DisassemblyImageRegistry.ICON_Copy_enabled)); + action.setDisabledImageDescriptor(DisassemblyImageRegistry.getImageDescriptor(DisassemblyImageRegistry.ICON_Copy_disabled)); + action.setActionDefinitionId(IWorkbenchActionDefinitionIds.COPY); + fGlobalActions.put(ITextEditorActionConstants.COPY, action); + fSelectionActions.add(action); + + action= new TextOperationAction(fViewer, ITextOperationTarget.SELECT_ALL); + action.setText(DisassemblyMessages.Disassembly_action_SelectAll_label); + action.setActionDefinitionId(IWorkbenchActionDefinitionIds.SELECT_ALL); + fGlobalActions.put(ITextEditorActionConstants.SELECT_ALL, action); + + action= new TextOperationAction(fViewer, ITextOperationTarget.PRINT); + action.setActionDefinitionId(IWorkbenchActionDefinitionIds.PRINT); + fGlobalActions.put(ITextEditorActionConstants.PRINT, action); + + fActionGotoPC = new ActionGotoProgramCounter(this); + fActionGotoPC.setActionDefinitionId(COMMAND_ID_GOTO_PC); + fStateDependentActions.add(fActionGotoPC); + registerWithHandlerService(fActionGotoPC); + + fActionGotoAddress = new ActionGotoAddress(this); + fActionGotoAddress.setActionDefinitionId(COMMAND_ID_GOTO_ADDRESS); + fStateDependentActions.add(fActionGotoAddress); + registerWithHandlerService(fActionGotoAddress); + + fActionGotoSymbol = new ActionGotoSymbol(this); + fActionGotoSymbol.setActionDefinitionId(COMMAND_ID_GOTO_SYMBOL); + fStateDependentActions.add(fActionGotoSymbol); + registerWithHandlerService(fActionGotoSymbol); + + fActionToggleSource = new ActionToggleSource(); + fStateDependentActions.add(fActionToggleSource); + fActionToggleBreakpoint = new ActionToggleBreakpoint(); +// fActionToggleBreakpoint.setActionDefinitionId(COMMAND_ID_TOGGLE_BREAKPOINT); +// registerWithHandlerService(fActionToggleBreakpoint); + fVerticalRuler.getControl().addMouseListener(new MouseAdapter() { + @Override + public void mouseDoubleClick(MouseEvent e) { + fActionToggleBreakpoint.update(); + if (fActionToggleBreakpoint.isEnabled()) { + fActionToggleBreakpoint.run(); + } + } + }); + fActionToggleBreakpointEnablement = new ActionToggleBreakpointEnablement(); + fActionToggleAddressColumn = new ActionToggleAddressColumn(); + fActionToggleFunctionColumn = new ActionToggleFunctionColumn(); + fActionToggleSymbols = new ActionToggleSymbols(); +// fActionSourceSteppingMode.setActionDefinitionId(COMMAND_ID_TOGGLE_STEPPING_MODE); +// registerWithHandlerService(fActionSourceSteppingMode); + fActionRefreshView = new ActionRefreshView(); + fStateDependentActions.add(fActionRefreshView); + fGlobalActions.put(ActionFactory.REFRESH.getId(), fActionRefreshView); + fActionOpenPreferences = new ActionOpenPreferences(getSite().getShell()); + } + + /** + * Register given action with the handler service for key bindings. + * + * @param action + */ + private void registerWithHandlerService(IAction action) { + if (fHandlerActivations == null) { + fHandlerActivations = new ArrayList<IHandlerActivation>(5); + } + IHandlerService handlerService = (IHandlerService)getSite().getService(IHandlerService.class); + fHandlerActivations.add(handlerService.activateHandler(action.getActionDefinitionId(), new ActionHandler(action))); + } + + private void gotoFrame(IFrameDMContext frame) { + if (fActive) { + gotoFrame(frame.getLevel(), PC_UNKNOWN); + } + } + + private void gotoFrame(int frame) { + if (fActive) { + gotoFrame(frame, PC_UNKNOWN); + } + } + + /* + * @see org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart#gotoProgramCounter() + */ + public final void gotoProgramCounter() { + if (fPCAddress != PC_RUNNING) { + updatePC(fPCAddress); + } + } + + /* + * @see org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart#gotoAddress(java.math.BigInteger) + */ + public final void gotoAddress(BigInteger address) { + fFocusAddress = address; + if (fDebugSessionId == null) { + return; + } + if (DEBUG) System.out.println("gotoAddress " + getAddressText(address)); //$NON-NLS-1$ + if (fGotoAddressPending == PC_UNKNOWN) { + fGotoAddressPending = address; + } + if (fUpdatePending) { + return; + } + AddressRangePosition pos = getPositionOfAddress(address); + if (pos != null) { + if (pos.fValid) { + AddressRangePosition previousPos = /* fUpdateBeforeFocus ? getPositionOfAddress(pos.fAddressOffset-1): */ null; + if (previousPos == null || previousPos.fValid) { + if (fGotoAddressPending.equals(address)) { + fGotoAddressPending = PC_UNKNOWN; + } + gotoPosition(pos, false); + } else { + int lines = fBufferZone+3; + BigInteger endAddress = pos.fAddressOffset; + BigInteger startAddress = previousPos.fAddressOffset.max( + endAddress.subtract(BigInteger.valueOf(lines * fDocument.getMeanSizeOfInstructions()))); + retrieveDisassembly(startAddress, endAddress, lines); + } + } else { + int lines = fBufferZone+3; + BigInteger endAddress = pos.fAddressOffset.add(pos.fAddressLength).min( + address.add(BigInteger.valueOf(lines * fDocument.getMeanSizeOfInstructions()))); + retrieveDisassembly(address, endAddress, lines); + } + } + } + + /* + * @see org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart#gotoSymbol(java.lang.String) + */ + public final void gotoSymbol(final String symbol) { + if (!fActive || !isSuspended() || fTargetFrameContext == null) { + return; + } + final IExpressions expressions= getService(IExpressions.class); + if (expressions == null) { + return; + } + IExpressionDMContext exprDmc= expressions.createExpression(fTargetContext, '&'+symbol); + final FormattedValueDMContext valueDmc= expressions.getFormattedValueContext(exprDmc, IFormattedValues.HEX_FORMAT); + final DsfExecutor executor= getSession().getExecutor(); + executor.submit(new Runnable() { + public void run() { + expressions.getFormattedExpressionValue(valueDmc, new DataRequestMonitor<FormattedValueDMData>(executor, null) { + @Override + protected void handleSuccess() { + FormattedValueDMData data= getData(); + final String value= data.getFormattedValue(); + final BigInteger address= decodeAddress(value); + if (address != null) { + asyncExec(new Runnable() { + public void run() { + gotoAddress(address); + }}); + } + } + @Override + protected void handleError() { + asyncExec(new Runnable() { + public void run() { + ErrorDialog.openError(getSite().getShell(), "Error", null, getStatus()); //$NON-NLS-1$ + }}); + } + }); + }}); + } + + private void gotoPosition(Position pos, boolean select) { + if (fViewer == null) { + return; + } + setFocusPosition(pos); + fViewer.setSelectedRange(pos.offset, select ? Math.max(pos.length-1, 0) : 0); + int revealOffset = pos.offset; + boolean onTop = false; + if (/* !fUpdateBeforeFocus && */ pos.offset > 0) { + try { + AddressRangePosition previousPos = fDocument.getModelPosition(pos.offset - 1); + if (previousPos instanceof LabelPosition) { + revealOffset = previousPos.offset; + onTop = true; + } else if (!previousPos.fValid) { + onTop = true; + } + } catch (BadLocationException e) { + // cannot happen + } + } + fViewer.revealOffset(revealOffset, onTop); + } + + private void gotoMarker(final IMarker marker) { + if (marker == null) { + return; + } + if (fDebugSessionId == null || fUpdatePending) { + fGotoMarkerPending = marker; + return; + } + fGotoMarkerPending = null; + + //TLETODO [disassembly] goto (breakpoint) marker + } + + /* + * @see org.eclipse.jface.text.IViewportListener#viewportChanged(int) + */ + public void viewportChanged(int verticalOffset) { + if (fDebugSessionId != null && fGotoAddressPending == PC_UNKNOWN && fScrollPos == null && !fUpdatePending && !fRefreshViewPending) { + fUpdatePending = true; + invokeLater(new Runnable() { + public void run() { + assert fUpdatePending; + if (fUpdatePending) { + fUpdatePending = false; + updateVisibleArea(); + } + } + }); + } + } + + /** + * Update lines of currently visible area + one page buffer zone below. + */ + private void updateVisibleArea() { + if (!fActive || fUpdatePending || fViewer == null || fDebugSessionId == null) { + return; + } + if (fTargetContext == null || !isSuspended(fTargetContext) || fFrameAddress == PC_UNKNOWN) { + return; + } + StyledText styledText = fViewer.getTextWidget(); + Rectangle clientArea = styledText.getClientArea(); + fBufferZone = Math.max(8, clientArea.height / styledText.getLineHeight()); + int topIndex = fViewer.getTopIndex(); + int bottomIndex = fViewer.getBottomIndex(); + int focusIndex = -1; + boolean focusVisible = false; + boolean isScrollingUp = fViewer.isUserTriggeredScrolling() && fViewer.getLastTopPixel() >= styledText.getTopPixel(); + if (fFocusPos != null) { + try { + int focusOffset = fFocusPos.offset; + focusIndex = fDocument.getLineOfOffset(focusOffset); + focusVisible = focusIndex >= topIndex && focusIndex <= bottomIndex; + // workaround for: Clicking the IP annotation in the right ruler has no effect. + // we deselect the IP location if it is scrolled outside the visible area + if (!focusVisible) { + Point selection = fViewer.getSelectedRange(); + if (selection.x == focusOffset && selection.y > 0) { + fViewer.setSelectedRange(selection.x, 0); + } + } + } catch (BadLocationException e) { + setFocusPosition(null); + } + } + if (!focusVisible) { + focusIndex = topIndex + fScrollLine; + } + BigInteger focusAddress = getAddressOfLine(focusIndex); + bottomIndex += 2; + AddressRangePosition bestPosition = null; + int bestLine = -1; + BigInteger bestDistance = null; + Iterator<AddressRangePosition> it = fDocument.getInvalidAddressRanges().iterator(); + while (it.hasNext()) { + AddressRangePosition p = it.next(); + try { + int line = fDocument.getLineOfOffset(p.offset); + if (line >= topIndex && line <= bottomIndex) { + if (p instanceof DisassemblyPosition || p.fAddressLength.compareTo( + BigInteger.valueOf(fBufferZone * 2)) <= 0) { + // small areas and known areas are OK to update + } else if (!isScrollingUp && !fUpdateBeforeFocus + && p.fAddressOffset.compareTo(focusAddress) < 0) { + continue; + } + BigInteger distance = p.fAddressOffset.subtract(focusAddress).abs(); + if (bestDistance == null || distance.compareTo(bestDistance) < 0) { + bestPosition = p; + bestLine = line; + bestDistance = distance; + if (bestDistance.compareTo(BigInteger.valueOf(fBufferZone * 2)) <= 0) { + break; + } + } + } + } catch (BadLocationException e) { + continue; + } + } + if (bestPosition != null) { + int lines = fBufferZone+3; + BigInteger startAddress = bestPosition.fAddressOffset; + BigInteger endAddress = bestPosition.fAddressOffset.add(bestPosition.fAddressLength); + BigInteger addressRange = BigInteger.valueOf(lines * fDocument.getMeanSizeOfInstructions()); + if (bestLine > focusIndex || bestLine == focusIndex && startAddress.compareTo(focusAddress) >= 0) { + // insert at start of range + if (endAddress.subtract(startAddress).compareTo(addressRange) < 0) { + // try to increase range to reduce number of requests + Iterator<?> iter = fDocument.getModelPositionIterator(endAddress); + while (iter.hasNext()) { + AddressRangePosition p = (AddressRangePosition)iter.next(); + if (p.fValid) { + endAddress = endAddress.add(p.fAddressLength); + if (endAddress.subtract(startAddress).compareTo(addressRange) >= 0) { + break; + } + } else { + break; + } + } + } + } else { + // insert at end of range + startAddress = startAddress.max(endAddress.subtract(addressRange)); + // make sure we get all disassembly lines until endAddress + lines = endAddress.subtract(startAddress).intValue(); + } + retrieveDisassembly(startAddress, endAddress, lines); + } + scheduleDoPending(); + } + + private void asyncExec(Runnable runnable) { + if (fViewer != null) { + fViewer.getControl().getDisplay().asyncExec(runnable); + } + } + private void invokeLater(Runnable runnable) { + invokeLater(10, runnable); + } + private void invokeLater(int delay, Runnable runnable) { + if (fViewer != null) { + fViewer.getControl().getDisplay().timerExec(delay, runnable); + } + } + + /** + * Insert sourcelines if available. + */ + /*default*/ void updateInvalidSource() { + if (fViewer == null) { + return; + } + boolean unlock = false; + try { + if (fScrollPos == null) { + if (fUpdatePending) { + fUpdateSourcePending= true; + return; + } + fUpdateSourcePending= false; + unlock = true; + fUpdatePending = true; + lockScroller(); + } + ArrayList<SourcePosition> copy = new ArrayList<SourcePosition>(fDocument.getInvalidSource()); + Iterator<SourcePosition> it = copy.iterator(); + while (it.hasNext()) { + SourcePosition p = it.next(); + if (!p.fValid) { + insertSource(p); + } else if (DEBUG && fDocument.getInvalidSource().remove(p)) { + System.err.println("!!! valid source position in invalid source list at "+getAddressText(p.fAddressOffset)); //$NON-NLS-1$ + } + } + } finally { + if (unlock) { + fUpdatePending = false; + unlockScroller(); + doPending(); + } + } + } + + /** + * Show disassembly for given (source) file. + * + * @param file + * @param lines + */ + void retrieveDisassembly(final String file, final int lines, final boolean mixed) { + if (fDebugSessionId == null) { + return; + } + if (fUpdatePending) { + invokeLater(new Runnable() { + public void run() { + retrieveDisassembly(file, lines, mixed); + }}); + return; + } + if (DEBUG) System.out.println("retrieveDisassembly "+file); //$NON-NLS-1$ + String debuggerPath= file; + + // try reverse lookup + final ISourceLookup lookup= getService(ISourceLookup.class); + final ISourceLookupDMContext ctx= DMContexts.getAncestorOfType(fTargetContext, ISourceLookupDMContext.class); + final DsfExecutor executor= getSession().getExecutor(); + Query<String> query= new Query<String>() { + @Override + protected void execute(final DataRequestMonitor<String> rm) { + final DataRequestMonitor<String> request= new DataRequestMonitor<String>(executor, rm) { + @Override + protected void handleSuccess() { + rm.setData(getData()); + rm.done(); + } + }; + lookup.getDebuggerPath(ctx, file, request); + } + }; + try { + getSession().getExecutor().execute(query); + debuggerPath= query.get(); + } catch (InterruptedException exc) { + internalError(exc); + } catch (ExecutionException exc) { + internalError(exc); + } + + final IDisassembly disassembly= fServicesTracker.getService(IDisassembly.class); + final IDisassemblyDMContext context= DMContexts.getAncestorOfType(fTargetContext, IDisassemblyDMContext.class); + + final String finalFile= debuggerPath; + final DataRequestMonitor<IMixedInstruction[]> disassemblyRequest= new DataRequestMonitor<IMixedInstruction[]>(executor, null) { + @Override + public void handleCompleted() { + final IMixedInstruction[] data= getData(); + if (!isCanceled() && data != null) { + asyncExec(new Runnable() { + public void run() { + if (!insertDisassembly(null, data)) { + // retry in non-mixed mode + retrieveDisassembly(file, lines, false); + } + }}); + } else { + final IStatus status= getStatus(); + if (status != null && !status.isOK()) { + asyncExec(new Runnable() { + public void run() { + ErrorDialog.openError(getSite().getShell(), "Error", null, getStatus()); //$NON-NLS-1$ + } + }); + } + fUpdatePending= false; + } + } + }; + assert !fUpdatePending; + fUpdatePending = true; + executor.submit(new Runnable() { + public void run() { + disassembly.getMixedInstructions(context, finalFile, 1, lines, disassemblyRequest); + }}); + } + + private void retrieveDisassembly(BigInteger startAddress, BigInteger endAddress, int lines) { + if (fDebugSessionId == null) { + return; + } + if (DEBUG) System.out.println("retrieveDisassembly "+getAddressText(startAddress)+" "+lines+" lines"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + retrieveDisassembly(startAddress, endAddress, lines, true); + } + + private void retrieveDisassembly(final BigInteger startAddress, BigInteger endAddress, final int linesHint, boolean mixed) { + assert !fUpdatePending; + fUpdatePending = true; + final int lines= linesHint + 2; + final BigInteger addressLength= BigInteger.valueOf(lines * 4); + if (endAddress.subtract(startAddress).compareTo(addressLength) > 0) { + endAddress= startAddress.add(addressLength); + } + boolean insideActiveFrame= startAddress.equals(fFrameAddress); + String file= null; + int lineNumber= -1; + if (insideActiveFrame && fTargetFrameData != null) { + file= fTargetFrameData.getFile(); + if (file != null && file.trim().length() == 0) { + file= null; + } + lineNumber= fTargetFrameData.getLine(); + } + final String finalFile= file; + final int finalLineNumber= lineNumber; + final BigInteger finalEndAddress= endAddress; + + final DsfExecutor executor= getSession().getExecutor(); + final IDisassembly disassembly= fServicesTracker.getService(IDisassembly.class); + final IDisassemblyDMContext context= DMContexts.getAncestorOfType(fTargetContext, IDisassemblyDMContext.class); + + if (mixed) { + final DataRequestMonitor<IMixedInstruction[]> disassemblyRequest= new DataRequestMonitor<IMixedInstruction[]>(executor, null) { + @Override + public void handleCompleted() { + final IMixedInstruction[] data= getData(); + if (!isCanceled() && data != null) { + asyncExec(new Runnable() { + public void run() { + if (!insertDisassembly(startAddress, data)) { + // retry in non-mixed mode + retrieveDisassembly(startAddress, finalEndAddress, linesHint, false); + } + }}); + } else { + final IStatus status= getStatus(); + if (status != null && !status.isOK()) { + asyncExec(new Runnable() { + public void run() { + doScrollLocked(new Runnable() { + public void run() { + insertError(startAddress, status.getMessage()); + } + }); + }}); + } + fUpdatePending= false; + } + } + }; + if (file != null) { + executor.submit(new Runnable() { + public void run() { + disassembly.getMixedInstructions(context, finalFile, finalLineNumber, lines*2, disassemblyRequest); + }}); + } else { + executor.submit(new Runnable() { + public void run() { + disassembly.getMixedInstructions(context, startAddress, finalEndAddress, disassemblyRequest); + }}); + } + } else { + final DataRequestMonitor<IInstruction[]> disassemblyRequest= new DataRequestMonitor<IInstruction[]>(executor, null) { + @Override + public void handleCompleted() { + if (!isCanceled() && getData() != null) { + asyncExec(new Runnable() { + public void run() { + insertDisassembly(startAddress, getData()); + }}); + } else { + final IStatus status= getStatus(); + if (status != null && !status.isOK()) { + asyncExec(new Runnable() { + public void run() { + doScrollLocked(new Runnable() { + public void run() { + insertError(startAddress, status.getMessage()); + } + }); + }}); + } + fUpdatePending= false; + } + } + }; + if (file != null) { + executor.submit(new Runnable() { + public void run() { + disassembly.getInstructions(context, finalFile, finalLineNumber, lines, disassemblyRequest); + }}); + } else { + executor.submit(new Runnable() { + public void run() { + disassembly.getInstructions(context, startAddress, finalEndAddress, disassemblyRequest); + }}); + } + } + } + + private void insertError(BigInteger address, String message) { + AddressRangePosition p = null; + p = getPositionOfAddress(address); + if (p.fValid) { + return; + } + try { + fDocument.insertErrorLine(p, address, BigInteger.ONE, message); + } catch (BadLocationException exc) { + internalError(exc); + } + } + + private void insertDisassembly(BigInteger startAddress, IInstruction[] instructions) { + if (fViewer == null || fDebugSessionId == null) { + return; + } + if (DEBUG) System.out.println("insertDisassembly "+getAddressText(startAddress)); //$NON-NLS-1$ + assert fUpdatePending; + if (!fUpdatePending) { + // safe-guard in case something weird is going on + return; + } + try { + lockScroller(); + + AddressRangePosition p= null; + for (int j = 0; j < instructions.length; j++) { + IInstruction instruction = instructions[j]; + BigInteger address= instruction.getAdress(); + if (startAddress == null || startAddress.compareTo(BigInteger.ZERO) < 0) { + fGotoAddressPending = startAddress = address; + } + if (p == null || !p.containsAddress(address)) { + p = getPositionOfAddress(address); + } + if (p instanceof ErrorPosition && p.fValid) { + p.fValid = false; + fDocument.getInvalidAddressRanges().add(p); + } else if (p == null || p.fValid) { + if (DEBUG) System.out.println("Excess disassembly lines at " + getAddressText(address)); //$NON-NLS-1$ + return; + } + boolean hasSource= false; + String compilationPath= null; + // insert symbol label + final String functionName= instruction.getFuntionName(); + if (functionName != null && functionName.length() > 0 && instruction.getOffset() == 0) { + p = fDocument.insertLabel(p, address, functionName, fShowSymbols && (!hasSource || fShowDisassembly)); + } + // determine instruction byte length + BigInteger instrLength= null; + if (j < instructions.length - 1) { + instrLength= instructions[j+1].getAdress().subtract(instruction.getAdress()).abs(); + } else if (instructions.length == 1) { + if (p.fAddressLength.compareTo(BigInteger.valueOf(8)) <= 0) { + instrLength= p.fAddressLength; + } + } + if (instrLength == null) { + // cannot determine length of last instruction + break; + } + final String opCode; + // insert function name+offset instead of opcode bytes + if (functionName != null && functionName.length() > 0) { + opCode= functionName + '+' + instruction.getOffset(); + } else { + opCode= ""; //$NON-NLS-1$ + } + p = fDocument.insertDisassemblyLine(p, address, instrLength.intValue(), opCode, instruction.getInstruction(), compilationPath, -1); + if (p == null) { + break; + } + } + + } catch (BadLocationException e) { + // should not happen + internalError(e); + } finally { + fUpdatePending = false; + updateInvalidSource(); + unlockScroller(); + doPending(); + updateVisibleArea(); + } + } + + private boolean insertDisassembly(BigInteger startAddress, IMixedInstruction[] mixedInstructions) { + if (fViewer == null || fDebugSessionId == null) { + return true; + } + if (DEBUG) System.out.println("insertDisassembly "+getAddressText(startAddress)); //$NON-NLS-1$ + assert fUpdatePending; + if (!fUpdatePending) { + // safe-guard in case something weird is going on + return true; + } + // indicates whether disassembly for the start address was inserted + boolean success= false; + try { + lockScroller(); + + AddressRangePosition p= null; + for (int i = 0; i < mixedInstructions.length; ++i) { + IMixedInstruction mixedInstruction= mixedInstructions[i]; + final String file= mixedInstruction.getFileName(); + final int lineNumber= mixedInstruction.getLineNumber() - 1; + IInstruction[] instructions= mixedInstruction.getInstructions(); + for (int j = 0; j < instructions.length; ++j) { + IInstruction instruction = instructions[j]; + BigInteger address= instruction.getAdress(); + if (startAddress == null || startAddress.compareTo(BigInteger.ZERO) < 0) { + fGotoAddressPending = startAddress = address; + } + if (p == null || !p.containsAddress(address)) { + p = getPositionOfAddress(address); + } + if (p instanceof ErrorPosition && p.fValid) { + p.fValid = false; + fDocument.getInvalidAddressRanges().add(p); + } else if (p == null) { + if (DEBUG) System.out.println("Excess disassembly lines at " + getAddressText(address)); //$NON-NLS-1$ + return success; + } else if (p.fValid) { + if (DEBUG) System.out.println("Excess disassembly lines at " + getAddressText(address)); //$NON-NLS-1$ + if (file != null && lineNumber >= 0 || p.fAddressLength == BigInteger.ONE) { + // override probably unaligned disassembly + p.fValid = false; + fDocument.getInvalidAddressRanges().add(p); + } else { + return success; + } + } + boolean hasSource= false; + if (file != null && lineNumber >= 0) { + p = insertSource(p, address, file, lineNumber); + hasSource = fFile2Storage.get(file) != null; + } + // insert symbol label + final String functionName= instruction.getFuntionName(); + if (functionName != null && functionName.length() > 0 && instruction.getOffset() == 0) { + p = fDocument.insertLabel(p, address, functionName, fShowSymbols && (!hasSource || fShowDisassembly)); + } + // determine instruction byte length + BigInteger instrLength= null; + if (j < instructions.length - 1) { + instrLength= instructions[j+1].getAdress().subtract(instruction.getAdress()).abs(); + } else if (i < mixedInstructions.length - 1) { + int nextSrcLineIdx= i+1; + while (nextSrcLineIdx < mixedInstructions.length) { + IInstruction[] nextInstrs= mixedInstructions[nextSrcLineIdx].getInstructions(); + if (nextInstrs.length > 0) { + instrLength= nextInstrs[0].getAdress().subtract(instruction.getAdress()).abs(); + break; + } + ++nextSrcLineIdx; + } + if (nextSrcLineIdx >= mixedInstructions.length) { + break; + } + } else if (instructions.length == 1) { + if (p.fAddressLength.compareTo(BigInteger.valueOf(8)) <= 0) { + instrLength= p.fAddressLength; + } + } + if (instrLength == null) { + // cannot determine length of last instruction + break; + } + final String opCode; + // insert function name+offset instead of opcode bytes + if (functionName != null && functionName.length() > 0) { + opCode= functionName + '+' + instruction.getOffset(); + } else { + opCode= ""; //$NON-NLS-1$ + } + success= success || address.compareTo(startAddress) == 0; + p = fDocument.insertDisassemblyLine(p, address, instrLength.intValue(), opCode, instruction.getInstruction(), file, lineNumber); + if (p == null && success) { + break; + } + } + } + + } catch (BadLocationException e) { + // should not happen + internalError(e); + } finally { + fUpdatePending = false; + if (success) { + updateInvalidSource(); + unlockScroller(); + doPending(); + updateVisibleArea(); + } else { + unlockScroller(); + } + } + return success; + } + + private void retrieveFrameAddress(final IExecutionDMContext targetContext, final int frame) { + if (targetContext != null && isSuspended(targetContext)) { + if (fUpdatePending) { + gotoFrame(frame); + return; + } + if (DEBUG) System.out.println("retrieveFrameAddress "+frame); //$NON-NLS-1$ + fUpdatePending = true; + final IStack stack= fServicesTracker.getService(IStack.class); + final DsfExecutor executor= getSession().getExecutor(); + if (fTargetFrameContext == null) { + if (frame == 0) { + final DataRequestMonitor<IFrameDMContext> request= new DataRequestMonitor<IFrameDMContext>(executor, null) { + @Override + protected void handleCompleted() { + fUpdatePending= false; + fTargetFrameContext= getData(); + if (fTargetFrameContext != null) { + retrieveFrameAddress(targetContext, frame); + } + } + }; + executor.submit(new Runnable() { + public void run() { + stack.getTopFrame(targetContext, request); + }}); + } else { + // TODO retrieve other stack frame + } + return; + } + final DataRequestMonitor<IFrameDMData> request= new DataRequestMonitor<IFrameDMData>(executor, null) { + @Override + protected void handleCompleted() { + if (!isCanceled()) { + fUpdatePending= false; + final IFrameDMData frameData= getData(); + fTargetFrameData= frameData; + final IAddress address= frameData.getAddress(); + final BigInteger addressValue= address.getValue(); + if (DEBUG) System.out.println("retrieveFrameAddress done "+getAddressText(addressValue)); //$NON-NLS-1$ + asyncExec(new Runnable() { + public void run() { + if (address.getSize() * 4 > fAddressSize) { + addressSizeChanged(address.getSize() * 4); + } + if (frame == 0) { + updatePC(addressValue); + } else { + gotoFrame(frame, addressValue); + } + } + + }); + } + } + }; + executor.submit(new Runnable() { + public void run() { + stack.getFrameData(fTargetFrameContext, request); + }}); + } + } + + private void addressSizeChanged(int addressSize) { + BigInteger oldEndAddress= fEndAddress; + fEndAddress= BigInteger.ONE.shiftLeft(addressSize); + int oldAddressSize= fAddressSize; + fAddressSize= addressSize; + if (addressSize < oldAddressSize) { + fDocument.deleteDisassemblyRange(fEndAddress, oldEndAddress, true, true); + List<AddressRangePosition> positions= fDocument.getInvalidAddressRanges(); + List<AddressRangePosition> toRemove= new ArrayList<AddressRangePosition>(); + for (AddressRangePosition position : positions) { + if (position.fAddressOffset.compareTo(fEndAddress) >= 0) { + try { + fDocument.replace(position, position.length, ""); //$NON-NLS-1$ + fDocument.removeModelPosition(position); + toRemove.add(position); + } catch (BadLocationException exc) { + internalError(exc); + } + } else if (position.containsAddress(fEndAddress)){ + position.fAddressLength= fEndAddress.subtract(position.fAddressOffset); + } + } + positions.removeAll(toRemove); + } else if (addressSize > oldAddressSize) { + fDocument.insertInvalidAddressRange(fDocument.getLength(), 0, oldEndAddress, fEndAddress); + } else { + return; + } + if (fAddressRulerColumn != null) { + fAddressRulerColumn.setAddressSize(addressSize); + if (fComposite != null) { + fComposite.layout(true); + } + } + } + + private AddressRangePosition getPositionOfAddress(BigInteger address) { + if (address == null || address.compareTo(BigInteger.ZERO) < 0) { + return null; + } + AddressRangePosition pos = fDocument.getPositionOfAddress(address); + assert !(pos instanceof SourcePosition); + assert pos != null || address.compareTo(fStartAddress) < 0|| address.compareTo(fEndAddress) >= 0; + return pos; + } + + private BigInteger getAddressOfLine(int line) { + return fDocument.getAddressOfLine(line); + } + + /** + * Passing the focus request to the viewer's control. + */ + @Override + public void setFocus() { + fViewer.getControl().setFocus(); + } + + protected void setActive(boolean active) { + if (DEBUG) System.out.println("setActive("+ active +")"); //$NON-NLS-1$ //$NON-NLS-2$ + fActive = active; + if (fActive) { + if (fRefreshAll) { + fRefreshAll = false; + refreshView(0); + } else { + doPendingPCUpdates(); + if (fTargetContext != null) { + int frame = getActiveStackFrame(); + if (frame < 0 && isSuspended(fTargetContext)) { + frame= 0; + } + if (frame != fTargetFrame) { + gotoFrame(frame); + } + } + } + } else { + fGotoAddressPending= fFocusAddress= PC_UNKNOWN; + } + firePropertyChange(PROP_ACTIVE); + } + + private int getActiveStackFrame() { + if (fTargetFrameContext != null) { + return fTargetFrameContext.getLevel(); + } + return -1; + } + + /** + * + */ + protected void updateDebugContext() { + IAdaptable debugContext= DebugUITools.getDebugContext(); + if (debugContext instanceof IDMVMContext) { + setDebugContext((IDMVMContext)debugContext); + } + } + + protected void setDebugContext(IDMVMContext vmContext) { + if (vmContext != null) { + IDMContext dmContext= vmContext.getDMContext(); + String sessionId= dmContext.getSessionId(); + if (!sessionId.equals(fDebugSessionId)) { + // switch to different session or initiate session + if (DEBUG) System.out.println("DisassemblyPart.setDebugContext() " + sessionId); //$NON-NLS-1$ + fTargetContext= null; + if (dmContext instanceof IFrameDMContext) { + IFrameDMContext frame= (IFrameDMContext) dmContext; + IExecutionDMContext executionContext= DMContexts.getAncestorOfType(frame, IExecutionDMContext.class); + if (executionContext != null) { + fTargetContext= executionContext; + fTargetFrameContext= frame; + fTargetFrame= frame.getLevel(); + } + } + if (fTargetContext != null) { + if (fDebugSessionId != null) { + if (getSession() != null) { + getSession().removeServiceEventListener(this); + } + } + fDebugSessionId= sessionId; + if (fServicesTracker != null) { + fServicesTracker.dispose(); + } + fServicesTracker = new DsfServicesTracker(DsfUIPlugin.getBundleContext(), sessionId); + if (fViewer != null) { + debugContextChanged(); + } + } + } else if (dmContext instanceof IFrameDMContext) { + // switch to different frame + IFrameDMContext frame= (IFrameDMContext) dmContext; + final IDMContext[] parents= frame.getParents(); + for (IDMContext context : parents) { + if (context instanceof IExecutionDMContext) { + fTargetContext= (IExecutionDMContext) context; + fTargetFrameContext= frame; + gotoFrame(frame); + break; + } + } + } + } else if (fDebugSessionId != null) { + if (getSession() != null) { + getSession().removeServiceEventListener(this); + } + fDebugSessionId= null; + fTargetContext= null; + if (fServicesTracker != null) { + fServicesTracker.dispose(); + fServicesTracker= null; + } + if (fViewer != null) { + debugContextChanged(); + } + } + } + + private void debugContextChanged() { + if (DEBUG) System.out.println("DisassemblyPart.debugContextChanged()"); //$NON-NLS-1$ + fRunnableQueue.clear(); + fUpdatePending = false; + resetViewer(); + if (fDebugSessionId != null) { + final DsfSession session= getSession(); + session.addServiceEventListener(this, null); + updatePC(PC_UNKNOWN); + + if (fGotoAddressPending != PC_UNKNOWN) { + gotoAddress(fGotoAddressPending); + } + if (fGotoMarkerPending != null) { + gotoMarker(fGotoMarkerPending); + } + fViewer.addViewportListener(this); + } else { + fViewer.removeViewportListener(this); + fGotoMarkerPending = null; +// invokeLater(new Runnable() { +// public void run() { +// closePart(); +// }}); + } + updateTitle(); + updateStateDependentActions(); + firePropertyChange(PROP_CONNECTED); + firePropertyChange(PROP_SUSPENDED); + } + + /* + * @see org.eclipse.cdt.dsf.service.DsfSession.SessionEndedListener#sessionEnded(org.eclipse.cdt.dsf.service.DsfSession) + */ + public void sessionEnded(DsfSession endedSsession) { + if (endedSsession.getId().equals(fDebugSessionId)) { + asyncExec(new Runnable() { + public void run() { + setDebugContext(null); + }}); + } + } + + @DsfServiceEventHandler + public void handleEvent(IExitedDMEvent event) { + final IExecutionDMContext context= event.getDMContext(); + if (context.equals(fTargetContext) + || DMContexts.isAncestorOf(fTargetContext, context)) { + asyncExec(new Runnable() { + public void run() { + setDebugContext(null); + }}); + } + } + + @DsfServiceEventHandler + public void handleEvent(ISuspendedDMEvent event) { + final IExecutionDMContext context= event.getDMContext(); + if (context.equals(fTargetContext) + || DMContexts.isAncestorOf(fTargetContext, context)) { + asyncExec(new Runnable() { + public void run() { + updatePC(PC_UNKNOWN); + firePropertyChange(PROP_SUSPENDED); + } + }); + } + } + + @DsfServiceEventHandler + public void handleEvent(IResumedDMEvent event) { + final IExecutionDMContext context= event.getDMContext(); + if (context.equals(fTargetContext) + || DMContexts.isAncestorOf(fTargetContext, context)) { + asyncExec(new Runnable() { + public void run() { + updatePC(PC_RUNNING); + firePropertyChange(PROP_SUSPENDED); + } + }); + } + } + + private void attachBreakpointsAnnotationModel() { + IAnnotationModel annotationModel = fViewer.getAnnotationModel(); + if (annotationModel instanceof IAnnotationModelExtension) { + IAnnotationModelExtension ame= (IAnnotationModelExtension) annotationModel; + ame.addAnnotationModel(BREAKPOINT_ANNOTATIONS, new BreakpointsAnnotationModel()); + } + } + + private void refreshView(int delay) { + if (fViewer == null || fRefreshViewPending || fRefreshAll) { + return; + } + fRunnableQueue.clear(); + fRefreshViewPending = true; + final long refreshViewScheduled = System.currentTimeMillis() + delay; + final Runnable refresh = new Runnable() { + public void run() { + fRefreshViewPending = false; + long now = System.currentTimeMillis(); + if (now >= refreshViewScheduled) { + if (DEBUG) System.err.println("*** refreshing view ***"); //$NON-NLS-1$ + fFocusAddress = PC_UNKNOWN; + int targetFrame= fTargetFrame; + resetViewer(); + if (fScrollPos != null) { + fScrollPos.isDeleted = true; + } + gotoFrame(targetFrame); + } else { + refreshView((int)(refreshViewScheduled - now)); + } + }}; + if (delay > 0) { + invokeLater(delay, new Runnable() { + public void run() { + doScrollLocked(refresh); + }}); + } else { + doScrollLocked(refresh); + } + } + + private void resetViewer() { + // clear all state and cache + fPCAnnotationUpdatePending = false; + fGotoFramePending = false; + fPCAddress = fFrameAddress = PC_RUNNING; + fTargetFrame = -1; + fGotoAddressPending = fFocusAddress; + fFocusAddress = PC_UNKNOWN; + setFocusPosition(null); + fPCHistory.clear(); + fPendingPCUpdates.clear(); + fFile2Storage.clear(); + DisassemblyDocument doc= fDocument; + fDocument = createDocument(); + fViewer.setDocument(fDocument, new AnnotationModel()); + doc.dispose(); + if (fDebugSessionId != null) { + attachBreakpointsAnnotationModel(); + fDocument.insertInvalidAddressRange(0, 0, fStartAddress, fEndAddress); + } + } + + private AddressRangePosition getPCPosition(BigInteger address) { + if (address.compareTo(BigInteger.ZERO) < 0) { + // invalid address + return null; + } + AddressRangePosition pos = getPositionOfAddress(address); + if (pos == null || !pos.fValid) { + // invalid disassembly line + return null; + } + if (pos.length > 0) { + // valid disassembly line + return pos; + } + // hidden disassembly + if (!(pos instanceof DisassemblyPosition)) { + return pos; + } + String srcFile = ((DisassemblyPosition)pos).getFile(); + if (srcFile == null) { + return pos; + } + SourceFileInfo fi = fDocument.getSourceInfo(srcFile); + if (fi == null) { + return pos; + } + if (fi.fSource == null) { + if (fi.fError != null) { + // could not read source + return pos; + } + return null; + } +// if (!fi.fValid) { +// // need line info first +// return null; +// } + // determine stmt line of source range + try { + int stmtLine = ((DisassemblyPosition)pos).getLine(); + if (stmtLine < 0) { + return pos; + } + BigInteger stmtAddress = fi.fLine2Addr[stmtLine]; + if (stmtAddress.compareTo(BigInteger.ZERO) < 0) { + return pos; + } + SourcePosition srcPos = fDocument.getSourcePosition(stmtAddress); + if (srcPos == null) { + return pos; + } else if (!srcPos.fValid) { + return null; + } + assert stmtLine >= srcPos.fLine; + int baseOffset = fi.fSource.getLineOffset(srcPos.fLine); + IRegion stmtLineRegion = fi.fSource.getLineInformation(stmtLine); + int lineOffset = stmtLineRegion.getOffset(); + int offset = srcPos.offset + lineOffset - baseOffset; + int length = stmtLineRegion.getLength() + 1; + if (offset >= srcPos.offset && offset < srcPos.offset + srcPos.length) { + return new AddressRangePosition(offset, length, address, BigInteger.ZERO); + } + } catch (BadLocationException e) { + internalError(e); + } + return pos; + } + + /** + * Update the annotation indicating the given address. + * @return a position which denotes the documents position + */ + private AddressRangePosition updateAddressAnnotation(Annotation annotation, BigInteger address) { + IAnnotationModel annotationModel = fViewer.getAnnotationModel(); + annotationModel.removeAnnotation(annotation); + AddressRangePosition pos = getPCPosition(address); + if (pos != null) { + annotationModel.addAnnotation(annotation, new Position(pos.offset, Math.max(0, pos.length-1))); + } + return pos; + } + + public IBreakpoint[] getBreakpointsAtLine(int line) { + BreakpointsAnnotationModel bpModel= null; + IAnnotationModel am= fViewer.getAnnotationModel(); + if (am instanceof IAnnotationModelExtension) { + IAnnotationModelExtension ame= (IAnnotationModelExtension) am; + bpModel= (BreakpointsAnnotationModel) ame.getAnnotationModel(BREAKPOINT_ANNOTATIONS); + if (bpModel != null) { + IRegion lineRegion; + try { + lineRegion= fDocument.getLineInformation(line); + } catch (BadLocationException exc) { + return null; + } + int offset= lineRegion.getOffset(); + int length= lineRegion.getLength(); + @SuppressWarnings("unchecked") + Iterator<SimpleMarkerAnnotation> it= bpModel.getAnnotationIterator(offset, length, true, true); + List<IBreakpoint> bpList= new ArrayList<IBreakpoint>(5); + final IBreakpointManager bpMgr= DebugPlugin.getDefault().getBreakpointManager(); + while (it.hasNext()) { + final SimpleMarkerAnnotation annotation= it.next(); + IBreakpoint bp= bpMgr.getBreakpoint(annotation.getMarker()); + if (bp != null) { + bpList.add(bp); + } + } + if (bpList.size() > 0) { + return bpList.toArray(new IBreakpoint[bpList.size()]); + } + } + } + return null; + } + + private void gotoFrame(int frame, BigInteger address) { + if (DEBUG) System.out.println("gotoFrame " + frame + " " + getAddressText(address)); //$NON-NLS-1$ //$NON-NLS-2$ + fTargetFrame = frame; + fFrameAddress = address; + if (fTargetFrame == -1) { + fTargetFrame = getActiveStackFrame(); + if (fTargetFrame < 0 && isSuspended(fTargetContext)) { + fTargetFrame= 0; + } + if (fTargetFrame == -1) { + fGotoFramePending = false; + return; + } + } + fGotoFramePending = true; + if (frame == 0) { + fPCAddress = fFrameAddress; + } + if (fFrameAddress.compareTo(PC_UNKNOWN) == 0) { + if (!fUpdatePending) { + fGotoFramePending = false; + retrieveFrameAddress(fTargetContext, fTargetFrame); + } + return; + } + AddressRangePosition pcPos = updatePCAnnotation(); + if (pcPos == null && fFrameAddress.compareTo(BigInteger.ZERO) >= 0) { + pcPos = getPCPosition(fFrameAddress); + if (pcPos == null) { + gotoAddress(fFrameAddress); + return; + } + } + if (pcPos != null) { + if (frame == 0) { + addToPCHistory(pcPos); + } + fGotoFramePending = false; + if (fGotoAddressPending == fFrameAddress) { + fGotoAddressPending = PC_UNKNOWN; + } +// if (DEBUG) System.out.println("pc updated "+getAddressText(address)); //$NON-NLS-1$ + gotoPosition(pcPos, false); + updateVisibleArea(); + } else { + // give up + fGotoFramePending = false; + fGotoAddressPending = PC_UNKNOWN; + } + doPendingPCUpdates(); + } + + /* + * @see org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart#isActive() + */ + public final boolean isActive() { + return fActive; + } + + /* + * @see org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart#isConnected() + */ + public final boolean isConnected() { + return fDebugSessionId != null && fTargetContext != null; + } + + /* + * @see org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart#isSuspended() + */ + public final boolean isSuspended() { + return isConnected() && isSuspended(fTargetContext); + } + + /* + * @see org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart#getTextViewer() + */ + public final ISourceViewer getTextViewer() { + return fViewer; + } + + /* + * @see org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart#addRulerContextMenuListener(org.eclipse.jface.action.IMenuListener) + */ + public final void addRulerContextMenuListener(IMenuListener listener) { + fRulerContextMenuListeners.add(listener); + } + + /* + * @see org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart#removeRulerContextMenuListener(org.eclipse.jface.action.IMenuListener) + */ + public final void removeRulerContextMenuListener(IMenuListener listener) { + fRulerContextMenuListeners.remove(listener); + } + + private boolean isSuspended(IExecutionDMContext targetContext) { + return getRunControl().isSuspended(targetContext); + } + + private IRunControl getRunControl() { + return getService(IRunControl.class); + } + + /*default*/ DsfSession getSession() { + return DsfSession.getSession(fDebugSessionId); + } + + /*default*/ <V> V getService(Class<V> serviceClass) { + if (fServicesTracker != null) { + return fServicesTracker.getService(serviceClass); + } + return null; + } + + /*default*/ IFrameDMContext getTargetFrameContext() { + return fTargetFrameContext; + } + + /** + * Schedule the retrieval of a module time stamp for the given address. + * Should return a <code>Long</code> object in case the value was computed, + * another object to be waited on if the retrieval is in progress, <code>null</code> + * if no time stamp could be retrieved. + * + * @param address + * @return Long, Object or <code>null</code> + */ + synchronized Object retrieveModuleTimestamp(BigInteger address) { + // TLETODO [disassembly] retrieve and cache module time stamp + return null; + } + + private void setFocusPosition(Position pcPos) { + if (fFocusPos != null) { + fDocument.removePosition(fFocusPos); + fFocusPos = null; + } + if (pcPos != null) { + fFocusPos = new Position(pcPos.offset, pcPos.length); + try { + fDocument.addPosition(fFocusPos); + } catch (BadLocationException e) { + internalError(e); + } + } else { + fFocusAddress = PC_UNKNOWN; + } + } + + private void doPendingPCUpdates() { + if (fPendingPCUpdates.isEmpty()) { + return; + } + BigInteger pc; + do { + pc = fPendingPCUpdates.remove(0); + if (pc.compareTo(BigInteger.ZERO) >= 0) { + break; + } + } while (!fPendingPCUpdates.isEmpty()); + gotoFrame(0, pc); + } + + private void addToPCHistory(AddressRangePosition pcPos) { + if (DEBUG) System.out.println("addToPCHistory "+getAddressText(pcPos.fAddressOffset)); //$NON-NLS-1$ + if (fPCHistorySizeMax <= 1) { + return; + } + AddressRangePosition first = null; + if (fPCHistory.size() > 0) { + first = fPCHistory.getFirst(); + if (first.fAddressOffset == pcPos.fAddressOffset) { + if (first.offset != pcPos.offset || first.length != pcPos.length) { + fPCHistory.removeFirst(); + fViewer.invalidateTextPresentation(first.offset, first.length); + } else { + return; + } + } + } + // clone and add + pcPos = new AddressRangePosition(pcPos.offset, pcPos.length, pcPos.fAddressOffset, BigInteger.ZERO); + fPCHistory.addFirst(pcPos); + try { + fDocument.addPosition(pcPos); + } catch (BadLocationException e) { + internalError(e); + } + // limit to max size + if (fPCHistory.size() > fPCHistorySizeMax) { + AddressRangePosition last = fPCHistory.removeLast(); + fDocument.removePosition(last); + fViewer.invalidateTextPresentation(last.offset, last.length); + } + // redraw + for (Iterator<AddressRangePosition> it=fPCHistory.iterator(); it.hasNext();) { + AddressRangePosition pos = it.next(); + fViewer.invalidateTextPresentation(pos.offset, pos.length); + } + } + + /** + * Update current pc. If a pc update is currently under way, adds this + * address to a list of pending pc updates. + * + * @param pc Current pc address. -1 means retrieve pc from top frame, -2 + * means target resumed + */ + private void updatePC(BigInteger pc) { + if (!fPendingPCUpdates.isEmpty()) { + BigInteger last = fPendingPCUpdates.get(fPendingPCUpdates.size()-1); + if (last.compareTo(BigInteger.ZERO) < 0) { + fPendingPCUpdates.remove(fPendingPCUpdates.size()-1); + } + } + fPendingPCUpdates.add(pc); + if (fPendingPCUpdates.size() > fPCHistorySizeMax) { + if (!fActive) { + // if not active, we can savely remove + // the pc updates before the history range + fPendingPCUpdates.remove(0); + } + // we ignore the current goto frame request + // and continue with the pending updates + fGotoFramePending = false; + } + if (fActive) { + if (fGotoFramePending) { + if (!fUpdatePending) { + gotoFrame(0, fFrameAddress); + } + } else { + doPendingPCUpdates(); + } + } + } + + private AddressRangePosition updatePCAnnotation() { + if (fUpdatePending) { + fPCAnnotationUpdatePending = true; + return null; + } + AddressRangePosition pos; + if (fTargetFrame == 0) { + // clear secondary + updateAddressAnnotation(fSecondaryPCAnnotation, PC_UNKNOWN); + // set primary + pos = updateAddressAnnotation(fPCAnnotation, fPCAddress); + } else { + // clear primary + updateAddressAnnotation(fPCAnnotation, PC_UNKNOWN); + // set secondary + pos = updateAddressAnnotation(fSecondaryPCAnnotation, fFrameAddress); + } + fPCAnnotationUpdatePending = pos == null && fFrameAddress.compareTo(BigInteger.ZERO) >= 0; + return pos; + } + + private void scheduleDoPending() { + if (!fUpdatePending && !fDoPendingPosted) { + fDoPendingPosted = true; + invokeLater(new Runnable() { + public void run() { + doPending(); + fDoPendingPosted = false; + } + }); + } + } + + private void doPending() { + if (fViewer == null || fDocument == null) { + return; + } + if (fUpdateSourcePending) { + updateInvalidSource(); + } + boolean sourceValid= fDocument.getInvalidSource().isEmpty(); + if (sourceValid || fShowDisassembly) { + if (fGotoFramePending) { + gotoFrame(fTargetFrame, fFrameAddress); + } + } + if (sourceValid) { + if (fGotoAddressPending != PC_UNKNOWN) { + gotoAddress(fGotoAddressPending); + } else if (fGotoMarkerPending != null) { + gotoMarker(fGotoMarkerPending); + } + if (fPCAnnotationUpdatePending && !fGotoFramePending) { + updatePCAnnotation(); + } + if (fUpdateTitlePending) { + updateTitle(); + } + } + } + + /** + * Safely run given runnable in a state when no update is pending. + * Delays execution by 10 ms if update is currently pending. + * @param doit + */ + private void doScrollLocked(final Runnable doit) { + if (fViewer == null || fDebugSessionId == null) { + // disposed + return; + } + if (!fActive) { + // refresh all when becoming active again + fRefreshViewPending= false; + fRefreshAll = true; + return; + } + if (doit != null) { + fRunnableQueue.add(doit); + } + if (fUpdatePending) { + if (fRunnableQueue.size() == 1) { + Runnable doitlater = new Runnable() { + public void run() { + doScrollLocked(null); + }}; + invokeLater(doitlater); + } + } else { + fUpdatePending = true; + lockScroller(); + try { + ArrayList<Runnable> copy = new ArrayList<Runnable>(fRunnableQueue); + fRunnableQueue.clear(); + for (Iterator<Runnable> iter = copy.iterator(); iter.hasNext();) { + Runnable doitnow = iter.next(); + try { + doitnow.run(); + } catch(Exception e) { + internalError(e); + } + } + } finally { + fUpdatePending = false; + unlockScroller(); + doPending(); + updateVisibleArea(); + } + } + } + + private void lockScroller() { + assert fScrollPos == null; + if (isOpcodeRulerVisible()) { + fRedrawControl = fViewer.getControl(); + } else { + fRedrawControl = fViewer.getTextWidget(); + } + fRedrawControl.setRedraw(false); + try { + int topOffset = fViewer.getTopIndexStartOffset(); + int topIndex = fViewer.getTopIndex(); + int bottomIndex = fViewer.getBottomIndex(); + int bottomOffset = fViewer.getBottomIndexEndOffset(); + int focusLine; + int focusOffset; + if (fFocusPos != null && fFocusPos.isDeleted) { + fFocusPos = null; + } + if (fFocusPos != null && fFocusPos.offset >= topOffset && fFocusPos.offset <= bottomOffset) { + focusOffset = fFocusPos.offset; + focusLine = fDocument.getLineOfOffset(focusOffset); + } else { + focusLine = Math.max(0, (topIndex + bottomIndex) / 2); + focusOffset = fDocument.getLineOffset(focusLine); + AddressRangePosition pos = fDocument.getDisassemblyPosition(focusOffset); + if (pos != null && !pos.fValid) { + // don't lock position of invalid range + focusOffset = pos.offset+pos.length; + focusLine = fDocument.getLineOfOffset(focusOffset); + } + } + fScrollPos = new Position(focusOffset); + fScrollLine = focusLine - topIndex; + fDocument.addPosition(fScrollPos); + } catch (BadLocationException e) { + // should not happen + internalError(e); + } + } + + private void unlockScroller() { + try { + if (fScrollPos == null) { + return; + } + if (fScrollPos.isDeleted) { + fScrollPos.isDeleted = false; + if (fScrollPos.offset >= fDocument.getLength()) { + fScrollPos.offset = 0; + fScrollLine = 0; + } + } + if (fFocusPos != null && (fFocusPos.isDeleted || fFocusPos.length == 0)) { + if (fFocusAddress.compareTo(BigInteger.ZERO) >= 0) { + fGotoAddressPending = fFocusAddress; + setFocusPosition(getPositionOfAddress(fFocusAddress)); + } + } + int topLine = fDocument.getLineOfOffset(fScrollPos.offset) - fScrollLine; + // limit text size + int lineCount = fDocument.getNumberOfLines(); + if (lineCount > fgHighWaterMark*fBufferZone) { + int startLine = Math.max(0, topLine-fgLowWaterMark/2*fBufferZone); + int endLine = Math.min(lineCount-1, topLine+fgLowWaterMark/2*fBufferZone); + fDocument.deleteLineRange(endLine, lineCount-1); + fDocument.deleteLineRange(0, startLine); + } + int lineHeight = fViewer.getTextWidget().getLineHeight(); + int topPixel = topLine * lineHeight; + if (Math.abs(fViewer.getTextWidget().getTopPixel() - topPixel) >= lineHeight) { + fViewer.setTopIndex(topLine); + } + } catch (BadLocationException e) { + // should not happen + internalError(e); + } finally { + if (fScrollPos != null && fDocument != null) { + fDocument.removePosition(fScrollPos); + fScrollPos = null; + } + if (fViewer != null) { + fRedrawControl.setRedraw(true); + getVerticalRuler().update(); + getOverviewRuler().update(); + } + } + } + + private void insertSource(SourcePosition pos) { + if (!fShowSource) { + fDocument.insertSource(pos, "", pos.fLine, true); //$NON-NLS-1$ + return; + } + SourceFileInfo fi = pos.fFileInfo; + BigInteger address = pos.fAddressOffset; + int lineNr = pos.fLine; + if (fi.fError != null) { + // handled below + } else if (fi.fValid) { +// assert fi.fLinesNode.isValid(); + Addr2Line a2l = fi.fAddr2Line[Addr2Line.hash(address, fi.fAddr2Line.length)]; + while (a2l != null && !a2l.addr.equals(address)) + a2l = a2l.next; + if (a2l != null) { + int first = a2l.first; + int line; + for (line = first; line <= a2l.last; ++line) { + if (!fi.fLine2Addr[line].equals(address)) { + if (line > first) { + String source = fi.getLines(first, line-1); + pos = fDocument.insertSource(pos, source, first, false); + } + first = line+1; + } + } + if (line > first) { + String source = fi.getLines(first, line-1); + fDocument.insertSource(pos, source, first, true); + if (source.length() == 0) { + fDocument.removeSourcePosition(pos); + } + } else if (first > a2l.first) { + fDocument.insertSource(pos, "", first, true); //$NON-NLS-1$ + fDocument.removeSourcePosition(pos); + } + } else { + // no source at all + fDocument.insertSource(pos, "", lineNr, true); //$NON-NLS-1$ + fDocument.removeSourcePosition(pos); + } + } else if (fi.fLinesNode == null) { + // TLETODO [disassembly] asynchronous line info + if (fi.fSource != null) { + fi.fError= new Error(); + } + } + if (fi.fError != null && !pos.fValid) { + if (fi.fSource != null) { + if (fi.fSource != null && lineNr >= 0 && lineNr < fi.fSource.getNumberOfLines()) { + fi.fStartAddress = fi.fStartAddress.min(pos.fAddressOffset); + fi.fEndAddress = fi.fEndAddress.max(pos.fAddressOffset.add(pos.fAddressLength)); + if (fi.fLine2Addr[lineNr] == null || fi.fLine2Addr[lineNr].compareTo(BigInteger.ZERO) < 0) { + fi.fLine2Addr[lineNr] = pos.fAddressOffset; + String sourceLine = fi.getLine(lineNr); + fDocument.insertSource(pos, sourceLine, lineNr, true); + } else if (fi.fLine2Addr[lineNr].compareTo(pos.fAddressOffset) > 0) { + SourcePosition oldPos = fDocument.getSourcePosition(fi.fLine2Addr[lineNr]); + if (oldPos != null) { + try { + fDocument.replace(oldPos, oldPos.length, null); + } catch (BadLocationException e) { + internalError(e); + } + fDocument.removeSourcePosition(oldPos); + } + fi.fLine2Addr[lineNr] = pos.fAddressOffset; + String sourceLine = fi.getLine(lineNr); + fDocument.insertSource(pos, sourceLine, lineNr, true); + } else if (fi.fLine2Addr[lineNr].equals(pos.fAddressOffset)) { + String sourceLine = fi.getLine(lineNr); + fDocument.insertSource(pos, sourceLine, lineNr, true); + } else { + fDocument.insertSource(pos, "", lineNr, true); //$NON-NLS-1$ + fDocument.removeSourcePosition(pos); + } + } + } else { + // no source at all + fDocument.insertSource(pos, "", lineNr, true); //$NON-NLS-1$ + fDocument.removeSourcePosition(pos); + } + } + } + + private void updateTitle() { + if (fDebugSessionId == null) { + String descr = DisassemblyMessages.Disassembly_message_notConnected; + String title = getConfigurationElement().getAttribute("name"); //$NON-NLS-1$ + setPartName(title); + setContentDescription(descr); + setTitleToolTip(title); + } else { + // TLETODO Proper content description + setContentDescription(""); //$NON-NLS-1$ + } + } + + private boolean isDissemblyMixedModeOn() { + // TLETODO [disassembly] mixed mode on/off + return true; + } + + /** + * Close this part + */ + protected abstract void closePart(); + + /* + * @see org.eclipse.jface.text.ITextPresentationListener#applyTextPresentation(org.eclipse.jface.text.TextPresentation) + */ + @SuppressWarnings("unchecked") + public void applyTextPresentation(TextPresentation textPresentation) { + IRegion coverage = textPresentation.getExtent(); + if (coverage == null) { + coverage= new Region(0, fDocument.getLength()); + } + int startOffset = coverage.getOffset(); + int length = coverage.getLength(); + int endOffset = startOffset + length; + Iterator<Position> it; + try { + // make sure we start with first overlapping position + AddressRangePosition pos = fDocument.getModelPosition(startOffset); + assert pos != null; + if (pos == null) { + return; + } + it = fDocument.getPositionIterator(DisassemblyDocument.CATEGORY_MODEL, pos.offset); + } catch (BadPositionCategoryException e) { + return; + } catch (BadLocationException e) { + return; + } + ArrayList<StyleRange> styleRanges = new ArrayList<StyleRange>(); + while(it.hasNext()) { + AddressRangePosition pos = (AddressRangePosition)it.next(); + if (pos.offset >= endOffset) { + break; + } + if (pos.offset+pos.length <= startOffset) { + continue; + } + if (pos.fValid && pos.length > 0) { + if (pos instanceof DisassemblyPosition) { + DisassemblyPosition disPos = (DisassemblyPosition)pos; + styleRanges.add(new StyleRange(pos.offset, disPos.length, fInstructionColor, null, SWT.NULL)); + } else if (pos instanceof ErrorPosition) { + styleRanges.add(new StyleRange(pos.offset, pos.length, fErrorColor, null, SWT.NULL)); + } else if (pos instanceof LabelPosition) { + styleRanges.add(new StyleRange(pos.offset, pos.length, fLabelColor, null, SWT.BOLD)); + } else if (pos instanceof SourcePosition) { + SourcePosition srcPos = (SourcePosition)pos; + TextPresentation presentation = null; + if (srcPos.fFileInfo.fSource != null) { + presentation = srcPos.fFileInfo.getPresentation(srcPos.fFileInfo.getRegion(srcPos.fLine, pos.length)); + } + if (presentation != null) { + // clip result window to coverage + int start = Math.max(startOffset, srcPos.offset); + int end = Math.min(endOffset, srcPos.offset + srcPos.length); + int srcOffset = srcPos.fFileInfo.getLineOffset(srcPos.fLine); + int clipOffset = start - srcPos.offset; + presentation.setResultWindow(new Region(srcOffset + clipOffset, end-start)); + for (Iterator<StyleRange> iter = presentation.getNonDefaultStyleRangeIterator(); iter.hasNext();) { + StyleRange styleRange = iter.next(); + styleRange.start += srcPos.offset + clipOffset; + styleRanges.add(styleRange); + } + } else { + styleRanges.add(new StyleRange(pos.offset, pos.length, fSourceColor, null, SWT.NULL)); + } + } + } + } + if (styleRanges.size() > 0) { + for (Iterator<StyleRange> iter = styleRanges.iterator(); iter.hasNext();) { + textPresentation.addStyleRange(iter.next()); + } + } + // update pc history trail + if (fPCHistory.size() > 1) { + HSL hsv = new HSL(fPCAnnotationRGB); + double luminanceStep = (1-hsv.luminance)/(fPCHistorySizeMax+1); + hsv.luminance = 1 - luminanceStep * (fPCHistorySizeMax - fPCHistory.size()); + for (ListIterator<AddressRangePosition> listIt = fPCHistory.listIterator(fPCHistory.size()); listIt.hasPrevious();) { + AddressRangePosition pcPos = listIt.previous(); + hsv.luminance -= luminanceStep; + if (pcPos.isDeleted) { + listIt.remove(); + continue; + } + if (!pcPos.fValid) { + continue; + } + if (pcPos.overlapsWith(startOffset, length)) { + RGB rgb = hsv.toRGB(); + Color pcColor = getSharedColors().getColor(rgb); + Color textColor = null; + // experimental: if color is dark, use white (background) as text color +// Color textColor = hsv.luminance < 0.7 ? fViewer.getTextWidget().getBackground() : null; + textPresentation.mergeStyleRange(new StyleRange(pcPos.offset, pcPos.length, textColor, pcColor)); + } + } + } + } + + + private IBreakpoint insertBreakpoint(int line, boolean edit) throws CoreException { + SourcePosition srcPos = null; + try { + int lineOffset = fDocument.getLineOffset(line); + srcPos = fDocument.getSourcePosition(lineOffset); + } catch (BadLocationException e) { + // should not happen, but its safe to ignore anyway + } + boolean lineBreakpoint = srcPos != null && srcPos.length > 0; + + IResource resource; + ICBreakpoint bp; + + if (lineBreakpoint) { + SourceFileInfo srcInfo = srcPos.fFileInfo; + String filePath = null; + resource = (IResource)srcInfo.fFile.getAdapter(IResource.class); + if (resource != null) { + final IPath location= resource.getLocation(); + if (location == null) { + return null; + } + filePath = location.toOSString(); + } else { + resource = ResourcesPlugin.getWorkspace().getRoot(); + filePath = srcInfo.fFile.getFullPath().toOSString(); + } + BigInteger address = srcPos.fAddressOffset; + AddressRangePosition pos = fDocument.getDisassemblyPosition(address); + int srcLine = -1; + if (pos instanceof DisassemblyPosition) { + srcLine = ((DisassemblyPosition)pos).getLine(); + } + bp= CDIDebugModel.createLineBreakpoint(filePath, resource, srcLine + 1, true, 0, "", true); //$NON-NLS-1$ + } else { + resource = ResourcesPlugin.getWorkspace().getRoot(); + BigInteger address = getAddressOfLine(line); + bp= CDIDebugModel.createAddressBreakpoint(null, null, resource, new Addr64(address), true, 0, "", true); //$NON-NLS-1$ + } + + return bp; + } + + private AddressRangePosition insertSource(AddressRangePosition pos, BigInteger address, final String file, int lineNr) { + Object sourceElement = null; + if (fFile2Storage.containsKey(file)) { + sourceElement = fFile2Storage.get(file); + } else { + final ISourceLookup lookup= getService(ISourceLookup.class); + final ISourceLookupDMContext ctx= DMContexts.getAncestorOfType(fTargetContext, ISourceLookupDMContext.class); + final DsfExecutor executor= getSession().getExecutor(); + Query<Object> query= new Query<Object>() { + @Override + protected void execute(final DataRequestMonitor<Object> rm) { + final DataRequestMonitor<Object> request= new DataRequestMonitor<Object>(executor, rm) { + @Override + protected void handleSuccess() { + rm.setData(getData()); + rm.done(); + } + }; + lookup.getSource(ctx, file, request); + } + }; + try { + getSession().getExecutor().execute(query); + sourceElement= query.get(); + } catch (InterruptedException exc) { + internalError(exc); + } catch (ExecutionException exc) { + internalError(exc); + } + if (sourceElement instanceof File) { + sourceElement = new LocalFileStorage((File)sourceElement); + } + if (sourceElement instanceof IStorage) { + fFile2Storage.put(file, sourceElement); + } else { + fFile2Storage.put(file, null); + logWarning(DisassemblyMessages.Disassembly_log_error_locateFile+file, null); + } + } + if (sourceElement instanceof IStorage) { + SourceFileInfo fi = fDocument.getSourceInfo((IStorage)sourceElement); + if (fi == null) { + IStorage storage = (IStorage)sourceElement; + Display display = getSite().getShell().getDisplay(); + Runnable done = new SourceColorerJob(display, storage, this); + fi = fDocument.createSourceInfo(file, storage, done); + EditionFinderJob editionJob = null; + if (storage instanceof IFile) { + editionJob = new EditionFinderJob(fi, address, this); + editionJob.schedule(); + } + fi.fReadingJob.schedule(); + } + pos = fDocument.insertInvalidSource(pos, address, fi, lineNr); + } + return pos; + } + + private void disassemblyModeChanged(boolean isDisassemblyOn) { + if (fShowDisassembly == isDisassemblyOn) { + return; + } + if (fShowDisassembly && !fSourceOnlyMode) { + // if not in source-only mode, do not update if disassembly mode is disabled + return; + } + fShowDisassembly = isDisassemblyOn; + if (!fShowDisassembly) { + sourceModeChanged(true); + } + fActionToggleSource.update(); + Runnable doit = new Runnable() { + public void run() { + fDocument.invalidateDisassemblyWithSource(!fShowDisassembly); + fGotoFramePending = true; + }}; + doScrollLocked(doit); + } + + /** + * Turn on/off source mode. + * @param isSourceModeOn + */ + private void sourceModeChanged(boolean isSourceModeOn) { + if (fShowSource == isSourceModeOn) { + return; + } + fShowSource = isSourceModeOn; + fActionToggleSource.update(); + fDocument.invalidateSource(); + if (!fShowSource && !fShowDisassembly) { + disassemblyModeChanged(true); + } else { + fPCAnnotationUpdatePending = true; + updateInvalidSource(); + } + } + + public static BigInteger decodeAddress(String string) { + if (string.startsWith("0x")) { //$NON-NLS-1$ + return new BigInteger(string.substring(2), 16); + } + return new BigInteger(string); + } + + private static String getAddressText(BigInteger address) { + if (address == null) { + return "<null>"; //$NON-NLS-1$ + } + if (address.compareTo(BigInteger.ZERO) < 0) { + return address.toString(); + } + String hex = address.toString(16); + return "0x" + "0000000000000000".substring(hex.length() + (address.bitLength() <= 32 ? 8 : 0)) + hex; //$NON-NLS-1$ //$NON-NLS-2$ + } + + static void internalError(Throwable e) { + if (DEBUG) { + System.err.println("Disassembly: Internal error"); //$NON-NLS-1$ + e.printStackTrace(); + } + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyRulerColumn.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyRulerColumn.java new file mode 100644 index 00000000000..83015f86bef --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyRulerColumn.java @@ -0,0 +1,985 @@ +/******************************************************************************* + * 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 + * Anton Leherbauer (Wind River Systems) + *******************************************************************************/ + +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly; + +import java.util.Arrays; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITextListener; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.ITextViewerExtension; +import org.eclipse.jface.text.ITextViewerExtension5; +import org.eclipse.jface.text.IViewportListener; +import org.eclipse.jface.text.TextEvent; +import org.eclipse.jface.text.source.CompositeRuler; +import org.eclipse.jface.text.source.IAnnotationModel; +import org.eclipse.jface.text.source.IVerticalRulerColumn; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StyleRange; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.events.MouseMoveListener; +import org.eclipse.swt.events.MouseTrackListener; +import org.eclipse.swt.events.PaintEvent; +import org.eclipse.swt.events.PaintListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontMetrics; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Canvas; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; + +/** + * Vertical ruler column for use with disassembly parts. + * <p> + * Derived from {@link org.eclipse.jface.text.source.LineNumberRulerColumn}. + * </p> + */ +public class DisassemblyRulerColumn implements IVerticalRulerColumn { + protected final static String DOTS = "......................................................................"; //$NON-NLS-1$ + protected final static String SPACES = " "; //$NON-NLS-1$ + + /** + * Internal listener class. + */ + class InternalListener implements IViewportListener, ITextListener, ISelectionChangedListener { + + /* + * @see IViewportListener#viewportChanged(int) + */ + public void viewportChanged(int verticalPosition) { + if (verticalPosition != fScrollPos) + redraw(); + } + + /* + * @see ITextListener#textChanged(TextEvent) + */ + public void textChanged(TextEvent event) { + + if (updateNumberOfDigits()) { + computeIndentations(); + layout(event.getViewerRedrawState()); + return; + } + + if (!event.getViewerRedrawState()) + return; + + if (fSensitiveToTextChanges || event.getDocumentEvent() == null) + postRedraw(); + + } + + /* + * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent) + */ + public void selectionChanged(SelectionChangedEvent event) { + postRedraw(); + } + } + + /** + * Handles all the mouse interaction in this line number ruler column. + */ + class MouseHandler implements MouseListener, MouseMoveListener, MouseTrackListener { + + /** The cached view port size */ + private int fCachedViewportSize; + /** The area of the line at which line selection started */ + private IRegion fStartLine; + /** The number of the line at which line selection started */ + private int fStartLineNumber; + /** The auto scroll direction */ + private int fAutoScrollDirection; + + /* + * @see org.eclipse.swt.events.MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent) + */ + public void mouseUp(MouseEvent event) { + // see bug 45700 + if (event.button == 1) { + stopSelecting(); + stopAutoScroll(); + postRedraw(); + } + } + + /* + * @see org.eclipse.swt.events.MouseListener#mouseDown(org.eclipse.swt.events.MouseEvent) + */ + public void mouseDown(MouseEvent event) { + fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y); + // see bug 45700 + if (event.button == 1) { + startSelecting(); + } + } + + /* + * @see org.eclipse.swt.events.MouseListener#mouseDoubleClick(org.eclipse.swt.events.MouseEvent) + */ + public void mouseDoubleClick(MouseEvent event) { + fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y); + stopSelecting(); + stopAutoScroll(); + } + + /* + * @see org.eclipse.swt.events.MouseMoveListener#mouseMove(org.eclipse.swt.events.MouseEvent) + */ + public void mouseMove(MouseEvent event) { + if (!autoScroll(event)) { + int newLine = fParentRuler.toDocumentLineNumber(event.y); + expandSelection(newLine); + } + } + + /* + * @see org.eclipse.swt.events.MouseTrackListener#mouseEnter(org.eclipse.swt.events.MouseEvent) + */ + public void mouseEnter(MouseEvent event) { + } + + /* + * @see org.eclipse.swt.events.MouseTrackListener#mouseExit(org.eclipse.swt.events.MouseEvent) + */ + public void mouseExit(MouseEvent event) { + } + + /* + * @see org.eclipse.swt.events.MouseTrackListener#mouseHover(org.eclipse.swt.events.MouseEvent) + */ + public void mouseHover(MouseEvent event) { + } + + /** + * Called when line drag selection started. Adds mouse move and track + * listeners to this column's control. + */ + private void startSelecting() { + try { + + // select line + IDocument document = fCachedTextViewer.getDocument(); + fStartLineNumber = fParentRuler.getLineOfLastMouseButtonActivity(); + fStartLine = document.getLineInformation(fStartLineNumber); + fCachedTextViewer.setSelectedRange(fStartLine.getOffset(), fStartLine.getLength()); + fCachedViewportSize = getVisibleLinesInViewport(); + + // prepare for drag selection + fCanvas.addMouseMoveListener(this); + fCanvas.addMouseTrackListener(this); + + } catch (BadLocationException x) { + } + } + + /** + * Called when line drag selection stopped. Removes all previously + * installed listeners from this column's control. + */ + private void stopSelecting() { + // drag selection stopped + fCanvas.removeMouseMoveListener(this); + fCanvas.removeMouseTrackListener(this); + } + + /** + * Expands the line selection from the remembered start line to the + * given line. + * + * @param lineNumber + * the line to which to expand the selection + */ + private void expandSelection(int lineNumber) { + try { + + IDocument document = fCachedTextViewer.getDocument(); + IRegion lineInfo = document.getLineInformation(lineNumber); + + int start = Math.min(fStartLine.getOffset(), lineInfo.getOffset()); + int end = Math.max(fStartLine.getOffset() + fStartLine.getLength(), lineInfo.getOffset() + + lineInfo.getLength()); + + if (lineNumber < fStartLineNumber) + fCachedTextViewer.setSelectedRange(end, start - end); + else + fCachedTextViewer.setSelectedRange(start, end - start); + + } catch (BadLocationException x) { + } + } + + /** + * Called when auto scrolling stopped. Clears the auto scroll direction. + */ + private void stopAutoScroll() { + fAutoScrollDirection = SWT.NULL; + } + + /** + * Called on drag selection. + * + * @param event + * the mouse event caught by the mouse move listener + * @return <code>true</code> if scrolling happened, <code>false</code> + * otherwise + */ + private boolean autoScroll(MouseEvent event) { + Rectangle area = fCanvas.getClientArea(); + + if (event.y > area.height) { + autoScroll(SWT.DOWN); + return true; + } + + if (event.y < 0) { + autoScroll(SWT.UP); + return true; + } + + stopAutoScroll(); + return false; + } + + /** + * Scrolls the viewer into the given direction. + * + * @param direction + * the scroll direction + */ + private void autoScroll(int direction) { + + if (fAutoScrollDirection == direction) + return; + + final int TIMER_INTERVAL = 5; + final Display display = fCanvas.getDisplay(); + Runnable timer = null; + switch (direction) { + case SWT.UP: + timer = new Runnable() { + public void run() { + if (fAutoScrollDirection == SWT.UP) { + int top = getInclusiveTopIndex(); + if (top > 0) { + fCachedTextViewer.setTopIndex(top - 1); + expandSelection(top - 1); + display.timerExec(TIMER_INTERVAL, this); + } + } + } + }; + break; + case SWT.DOWN: + timer = new Runnable() { + public void run() { + if (fAutoScrollDirection == SWT.DOWN) { + int top = getInclusiveTopIndex(); + fCachedTextViewer.setTopIndex(top + 1); + expandSelection(top + 1 + fCachedViewportSize); + display.timerExec(TIMER_INTERVAL, this); + } + } + }; + break; + } + + if (timer != null) { + fAutoScrollDirection = direction; + display.timerExec(TIMER_INTERVAL, timer); + } + } + + /** + * Returns the viewer's first visible line, even if only partially + * visible. + * + * @return the viewer's first visible line + */ + private int getInclusiveTopIndex() { + if (fCachedTextWidget != null && !fCachedTextWidget.isDisposed()) { + int top = fCachedTextViewer.getTopIndex(); + if ((fCachedTextWidget.getTopPixel() % fCachedTextWidget.getLineHeight()) != 0) + --top; + return top; + } + return -1; + } + } + + /** This column's parent ruler */ + private CompositeRuler fParentRuler; + /** Cached text viewer */ + private ITextViewer fCachedTextViewer; + /** Cached text widget */ + private StyledText fCachedTextWidget; + /** The columns canvas */ + private Canvas fCanvas; + /** Cache for the actual scroll position in pixels */ + private int fScrollPos; + /** The drawable for double buffering */ + private Image fBuffer; + /** The internal listener */ + private InternalListener fInternalListener = new InternalListener(); + /** The font of this column */ + private Font fFont; + /** The indentation cache */ + private int[] fIndentation; + /** Indicates whether this column reacts on text change events */ + private boolean fSensitiveToTextChanges = false; + /** The foreground color */ + private Color fForeground; + /** The background color */ + private Color fBackground; + /** Cached number of displayed digits */ + private int fCachedNumberOfDigits = -1; + /** Flag indicating whether a relayout is required */ + private boolean fRelayoutRequired = false; + /** + * Redraw runnable lock + */ + private Object fRunnableLock = new Object(); + /** + * Redraw runnable state + */ + private boolean fIsRunnablePosted = false; + /** + * Redraw runnable + */ + private Runnable fRunnable = new Runnable() { + public void run() { + synchronized (fRunnableLock) { + fIsRunnablePosted = false; + } + redraw(); + } + }; + private boolean fAlignRight; + private boolean fPaintStyleBackground; + private boolean fPaintSelectionBackground; + + /** + * Constructs a new vertical ruler column. + * + */ + public DisassemblyRulerColumn() { + this(SWT.LEFT); + // default constructor + } + + public DisassemblyRulerColumn(int align) { + this(align, true, false); + } + + public DisassemblyRulerColumn(int align, boolean paintSelection, boolean paintStyle) { + fAlignRight = (align & SWT.RIGHT) != 0; + fPaintSelectionBackground = paintSelection; + fPaintStyleBackground = paintStyle; + } + + /** + * Sets the foreground color of this column. + * + * @param foreground + * the foreground color + */ + public void setForeground(Color foreground) { + fForeground = foreground; + } + + /** + * Returns the foreground color being used to print the line numbers. + * + * @return the configured foreground color + */ + protected Color getForeground() { + return fForeground; + } + + /** + * Sets the background color of this column. + * + * @param background + * the background color + */ + public void setBackground(Color background) { + fBackground = background; + if (fCanvas != null && !fCanvas.isDisposed()) + fCanvas.setBackground(getBackground(fCanvas.getDisplay())); + } + + /** + * Returns the System background color for list widgets. + * + * @param display + * the display + * @return the System background color for list widgets + */ + protected Color getBackground(Display display) { + if (fBackground == null) + return display.getSystemColor(SWT.COLOR_LIST_BACKGROUND); + return fBackground; + } + + /* + * @see IVerticalRulerColumn#getControl() + */ + public Control getControl() { + return fCanvas; + } + + /* + * @see IVerticalRuleColumnr#getWidth + */ + public int getWidth() { + return fIndentation[0]; + } + + /** + * Computes the number of digits to be displayed. Returns <code>true</code> + * if the number of digits changed compared to the previous call of this + * method. If the method is called for the first time, the return value is + * also <code>true</code>. + * + * @return whether the number of digits has been changed + */ + protected boolean updateNumberOfDigits() { + if (fCachedTextViewer == null) + return false; + + int digits = computeNumberOfCharacters(); + + if (fCachedNumberOfDigits != digits) { + fCachedNumberOfDigits = digits; + return true; + } + + return false; + } + + /** + * Does the real computation of the number of characters. The default + * implementation computes the number of digits for the line number. + * Subclasses may override this method if they need extra space on the ruler. + * + * @return the number of characters to be displayed on the ruler. + */ + protected int computeNumberOfCharacters() { + IDocument document = fCachedTextViewer.getDocument(); + int lines= document == null ? 0 : document.getNumberOfLines(); + + int digits= 2; + while (lines > Math.pow(10, digits) - 1) { + ++digits; + } + return digits; + } + + /** + * Layouts the enclosing viewer to adapt the layout to changes of the size + * of the individual components. + * + * @param redraw + * <code>true</code> if this column can be redrawn + */ + protected void layout(boolean redraw) { + if (!redraw) { + fRelayoutRequired= true; + return; + } + + fRelayoutRequired= false; + if (fCachedTextViewer instanceof ITextViewerExtension) { + ITextViewerExtension extension= (ITextViewerExtension) fCachedTextViewer; + Control control= extension.getControl(); + if (control instanceof Composite && !control.isDisposed()) { + Composite composite= (Composite) control; + composite.layout(true); + } + } + } + + /** + * Computes the indentations for the given font and stores them in + * <code>fIndentation</code>. + */ + protected void computeIndentations() { + if (fCanvas == null) + return; + + GC gc= new GC(fCanvas); + try { + + gc.setFont(fCanvas.getFont()); + + fIndentation= new int[fCachedNumberOfDigits + 1]; + char[] digitStr= new char[fCachedNumberOfDigits + 1]; + Arrays.fill(digitStr, '9'); + Point p= gc.stringExtent(new String(digitStr, 0, fCachedNumberOfDigits + 1)); + fIndentation[0]= p.x; + + for (int i= 1; i <= fCachedNumberOfDigits; i++) { + p= gc.stringExtent(new String(digitStr, 0, i)); + fIndentation[i]= fIndentation[0] - p.x; + } + + } finally { + gc.dispose(); + } + } + + /* + * @see IVerticalRulerColumn#createControl(CompositeRuler, Composite) + */ + public Control createControl(CompositeRuler parentRuler, Composite parentControl) { + + fParentRuler= parentRuler; + fCachedTextViewer= parentRuler.getTextViewer(); + fCachedTextWidget= fCachedTextViewer.getTextWidget(); + + fCanvas= new Canvas(parentControl, SWT.NONE); + fCanvas.setBackground(getBackground(fCanvas.getDisplay())); + fCanvas.setForeground(fForeground); + + fCanvas.addPaintListener(new PaintListener() { + public void paintControl(PaintEvent event) { + if (fCachedTextViewer != null) + doubleBufferPaint(event.gc); + } + }); + + fCanvas.addDisposeListener(new DisposeListener() { + public void widgetDisposed(DisposeEvent e) { + handleDispose(); + fCachedTextViewer= null; + fCachedTextWidget= null; + } + }); + + fCanvas.addMouseListener(new MouseHandler()); + + if (fCachedTextViewer != null) { + + fCachedTextViewer.addViewportListener(fInternalListener); + fCachedTextViewer.addTextListener(fInternalListener); + fCachedTextViewer.getSelectionProvider().addSelectionChangedListener(fInternalListener); + + if (fFont == null) { + if (fCachedTextWidget != null && !fCachedTextWidget.isDisposed()) + fFont= fCachedTextWidget.getFont(); + } + } + + if (fFont != null) + fCanvas.setFont(fFont); + + updateNumberOfDigits(); + computeIndentations(); + return fCanvas; + } + + /** + * Disposes the column's resources. + */ + protected void handleDispose() { + + if (fCachedTextViewer != null) { + fCachedTextViewer.removeViewportListener(fInternalListener); + fCachedTextViewer.removeTextListener(fInternalListener); + fCachedTextViewer.getSelectionProvider().removeSelectionChangedListener(fInternalListener); + } + + if (fBuffer != null) { + fBuffer.dispose(); + fBuffer= null; + } + } + + /** + * Double buffer drawing. + * + * @param dest + * the gc to draw into + */ + private void doubleBufferPaint(GC dest) { + + Point size= fCanvas.getSize(); + + if (size.x <= 0 || size.y <= 0) + return; + + if (fBuffer != null) { + Rectangle r= fBuffer.getBounds(); + if (r.width != size.x || r.height != size.y) { + fBuffer.dispose(); + fBuffer= null; + } + } + if (fBuffer == null) + fBuffer= new Image(fCanvas.getDisplay(), size.x, size.y); + + GC gc= new GC(fBuffer); + gc.setFont(fCanvas.getFont()); + if (fForeground != null) + gc.setForeground(fForeground); + + try { + gc.setBackground(getBackground(fCanvas.getDisplay())); + gc.fillRectangle(0, 0, size.x, size.y); + + if (fCachedTextViewer instanceof ITextViewerExtension5) + doPaint1(gc); + else + doPaint(gc); + + } finally { + gc.dispose(); + } + + dest.drawImage(fBuffer, 0, 0); + } + + /** + * Returns the viewport height in lines. + * + * @return the viewport height in lines + */ + protected int getVisibleLinesInViewport() { + Rectangle clArea= fCachedTextWidget.getClientArea(); + return clArea.height / fCachedTextWidget.getLineHeight(); + } + + /** + * Draws the ruler column. + * + * @param gc + * the gc to draw into + */ + private void doPaint(GC gc) { + + if (fCachedTextViewer == null) + return; + + if (fCachedTextWidget == null) + return; + + int firstLine= 0; + + int topLine= fCachedTextWidget.getTopIndex(); + fScrollPos= fCachedTextWidget.getTopPixel(); + int lineheight= fCachedTextWidget.getLineHeight(); + int partialLineHidden= fScrollPos % lineheight; + + if (partialLineHidden > 0 && topLine > 0) // widgetTopLine shows the + // first fully visible line + --topLine; + + int bottomLine; + + try { + + IRegion region= fCachedTextViewer.getVisibleRegion(); + IDocument doc= fCachedTextViewer.getDocument(); + + if (doc == null) + return; + + firstLine= doc.getLineOfOffset(region.getOffset()); + if (firstLine > topLine) + topLine= firstLine; + + bottomLine= doc.getLineOfOffset(region.getOffset() + region.getLength()); + + } catch (BadLocationException x) { + return; + } + + fSensitiveToTextChanges= bottomLine - topLine < getVisibleLinesInViewport(); + + int baselineBias= getBaselineBias(gc); + + int topInset= fCachedTextViewer.getTopInset(); + int y= topInset - partialLineHidden; + Point canvasSize= fCanvas.getSize(); + Point selection= fCachedTextWidget.getSelection(); + boolean selectedLine= false; + Color defaultForeground= gc.getForeground(); + Color defaultBackground= gc.getBackground(); + + for (int line= topLine; y < canvasSize.y && line <= bottomLine; line++, y += lineheight) { + int widgetOffset= fCachedTextWidget.getOffsetAtLine(line); + if (fPaintSelectionBackground && widgetOffset >= selection.x && widgetOffset < selection.y) { + if (!selectedLine) { + selectedLine= true; + gc.setForeground(fCachedTextWidget.getSelectionForeground()); + gc.setBackground(fCachedTextWidget.getSelectionBackground()); + } + } else if (selectedLine) { + selectedLine= false; + gc.setForeground(defaultForeground); + gc.setBackground(defaultBackground); + } + if (selectedLine) { + gc.fillRectangle(0, y, canvasSize.x, lineheight); + } else if (fPaintStyleBackground && widgetOffset >= 0 && widgetOffset < fCachedTextWidget.getCharCount()) { + StyleRange style= fCachedTextWidget.getStyleRangeAtOffset(widgetOffset); + if (style != null && style.background != null) { + gc.setBackground(style.background); + gc.fillRectangle(0, y + baselineBias, canvasSize.x, lineheight - baselineBias); + gc.setBackground(defaultBackground); + } + } + paintLine(line, y, lineheight, gc, fCachedTextWidget.getDisplay()); + String s= createDisplayString(line); + int indentation= fAlignRight ? fIndentation[s.length()] : 0; + gc.drawString(s, indentation, y + baselineBias, true); + } + } + + /** + * Computes the string to be printed for <code>line</code>. The default + * implementation returns <code>Integer.toString(line + 1)</code>. + * + * @param line + * the line number for which the string is generated + * @return the string to be printed on the ruler column for <code>line</code> + */ + String createDisplayString(int line) { + return Integer.toString(line + 1); + } + + /** + * Draws the ruler column. Uses <code>ITextViewerExtension5</code> for the + * implementation. Will replace <code>doPinat(GC)</code>. + * + * @param gc + * the gc to draw into + */ + private void doPaint1(GC gc) { + + if (fCachedTextViewer == null) + return; + + ITextViewerExtension5 extension= (ITextViewerExtension5) fCachedTextViewer; + + int widgetTopLine= fCachedTextWidget.getTopIndex(); + fScrollPos= fCachedTextWidget.getTopPixel(); + int lineheight= fCachedTextWidget.getLineHeight(); + int partialLineHidden= fScrollPos % lineheight; + + if (partialLineHidden > 0 && widgetTopLine > 0) // widgetTopLine shows + // the first fully + // visible line + --widgetTopLine; + + int modelTopLine= extension.widgetLine2ModelLine(widgetTopLine); + int modelBottomLine= fCachedTextViewer.getBottomIndex(); + if (modelBottomLine >= 0) + ++modelBottomLine; + + try { + + IRegion region= extension.getModelCoverage(); + IDocument doc= fCachedTextViewer.getDocument(); + + if (doc == null) + return; + + int coverageTopLine= doc.getLineOfOffset(region.getOffset()); + if (coverageTopLine > modelTopLine || modelTopLine == -1) + modelTopLine= coverageTopLine; + + int coverageBottomLine= doc.getLineOfOffset(region.getOffset() + region.getLength()); + if (coverageBottomLine < modelBottomLine || modelBottomLine == -1) + modelBottomLine= coverageBottomLine; + + } catch (BadLocationException x) { + return; + } + + fSensitiveToTextChanges= modelBottomLine - modelTopLine < getVisibleLinesInViewport(); + + int baselineBias= getBaselineBias(gc); + + int topInset= fCachedTextViewer.getTopInset(); + int y= topInset - partialLineHidden; + Point canvasSize= fCanvas.getSize(); + Point selection= fCachedTextWidget.getSelection(); + boolean selectedLine= false; + Color defaultForeground= gc.getForeground(); + Color defaultBackground= gc.getBackground(); + + for (int modelLine= modelTopLine; y < canvasSize.y && modelLine <= modelBottomLine; modelLine++) { + + // don't draw hidden (e.g. folded) lines + int widgetLine= extension.modelLine2WidgetLine(modelLine); + if (widgetLine == -1) + continue; + int widgetOffset= fCachedTextWidget.getOffsetAtLine(widgetLine); + if (fPaintSelectionBackground && widgetOffset >= selection.x && widgetOffset < selection.y) { + if (!selectedLine) { + selectedLine= true; + gc.setForeground(fCachedTextWidget.getSelectionForeground()); + gc.setBackground(fCachedTextWidget.getSelectionBackground()); + } + } else if (selectedLine) { + selectedLine= false; + gc.setForeground(defaultForeground); + gc.setBackground(defaultBackground); + } + if (selectedLine) { + gc.fillRectangle(0, y, canvasSize.x, lineheight); + } else if (fPaintStyleBackground && widgetOffset >= 0 && widgetOffset < fCachedTextWidget.getCharCount()) { + StyleRange style= fCachedTextWidget.getStyleRangeAtOffset(widgetOffset); + if (style != null && style.background != null) { + gc.setBackground(style.background); + gc.fillRectangle(0, y + baselineBias, canvasSize.x, lineheight - baselineBias); + gc.setBackground(defaultBackground); + } + } + + paintLine(modelLine, y, lineheight, gc, fCachedTextWidget.getDisplay()); + + String s= createDisplayString(modelLine); + int indentation= fAlignRight ? fIndentation[s.length()] : 0; + gc.drawString(s, indentation, y + baselineBias, true); + y += lineheight; + } + } + + /** + * Returns the difference between the baseline of the widget and the + * baseline as specified by the font for <code>gc</code>. When drawing + * text, the returned bias should be added to obtain text line up on + * the correct base line of the text widget. + * + * @param gc + * the <code>GC</code> to get the font metrics from + * @return the baseline bias to use when drawing text that is line up with + * <code>fCachedTextWidget</code> + */ + private int getBaselineBias(GC gc) { + /* + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=62951 widget line + * height may be more than the font height used for the text, + * since font styles (bold, italics...) can have larger font metrics + * than the simple font used for the numbers. + */ + int widgetBaseline= fCachedTextWidget.getBaseline(); + FontMetrics fm= gc.getFontMetrics(); + int fontBaseline= fm.getAscent() + fm.getLeading(); + Assert.isTrue(widgetBaseline >= fontBaseline); + int baselineBias= widgetBaseline - fontBaseline; + return baselineBias; + } + + /** + * Paints the line. After this method is called the text is painted + * on top of the result of this method. + * <p> + * This default implementation does nothing. + * </p> + * + * @param line + * the line of the document which the ruler is painted for + * @param y + * the y-coordinate of the box being painted for + * <code>line</code>, relative to <code>gc</code> + * @param lineheight + * the height of one line (and therefore of the box being + * painted) + * @param gc + * the drawing context the client may choose to draw on. + * @param display + * the display the drawing occurs on + */ + protected void paintLine(int line, int y, int lineheight, GC gc, Display display) { + } + + /** + * Triggers a redraw in the display thread. + */ + protected final void postRedraw() { + if (fCanvas != null && !fCanvas.isDisposed()) { + Display d= fCanvas.getDisplay(); + if (d != null) { + synchronized (fRunnableLock) { + if (fIsRunnablePosted) + return; + fIsRunnablePosted= true; + } + d.asyncExec(fRunnable); + } + } + } + + /* + * @see IVerticalRulerColumn#redraw() + */ + public void redraw() { + + if (fRelayoutRequired) { + layout(true); + return; + } + + if (fCanvas != null && !fCanvas.isDisposed()) { + GC gc= new GC(fCanvas); + doubleBufferPaint(gc); + gc.dispose(); + } + } + + /* + * @see IVerticalRulerColumn#setModel(IAnnotationModel) + */ + public void setModel(IAnnotationModel model) { + } + + /* + * @see IVerticalRulerColumn#setFont(Font) + */ + public void setFont(Font font) { + fFont= font; + if (fCanvas != null && !fCanvas.isDisposed()) { + fCanvas.setFont(fFont); + updateNumberOfDigits(); + computeIndentations(); + } + } + + /** + * Returns the parent (composite) ruler of this ruler column. + * + * @return the parent ruler + */ + protected CompositeRuler getParentRuler() { + return fParentRuler; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyTextHover.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyTextHover.java new file mode 100644 index 00000000000..4d6fc9eac78 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyTextHover.java @@ -0,0 +1,149 @@ +/******************************************************************************* + * 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.internal.ui.disassembly; + +import java.util.concurrent.ExecutionException; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.Query; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.AddressRangePosition; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.DisassemblyDocument; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.DisassemblyPosition; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.LabelPosition; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.SourcePosition; +import org.eclipse.cdt.dsf.debug.service.IExpressions; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues; +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; +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.IStack.IFrameDMContext; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.cdt.internal.ui.text.CWordFinder; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITextHover; +import org.eclipse.jface.text.ITextViewer; + +/** + * A text hover to evaluate registers and variables under the cursor. + */ +@SuppressWarnings("restriction") +public class DisassemblyTextHover implements ITextHover { + + private final DisassemblyPart fDisassemblyPart; + + /** + * Create a new disassembly text hover. + */ + public DisassemblyTextHover(DisassemblyPart part) { + fDisassemblyPart= part; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.ITextHover#getHoverRegion(org.eclipse.jface.text.ITextViewer, int) + */ + public IRegion getHoverRegion(ITextViewer textViewer, int offset) { + IDocument doc = textViewer.getDocument(); + return CWordFinder.findWord(doc, offset); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.ITextHover#getHoverInfo(org.eclipse.jface.text.ITextViewer, org.eclipse.jface.text.IRegion) + */ + public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) { + DisassemblyDocument doc = (DisassemblyDocument)textViewer.getDocument(); + int offset = hoverRegion.getOffset(); + AddressRangePosition pos; + try { + String ident = doc.get(offset, hoverRegion.getLength()); + String value = null; + pos = doc.getModelPosition(offset); + if (pos instanceof SourcePosition) { + value = evaluateExpression(ident); + } else if (pos instanceof LabelPosition) { + value = evaluateExpression(ident); + } else if (pos instanceof DisassemblyPosition) { + // first, try to evaluate as register + value = evaluateRegister(ident); + if (value == null) { + // if this fails, try expression + value = evaluateExpression(ident); + } + } + if (value != null) { + return ident + " = " + value; //$NON-NLS-1$ + } + } catch (BadLocationException e) { + if (DsfUIPlugin.getDefault().isDebugging()) { + DsfUIPlugin.getDefault().getLog().log(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, "Internal Error", e)); //$NON-NLS-1$ + } + } + return null; + } + + /** + * Evaluate the given register. + * @param register + * @return register value or <code>null</code> + */ + private String evaluateRegister(String register) { + // TLETODO [disassembly] evaluate register + return null; + } + + /** + * Evaluate the given expression. + * @param expr + * @return expression value or <code>null</code> + */ + private String evaluateExpression(String expr) { + final IExpressions expressions= fDisassemblyPart.getService(IExpressions.class); + if (expressions == null) { + return null; + } + final IFrameDMContext frameDmc= fDisassemblyPart.getTargetFrameContext(); + if (frameDmc == null || !fDisassemblyPart.isSuspended()) { + return null; + } + IExpressionDMContext exprDmc= expressions.createExpression(frameDmc, expr); + final FormattedValueDMContext valueDmc= expressions.getFormattedValueContext(exprDmc, IFormattedValues.NATURAL_FORMAT); + final DsfExecutor executor= fDisassemblyPart.getSession().getExecutor(); + Query<FormattedValueDMData> query= new Query<FormattedValueDMData>() { + @Override + protected void execute(final DataRequestMonitor<FormattedValueDMData> rm) { + expressions.getFormattedExpressionValue(valueDmc, new DataRequestMonitor<FormattedValueDMData>(executor, rm) { + @Override + protected void handleSuccess() { + FormattedValueDMData data= getData(); + rm.setData(data); + rm.done(); + } + }); + }}; + + executor.execute(query); + FormattedValueDMData data= null; + try { + data= query.get(); + } catch (InterruptedException exc) { + } catch (ExecutionException exc) { + } + if (data != null) { + return data.getFormattedValue(); + } + return null; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyView.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyView.java new file mode 100644 index 00000000000..5ab83c6c1d3 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyView.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * 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.internal.ui.disassembly; + +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.action.Separator; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.ui.IActionBars; +import org.eclipse.ui.IMemento; +import org.eclipse.ui.ISelectionListener; +import org.eclipse.ui.IViewPart; +import org.eclipse.ui.IViewSite; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.PartInitException; + +/** + * DisassemblyView + */ +public class DisassemblyView extends DisassemblyPart implements IViewPart { + + private ISelectionListener fDebugViewListener; + + /** + * + */ + public DisassemblyView() { + super(); + } + + @Override + protected IActionBars getActionBars() { + return getViewSite().getActionBars(); + } + + /* + * @see org.eclipse.ui.IViewPart#getViewSite() + */ + public IViewSite getViewSite() { + return (IViewSite)getSite(); + } + + /* + * @see org.eclipse.ui.IViewPart#init(org.eclipse.ui.IViewSite) + */ + public void init(IViewSite site) throws PartInitException { + setSite(site); + } + + /* + * @see org.eclipse.ui.IViewPart#init(org.eclipse.ui.IViewSite, org.eclipse.ui.IMemento) + */ + public void init(IViewSite site, IMemento memento) throws PartInitException { + setSite(site); + site.getPage().addSelectionListener(IDebugUIConstants.ID_DEBUG_VIEW, fDebugViewListener= new ISelectionListener() { + public void selectionChanged(IWorkbenchPart part, ISelection selection) { + updateDebugContext(); + }}); + } + + /* + * @see org.eclipse.ui.IViewPart#saveState(org.eclipse.ui.IMemento) + */ + public void saveState(IMemento memento) { + } + + @Override + protected void contributeToActionBars(IActionBars bars) { + super.contributeToActionBars(bars); + fillLocalPullDown(bars.getMenuManager()); + } + + protected void fillLocalPullDown(IMenuManager manager) { + manager.add(fActionGotoPC); + manager.add(fActionGotoAddress); + manager.add(fActionToggleSource); + manager.add(new Separator()); + } + + @Override + protected void closePart() { + getViewSite().getPage().hideView(this); + } + + @Override + public void dispose() { + getSite().getPage().removeSelectionListener(fDebugViewListener); + super.dispose(); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyViewer.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyViewer.java new file mode 100644 index 00000000000..b57cd30f185 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyViewer.java @@ -0,0 +1,283 @@ +/******************************************************************************* + * 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.internal.ui.disassembly; + +import java.util.Iterator; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.DisassemblyDocument; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.source.CompositeRuler; +import org.eclipse.jface.text.source.IOverviewRuler; +import org.eclipse.jface.text.source.IVerticalRuler; +import org.eclipse.jface.text.source.IVerticalRulerColumn; +import org.eclipse.jface.text.source.SourceViewer; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.dnd.Clipboard; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.events.ControlListener; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Composite; + +/** + * DisassemblyViewer + */ +public class DisassemblyViewer extends SourceViewer { + + class ResizeListener implements ControlListener { + /* + * @see ControlListener#controlResized(ControlEvent) + */ + public void controlResized(ControlEvent e) { + updateViewportListeners(RESIZE); + } + /* + * @see ControlListener#controlMoved(ControlEvent) + */ + public void controlMoved(ControlEvent e) { + } + } + + private boolean fUserTriggeredScrolling; + private int fCachedLastTopPixel; + + // extra resize listener to workaround bug 171018 + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=171018 + private ResizeListener fResizeListener; + + /** + * Create a new DisassemblyViewer. + * @param parent + * @param ruler + * @param overviewRuler + * @param showsAnnotationOverview + * @param styles + */ + public DisassemblyViewer(Composite parent, IVerticalRuler ruler, IOverviewRuler overviewRuler, boolean showsAnnotationOverview, int styles) { + super(parent, ruler, overviewRuler, showsAnnotationOverview, styles); + // always readonly + setEditable(false); + } + + /* + * @see org.eclipse.jface.text.source.SourceViewer#createControl(org.eclipse.swt.widgets.Composite, int) + */ + @Override + protected void createControl(Composite parent, int styles) { + super.createControl(parent, styles); + // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=171018 + getTextWidget().addControlListener(fResizeListener= new ResizeListener()); + } + + /* + * @see org.eclipse.jface.text.source.SourceViewer#handleDispose() + */ + @Override + protected void handleDispose() { + if (fResizeListener != null) { + getTextWidget().removeControlListener(fResizeListener); + } + super.handleDispose(); + } + + /* + * @see org.eclipse.jface.text.source.SourceViewer#doOperation(int) + */ + @Override + public void doOperation(int operation) { + switch (operation) { + case COPY: + StyledText textWidget = getTextWidget(); + if (textWidget == null || !redraws()) { + return; + } + if (textWidget.getSelectionCount() == 0) { + return; + } + String selectedText; + try { + selectedText = getSelectedText(); + } catch (BadLocationException e) { + // should not happend + DsfUIPlugin.getDefault().getLog().log(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, e.getLocalizedMessage(), e)); + return; + } + Clipboard clipboard = new Clipboard(textWidget.getDisplay()); + clipboard.setContents(new Object[] { selectedText }, new Transfer[] { TextTransfer.getInstance() }); + clipboard.dispose(); + break; + default: + super.doOperation(operation); + } + } + + /** + * Get the selected text together with text displayed in visible + * ruler columns. + * @return the selected text + * @throws BadLocationException + */ + public String getSelectedText() throws BadLocationException { + StringBuffer text = new StringBuffer(200); + String lineSeparator = System.getProperty("line.separator"); //$NON-NLS-1$ + DisassemblyDocument doc = (DisassemblyDocument)getDocument(); + Point selection = getSelectedRange(); + int startOffset = selection.x; + int length = selection.y; + int endOffset = startOffset + length; + int startLine = doc.getLineOfOffset(startOffset); + int endLine = doc.getLineOfOffset(endOffset); + int firstLineOffset = startOffset - doc.getLineOffset(startLine); + if (firstLineOffset > 0) { + // partial first line + int lineLength = doc.getLineInformation(startLine).getLength(); + text.append(doc.get(startOffset, Math.min(lineLength - firstLineOffset, length))); + ++startLine; + if (startLine <= endLine) { + text.append(lineSeparator); + } + } + for (int line = startLine; line < endLine; ++line) { + String lineText = getLineText(line); + text.append(lineText); + text.append(lineSeparator); + } + if (doc.getLineOffset(endLine) < endOffset) { + // partial last line + if (startLine <= endLine) { + int lineStart = doc.getLineOffset(endLine); + text.append(getLinePrefix(endLine)); + text.append(doc.get(lineStart, endOffset - lineStart)); + } + } + return text.toString(); + } + + /** + * Return the content of the given line, excluding line separator. + * @param line the line number + * @return the line content + * @throws BadLocationException + */ + public String getLineText(int line) throws BadLocationException { + IDocument doc = getDocument(); + IRegion lineRegion = doc.getLineInformation(line); + return getLinePrefix(line) + doc.get(lineRegion.getOffset(), lineRegion.getLength()); + } + + /** + * Get the line prefix by concatenating the text displayed by + * the visible ruler columns. + * @param line the line number + * @return the prefix string with trailing blank or the empty string + */ + public String getLinePrefix(int line) { + StringBuffer prefix = new StringBuffer(10); + IVerticalRuler ruler = getVerticalRuler(); + if (ruler instanceof CompositeRuler) { + for (Iterator<?> iter = ((CompositeRuler)ruler).getDecoratorIterator(); iter.hasNext();) { + IVerticalRulerColumn column = (IVerticalRulerColumn) iter.next(); + if (column instanceof DisassemblyRulerColumn) { + DisassemblyRulerColumn disassColumn = (DisassemblyRulerColumn)column; + String columnText = disassColumn.createDisplayString(line); + prefix.append(columnText); + int columnWidth = disassColumn.computeNumberOfCharacters(); + columnWidth -= columnText.length(); + while(columnWidth-- > 0) + prefix.append(' '); + prefix.append(' '); + } + } + } + return prefix.toString(); + } + + /** + * Scroll the given position into the visible area if it is not yet visible. + * @param offset + * @see org.eclipse.jface.text.TextViewer#revealRange(int, int) + */ + public void revealOffset(int offset, boolean onTop) { + try { + IDocument doc = getVisibleDocument(); + + int focusLine = doc.getLineOfOffset(offset); + + StyledText textWidget = getTextWidget(); + int top = textWidget.getTopIndex(); + if (top > -1) { + + // scroll vertically + int lines = getEstimatedVisibleLinesInViewport(); + int bottom = top + lines; + + int bottomBuffer = Math.max(1, lines / 3); + + if (!onTop && 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 = onTop ? 0 : lines / 3; + textWidget.setTopIndex(Math.max(0, focusLine - topBuffer)); + } + updateViewportListeners(INTERNAL); + } + } + } catch (BadLocationException ble) { + throw new IllegalArgumentException(ble.getLocalizedMessage()); + } + } + + /** + * @return the number of visible lines in the viewport assuming a constant + * line height. + */ + private int getEstimatedVisibleLinesInViewport() { + StyledText textWidget = getTextWidget(); + if (textWidget != null) { + Rectangle clArea= textWidget.getClientArea(); + if (!clArea.isEmpty()) + return clArea.height / textWidget.getLineHeight(); + } + return -1; + } + + int getLastTopPixel() { + return fCachedLastTopPixel; + } + boolean isUserTriggeredScrolling() { + return fUserTriggeredScrolling; + } + + /* + * @see org.eclipse.jface.text.TextViewer#updateViewportListeners(int) + */ + @Override + protected void updateViewportListeners(int origin) { + fCachedLastTopPixel = fLastTopPixel; + fUserTriggeredScrolling = origin != INTERNAL && origin != RESIZE; + super.updateViewportListeners(origin); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyViewerConfiguration.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyViewerConfiguration.java new file mode 100644 index 00000000000..c11fbe38d03 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyViewerConfiguration.java @@ -0,0 +1,131 @@ +/******************************************************************************* + * 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.internal.ui.disassembly; + +import org.eclipse.jface.text.DocumentEvent; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITextHover; +import org.eclipse.jface.text.ITypedRegion; +import org.eclipse.jface.text.IUndoManager; +import org.eclipse.jface.text.Region; +import org.eclipse.jface.text.TextPresentation; +import org.eclipse.jface.text.hyperlink.IHyperlinkDetector; +import org.eclipse.jface.text.presentation.IPresentationDamager; +import org.eclipse.jface.text.presentation.IPresentationReconciler; +import org.eclipse.jface.text.presentation.IPresentationRepairer; +import org.eclipse.jface.text.presentation.PresentationReconciler; +import org.eclipse.jface.text.reconciler.IReconciler; +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.ui.editors.text.EditorsUI; +import org.eclipse.ui.editors.text.TextSourceViewerConfiguration; + +/** + * DisassemblyViewerConfiguration + */ +public class DisassemblyViewerConfiguration extends TextSourceViewerConfiguration { + + private DisassemblyPart fPart; + + /** + * SimpleDamagerRepairer + */ + public class SimpleDamagerRepairer implements IPresentationDamager, IPresentationRepairer { + + /* + * @see org.eclipse.jface.text.presentation.IPresentationDamager#setDocument(org.eclipse.jface.text.IDocument) + */ + public void setDocument(IDocument document) { + } + + /* + * @see org.eclipse.jface.text.presentation.IPresentationDamager#getDamageRegion(org.eclipse.jface.text.ITypedRegion, org.eclipse.jface.text.DocumentEvent, boolean) + */ + public IRegion getDamageRegion(ITypedRegion partition, DocumentEvent e, boolean documentPartitioningChanged) { + int start= e.fOffset; + int end= e.getOffset() + (e.getText() == null ? 0 : e.getText().length()); + return new Region(start, end - start); + } + + /* + * @see org.eclipse.jface.text.presentation.IPresentationRepairer#createPresentation(org.eclipse.jface.text.TextPresentation, org.eclipse.jface.text.ITypedRegion) + */ + public void createPresentation(TextPresentation presentation, ITypedRegion damage) { + // do nothing + } + + } + + /** + * + */ + public DisassemblyViewerConfiguration(DisassemblyPart part) { + super(EditorsUI.getPreferenceStore()); + fPart = part; + } + + /* + * @see org.eclipse.jface.text.source.SourceViewerConfiguration#getPresentationReconciler(org.eclipse.jface.text.source.ISourceViewer) + */ + @Override + public IPresentationReconciler getPresentationReconciler(ISourceViewer sourceViewer) { + PresentationReconciler reconciler = new PresentationReconciler(); + SimpleDamagerRepairer dr = new SimpleDamagerRepairer(); + reconciler.setDamager(dr, IDocument.DEFAULT_CONTENT_TYPE); + reconciler.setRepairer(dr, IDocument.DEFAULT_CONTENT_TYPE); + return reconciler; + } + /* + * @see org.eclipse.jface.text.source.SourceViewerConfiguration#getUndoManager(org.eclipse.jface.text.source.ISourceViewer) + */ + @Override + public IUndoManager getUndoManager(ISourceViewer sourceViewer) { + // no undo/redo + return null; + } + + /* + * @see org.eclipse.jface.text.source.SourceViewerConfiguration#getTextHover(org.eclipse.jface.text.source.ISourceViewer, java.lang.String) + */ + @Override + public ITextHover getTextHover(ISourceViewer sourceViewer, String contentType) { + return new DisassemblyTextHover(fPart); + } + + /* + * @see org.eclipse.ui.editors.text.TextSourceViewerConfiguration#getHyperlinkDetectors(org.eclipse.jface.text.source.ISourceViewer) + */ + @Override + public IHyperlinkDetector[] getHyperlinkDetectors(ISourceViewer sourceViewer) { + IHyperlinkDetector[] inheritedDetectors= super.getHyperlinkDetectors(sourceViewer); + + if (fPart == null) + return inheritedDetectors; + + int inheritedDetectorsLength= inheritedDetectors != null ? inheritedDetectors.length : 0; + IHyperlinkDetector[] detectors= new IHyperlinkDetector[inheritedDetectorsLength + 1]; + detectors[0]= new DisassemblyHyperlinkDetector(fPart); + for (int i= 0; i < inheritedDetectorsLength; i++) { + detectors[i+1]= inheritedDetectors[i]; + } + + return detectors; + } + + /* + * @see org.eclipse.ui.editors.text.TextSourceViewerConfiguration#getReconciler(org.eclipse.jface.text.source.ISourceViewer) + */ + @Override + public IReconciler getReconciler(ISourceViewer sourceViewer) { + // disable spell checking + return null; + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/EditionFinderJob.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/EditionFinderJob.java new file mode 100644 index 00000000000..40b32498824 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/EditionFinderJob.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * 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.internal.ui.disassembly; + +import java.math.BigInteger; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.SourceFileInfo; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFileState; +import org.eclipse.core.runtime.Assert; +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.SubProgressMonitor; +import org.eclipse.core.runtime.jobs.Job; + +/** + * A job to find a suitable edition from the local history + * based on a file and the timestamp of the code module. + */ +class EditionFinderJob extends Job { + + private final IFile fFile; + private final BigInteger fAddress; + private final DisassemblyPart fDisassemblyPart; + private final SourceFileInfo fSourceInfo; + + /** + * Create a new edition finder for a file resource and address. + * + * @param sourceInfo the file info containing the file resource for which to find an edition + * @param address address inside the module + * @param disassemblyPart the disassembly part where this job originated from + */ + public EditionFinderJob(SourceFileInfo sourceInfo, BigInteger address, DisassemblyPart disassemblyPart) { + super(DisassemblyMessages.EditionFinderJob_name); + Assert.isNotNull(sourceInfo); + Assert.isLegal(sourceInfo.fFile instanceof IFile); + fSourceInfo= sourceInfo; + fFile = (IFile)sourceInfo.fFile; + fAddress = address; + fDisassemblyPart= disassemblyPart; + setRule(fFile); + setSystem(true); + sourceInfo.fEditionJob= this; + } + + /* + * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) + */ + @Override + protected IStatus run(IProgressMonitor monitor) { + monitor.beginTask(DisassemblyMessages.EditionFinderJob_name, 2); + monitor.subTask(DisassemblyMessages.EditionFinderJob_task_get_timestamp); + long moduleTime; + Object token = fDisassemblyPart.retrieveModuleTimestamp(fAddress); + if (token != null && !(token instanceof Long) && !monitor.isCanceled()) { + try { + synchronized (token) { + token.wait(1000); + } + } catch (InterruptedException e) { + DisassemblyPart.internalError(e); + } + token = fDisassemblyPart.retrieveModuleTimestamp(fAddress); + } + monitor.worked(1); + if (token instanceof Long && !monitor.isCanceled()) { + moduleTime = ((Long)token).longValue(); + long buildTime = moduleTime * 1000; + if (fFile.getLocalTimeStamp() > buildTime) { + monitor.subTask(DisassemblyMessages.EditionFinderJob_task_search_history); + // get history - recent states first + IFileState[] states; + try { + states = fFile.getHistory(new SubProgressMonitor(monitor, 1)); + } catch (CoreException e) { + states = new IFileState[0]; + } + for (int i = 0; i < states.length; i++) { + IFileState state = states[i]; + long saveTime = state.getModificationTime(); + if (saveTime <= buildTime) { + fSourceInfo.fEdition = state; + break; + } + } + } + } + fSourceInfo.fEditionJob = null; + monitor.worked(1); + monitor.done(); + return Status.OK_STATUS; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/FunctionOffsetRulerColumn.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/FunctionOffsetRulerColumn.java new file mode 100644 index 00000000000..3c8809d3e10 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/FunctionOffsetRulerColumn.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * 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.internal.ui.disassembly; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.AddressRangePosition; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.DisassemblyDocument; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.DisassemblyPosition; +import org.eclipse.jface.text.BadLocationException; + +/** + * A vertical ruler column to display the function + offset of instructions. + */ +public class FunctionOffsetRulerColumn extends DisassemblyRulerColumn { + + /** Maximum width of column (in characters) */ + private static final int MAXWIDTH= 40; + + /** + * Default constructor. + */ + public FunctionOffsetRulerColumn() { + super(); + } + + /* + * @see org.eclipse.jface.text.source.LineNumberRulerColumn#createDisplayString(int) + */ + @Override + protected String createDisplayString(int line) { + DisassemblyDocument doc = (DisassemblyDocument)getParentRuler().getTextViewer().getDocument(); + int offset; + try { + offset = doc.getLineOffset(line); + AddressRangePosition pos = doc.getDisassemblyPosition(offset); + if (pos instanceof DisassemblyPosition && pos.length > 0 && pos.offset == offset && pos.fValid) { + DisassemblyPosition disassPos = (DisassemblyPosition)pos; + int length = disassPos.fFunction.length; + if (length > MAXWIDTH) { + return "..." + new String(disassPos.fFunction, length - MAXWIDTH + 3, MAXWIDTH - 3); //$NON-NLS-1$ + } + return new String(disassPos.fFunction); + } else if (pos != null && !pos.fValid) { + return DOTS.substring(0, Math.min(MAXWIDTH, doc.getMaxFunctionLength())); + } + } catch (BadLocationException e) { + // silently ignored + } + return ""; //$NON-NLS-1$ + } + + @Override + protected int computeNumberOfCharacters() { + DisassemblyDocument doc = (DisassemblyDocument)getParentRuler().getTextViewer().getDocument(); + return Math.min(MAXWIDTH, doc.getMaxFunctionLength()); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/IDisassemblyHelpContextIds.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/IDisassemblyHelpContextIds.java new file mode 100644 index 00000000000..4e4eb8838aa --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/IDisassemblyHelpContextIds.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * 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.internal.ui.disassembly; + +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; + +/** + * IDisassemblyHelpContextIds + */ +public interface IDisassemblyHelpContextIds { + + public final static String PREFIX = DsfUIPlugin.PLUGIN_ID + '.'; + public final static String DISASSEMBLY_PREFERENCE_PAGE = PREFIX + "disassembly_preference_page"; //$NON-NLS-1$ + public final static String DISASSEMBLY_VIEW = PREFIX + "disassembly_view"; //$NON-NLS-1$ + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/IDisassemblyPart.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/IDisassemblyPart.java new file mode 100644 index 00000000000..5b6b2ebe380 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/IDisassemblyPart.java @@ -0,0 +1,102 @@ +/******************************************************************************* + * 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.internal.ui.disassembly; + +import java.math.BigInteger; + +import org.eclipse.jface.action.IMenuListener; +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.ui.IWorkbenchPart; + +/** + * Interface which the disassembly view and editor implement. + */ +public interface IDisassemblyPart extends IWorkbenchPart { + + /** + * Property id for the active state of the part. + */ + public final int PROP_ACTIVE= 0x505; + + /** + * Property id for the connected state of the part. + */ + public final int PROP_CONNECTED= 0x506; + + /** + * Property id for the suspended state of the underlying execution context. + */ + public final int PROP_SUSPENDED= 0x507; + + /** + * Test whether this part is connected to a debug session and execution context. + * + * @return <code>true</code> if the part is connected to a debug session and execution context + */ + boolean isConnected(); + + /** + * Test whether this part is active. A part is active if it is visible and connected. + * + * @return <code>true</code> if the part is active + */ + boolean isActive(); + + /** + * Test whether the underlying execution context is currently suspended. + * Implies connected state. + * + * @return <code>true</code> if the execution context is currently suspended + */ + boolean isSuspended(); + + /** + * Get access to the text viewer. + * + * @return the text viewer + */ + ISourceViewer getTextViewer(); + + /** + * Navigate to the given address. + * + * @param address + */ + void gotoAddress(BigInteger address); + + /** + * Navigate to current program counter. + */ + void gotoProgramCounter(); + + /** + * Navigate to the address the given expression evaluates to. + * + * @param expression a symbolic address expression + */ + void gotoSymbol(String expression); + + /** + * Adds a ruler context menu listener to the disassembly part. + * + * @param listener the listener + */ + void addRulerContextMenuListener(IMenuListener listener); + + /** + * Removes a ruler context menu listener from the disassembly part. + * + * @param listener the listener + */ + void removeRulerContextMenuListener(IMenuListener listener); + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/SourceColorerJob.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/SourceColorerJob.java new file mode 100644 index 00000000000..508aed24244 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/SourceColorerJob.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * 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.internal.ui.disassembly; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.DisassemblyDocument; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.SourceFileInfo; +import org.eclipse.core.resources.IStorage; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.progress.IWorkbenchSiteProgressService; +import org.eclipse.ui.progress.UIJob; + +/** + * UI job to color source code. + */ +class SourceColorerJob extends UIJob implements Runnable { + + private final DisassemblyPart fDisassemblyPart; + private final ISourceViewer fViewer; + private final DisassemblyDocument fDocument; + private final IStorage fStorage; + + public SourceColorerJob(Display jobDisplay, IStorage storage, DisassemblyPart disassemblyPart) { + super(DisassemblyMessages.SourceColorerJob_name); + fDisassemblyPart= disassemblyPart; + fViewer= disassemblyPart.getTextViewer(); + fDocument= (DisassemblyDocument) fViewer.getDocument(); + fStorage = storage; + setDisplay(fDisassemblyPart.getSite().getShell().getDisplay()); + setSystem(true); + setPriority(INTERACTIVE); + } + + /* + * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor) + */ + @Override + public IStatus runInUIThread(IProgressMonitor monitor) { + if (fViewer != null && !monitor.isCanceled()) { + monitor.beginTask(DisassemblyMessages.SourceColorerJob_name, IProgressMonitor.UNKNOWN); + SourceFileInfo fi = fDocument.getSourceInfo(fStorage); + if (fi != null) { + fi.initPresentationCreator(fViewer); + if (fi.fError != null) { + String message= DisassemblyMessages.Disassembly_log_error_readFile + fi.fFileKey; + fDisassemblyPart.logWarning(message, fi.fError); + } + } + fDisassemblyPart.updateInvalidSource(); + monitor.done(); + } + return Status.OK_STATUS; + } + + /* + * @see java.lang.Runnable#run() + */ + public void run() { + IWorkbenchSiteProgressService progressService = (IWorkbenchSiteProgressService)fDisassemblyPart.getSite().getAdapter(IWorkbenchSiteProgressService.class); + if(progressService != null) { + progressService.schedule(this, 0, true); + } else { + schedule(); + } + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/AbstractDisassemblyAction.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/AbstractDisassemblyAction.java new file mode 100644 index 00000000000..81b935133c0 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/AbstractDisassemblyAction.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.actions; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart; +import org.eclipse.core.runtime.Assert; +import org.eclipse.jface.action.Action; +import org.eclipse.ui.IPropertyListener; +import org.eclipse.ui.texteditor.IUpdate; + +public abstract class AbstractDisassemblyAction extends Action implements IUpdate, IPropertyListener { + + protected IDisassemblyPart fDisassemblyPart; + + AbstractDisassemblyAction() { + } + + /** + * Create a disassembly action. + * + * @param disassemblyPart + */ + public AbstractDisassemblyAction(IDisassemblyPart disassemblyPart) { + Assert.isLegal(disassemblyPart != null); + fDisassemblyPart= disassemblyPart; + fDisassemblyPart.addPropertyListener(this); + } + + /** + * @return the disassembly part + */ + public final IDisassemblyPart getDisassemblyPart() { + return fDisassemblyPart; + } + + /* + * @see org.eclipse.jface.action.Action#run() + */ + @Override + public abstract void run(); + + public void update() { + boolean enabled= fDisassemblyPart == null || fDisassemblyPart.isConnected(); + setEnabled(enabled); + } + + /* + * @see org.eclipse.ui.IPropertyListener#propertyChanged(java.lang.Object, int) + */ + public void propertyChanged(Object source, int propId) { + if (source == fDisassemblyPart && (propId & 0x500) != 0) { + update(); + } + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/AbstractDisassemblyBreakpointRulerAction.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/AbstractDisassemblyBreakpointRulerAction.java new file mode 100644 index 00000000000..1a3ad778b39 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/AbstractDisassemblyBreakpointRulerAction.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.actions; + +import java.util.Iterator; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.model.IBreakpoint; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.source.IAnnotationModel; +import org.eclipse.jface.text.source.IVerticalRulerInfo; +import org.eclipse.ui.texteditor.SimpleMarkerAnnotation; + +/** + * Abstract implementation of a breakpoint ruler action. + */ +public abstract class AbstractDisassemblyBreakpointRulerAction extends AbstractDisassemblyRulerAction { + + /** + * Create breakpoint ruler action. + * + * @param disassemblyPart + * @param rulerInfo + */ + protected AbstractDisassemblyBreakpointRulerAction(IDisassemblyPart disassemblyPart, IVerticalRulerInfo rulerInfo) { + super(disassemblyPart, rulerInfo); + } + + /** + * Returns the breakpoint at the last line of mouse activity in the ruler + * or <code>null</code> if none. + * + * @return breakpoint associated with activity in the ruler or <code>null</code> + */ + protected IBreakpoint getBreakpoint() { + IAnnotationModel annotationModel = getAnnotationModel(); + IDocument document = getDocument(); + if (annotationModel != null) { + Iterator<?> iterator = annotationModel.getAnnotationIterator(); + while (iterator.hasNext()) { + Object object = iterator.next(); + if (object instanceof SimpleMarkerAnnotation) { + SimpleMarkerAnnotation markerAnnotation = (SimpleMarkerAnnotation) object; + IMarker marker = markerAnnotation.getMarker(); + try { + if (marker.isSubtypeOf(IBreakpoint.BREAKPOINT_MARKER)) { + Position position = annotationModel.getPosition(markerAnnotation); + int line = document.getLineOfOffset(position.getOffset()); + if (line == getRulerInfo().getLineOfLastMouseButtonActivity()) { + IBreakpoint breakpoint = DebugPlugin.getDefault().getBreakpointManager().getBreakpoint(marker); + if (breakpoint != null) { + return breakpoint; + } + } + } + } catch (CoreException e) { + } catch (BadLocationException e) { + } + } + } + } + return null; + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/AbstractDisassemblyRulerAction.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/AbstractDisassemblyRulerAction.java new file mode 100644 index 00000000000..d5af5e28b04 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/AbstractDisassemblyRulerAction.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.actions; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.source.IAnnotationModel; +import org.eclipse.jface.text.source.IVerticalRulerInfo; + +/** + * Abstract implementation for disassembly vertical ruler actions. + */ +public abstract class AbstractDisassemblyRulerAction extends AbstractDisassemblyAction { + + private final IVerticalRulerInfo fRulerInfo; + + protected AbstractDisassemblyRulerAction(IDisassemblyPart disassemblyPart, IVerticalRulerInfo rulerInfo) { + fDisassemblyPart= disassemblyPart; + fRulerInfo= rulerInfo; + } + + public final IVerticalRulerInfo getRulerInfo() { + return fRulerInfo; + } + + public final IDocument getDocument() { + return getDisassemblyPart().getTextViewer().getDocument(); + } + + public final IAnnotationModel getAnnotationModel() { + return getDisassemblyPart().getTextViewer().getAnnotationModel(); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/AbstractDisassemblyRulerActionDelegate.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/AbstractDisassemblyRulerActionDelegate.java new file mode 100644 index 00000000000..91aafa31b32 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/AbstractDisassemblyRulerActionDelegate.java @@ -0,0 +1,167 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.actions; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.action.IMenuListener; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.text.source.IVerticalRulerInfo; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Event; +import org.eclipse.ui.IEditorActionDelegate; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IViewActionDelegate; +import org.eclipse.ui.IViewPart; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.actions.ActionDelegate; +import org.eclipse.ui.texteditor.IUpdate; + +/** + * This class serves as an adapter for actions contributed to the vertical ruler's + * context menu. This adapter provides the contributed actions access to their disassembly part + * and the disassembly part's vertical ruler. These actions gain only limited access to the vertical + * ruler as defined by <code>IVerticalRulerInfo</code>. The adapter updates the + * adapter (inner) action on menu and mouse action on the vertical ruler.<p> + * Extending classes must implement the factory method + * <code>createAction(IDisassemblyPart, IVerticalRulerInfo)</code>. + * + * @see org.eclipse.ui.texteditor.AbstractRulerActionDelegate + */ +public abstract class AbstractDisassemblyRulerActionDelegate extends ActionDelegate implements IEditorActionDelegate, IViewActionDelegate, MouseListener, IMenuListener { + + /** The disassembly part. */ + private IDisassemblyPart fDisassemblyPart; + /** The action calling the action delegate. */ + private IAction fCallerAction; + /** The underlying action. */ + private IAction fAction; + + /** + * The factory method creating the underlying action. + * + * @param disassemblyPart the disassembly part the action to be created will work on + * @param rulerInfo the vertical ruler the action to be created will work on + * @return the created action + */ + protected abstract IAction createAction(IDisassemblyPart disassemblyPart, IVerticalRulerInfo rulerInfo); + + /* + * @see IEditorActionDelegate#setActiveEditor(org.eclipse.jface.action.IAction, org.eclipse.ui.IEditorPart) + */ + public void setActiveEditor(IAction callerAction, IEditorPart targetEditor) { + setTargetPart(callerAction, targetEditor); + } + + /* + * @see org.eclipse.ui.IViewActionDelegate#init(org.eclipse.ui.IViewPart) + */ + public void init(IViewPart view) { + setTargetPart(fCallerAction, view); + } + + @Override + public void init(IAction action) { + fCallerAction= action; + } + + private void setTargetPart(IAction callerAction, IWorkbenchPart targetPart) { + if (fDisassemblyPart != null) { + IVerticalRulerInfo rulerInfo= (IVerticalRulerInfo) fDisassemblyPart.getAdapter(IVerticalRulerInfo.class); + if (rulerInfo != null) { + Control control= rulerInfo.getControl(); + if (control != null && !control.isDisposed()) + control.removeMouseListener(this); + } + + fDisassemblyPart.removeRulerContextMenuListener(this); + } + + fDisassemblyPart= (IDisassemblyPart)(targetPart == null ? null : targetPart.getAdapter(IDisassemblyPart.class)); + fCallerAction= callerAction; + fAction= null; + + if (fDisassemblyPart != null) { + fDisassemblyPart.addRulerContextMenuListener(this); + + IVerticalRulerInfo rulerInfo= (IVerticalRulerInfo) fDisassemblyPart.getAdapter(IVerticalRulerInfo.class); + if (rulerInfo != null) { + fAction= createAction(fDisassemblyPart, rulerInfo); + update(); + + Control control= rulerInfo.getControl(); + if (control != null && !control.isDisposed()) + control.addMouseListener(this); + } + } + } + + @Override + public void run(IAction callerAction) { + if (fAction != null) + fAction.run(); + } + + @Override + public void runWithEvent(IAction action, Event event) { + if (fAction != null) + fAction.runWithEvent(event); + } + + @Override + public void selectionChanged(IAction action, ISelection selection) { + /* + * This is a ruler action - don't update on selection. + */ + } + + /** + * Updates to the current state. + */ + private void update() { + if (fAction instanceof IUpdate) { + ((IUpdate) fAction).update(); + if (fCallerAction != null) { + fCallerAction.setText(fAction.getText()); + fCallerAction.setEnabled(fAction.isEnabled()); + } + } + } + + /* + * @see IMenuListener#menuAboutToShow(org.eclipse.jface.action.IMenuManager) + */ + public void menuAboutToShow(IMenuManager manager) { + update(); + } + + /* + * @see MouseListener#mouseDoubleClick(org.eclipse.swt.events.MouseEvent) + */ + public void mouseDoubleClick(MouseEvent e) { + } + + /* + * @see MouseListener#mouseDown(org.eclipse.swt.events.MouseEvent) + */ + public void mouseDown(MouseEvent e) { + update(); + } + + /* + * @see MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent) + */ + public void mouseUp(MouseEvent e) { + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/ActionGotoAddress.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/ActionGotoAddress.java new file mode 100644 index 00000000000..2b9a597ef15 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/ActionGotoAddress.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.actions; + +import java.math.BigInteger; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.DisassemblyMessages; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.DisassemblyPart; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.IInputValidator; +import org.eclipse.jface.dialogs.InputDialog; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.swt.widgets.Shell; + +public final class ActionGotoAddress extends AbstractDisassemblyAction { + public ActionGotoAddress(IDisassemblyPart disassemblyPart) { + super(disassemblyPart); + setText(DisassemblyMessages.Disassembly_action_GotoAddress_label); + } + @Override + public void run() { + IInputValidator validator = new IInputValidator() { + public String isValid(String input) { + if (input == null || input.length() == 0) + return " "; //$NON-NLS-1$ + try { + BigInteger address= DisassemblyPart.decodeAddress(input); + if (address.compareTo(BigInteger.ZERO) < 0) { + return DisassemblyMessages.Disassembly_GotoAddressDialog_error_invalid_address; + } + } catch (NumberFormatException x) { + return DisassemblyMessages.Disassembly_GotoAddressDialog_error_not_a_number; //; + } + return null; + } + }; + String defaultValue = ((ITextSelection)getDisassemblyPart().getSite().getSelectionProvider().getSelection()).getText(); + if (validator.isValid(defaultValue) != null) { + defaultValue = DsfUIPlugin.getDefault().getDialogSettings().get("gotoAddress"); //$NON-NLS-1$ + if (validator.isValid(defaultValue) != null) { + defaultValue = ""; //$NON-NLS-1$ + } + } + String dlgTitle = DisassemblyMessages.Disassembly_GotoAddressDialog_title; + String dlgLabel = DisassemblyMessages.Disassembly_GotoAddressDialog_label; + final Shell shell= getDisassemblyPart().getSite().getShell(); + InputDialog dlg = new InputDialog(shell, dlgTitle, dlgLabel, defaultValue, validator); + if (dlg.open() == IDialogConstants.OK_ID) { + String value = dlg.getValue(); + BigInteger address= DisassemblyPart.decodeAddress(value); + DsfUIPlugin.getDefault().getDialogSettings().put("gotoAddress", value); //$NON-NLS-1$ + getDisassemblyPart().gotoAddress(address); + } + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/ActionGotoProgramCounter.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/ActionGotoProgramCounter.java new file mode 100644 index 00000000000..5dfddfb633e --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/ActionGotoProgramCounter.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.actions; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.DisassemblyMessages; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart; + +public final class ActionGotoProgramCounter extends AbstractDisassemblyAction { + public ActionGotoProgramCounter(IDisassemblyPart disassemblyPart) { + super(disassemblyPart); + setText(DisassemblyMessages.Disassembly_action_GotoPC_label); + setToolTipText(DisassemblyMessages.Disassembly_action_GotoPC_tooltip); + } + @Override + public void run() { + getDisassemblyPart().gotoProgramCounter(); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/ActionGotoSymbol.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/ActionGotoSymbol.java new file mode 100644 index 00000000000..893970968b7 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/ActionGotoSymbol.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.actions; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.DisassemblyMessages; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.cdt.internal.ui.text.CWordFinder; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.InputDialog; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.swt.widgets.Shell; + +@SuppressWarnings("restriction") +public final class ActionGotoSymbol extends AbstractDisassemblyAction { + public ActionGotoSymbol(IDisassemblyPart disassemblyPart) { + super(disassemblyPart); + setText(DisassemblyMessages.Disassembly_action_GotoSymbol_label); + } + @Override + public void run() { + ITextViewer viewer = getDisassemblyPart().getTextViewer(); + IDocument document= viewer.getDocument(); + IRegion wordRegion = CWordFinder.findWord(document, viewer.getSelectedRange().x); + String defaultValue = null; + if (wordRegion != null) { + try { + defaultValue = document.get(wordRegion.getOffset(), wordRegion.getLength()); + } catch (BadLocationException e) { + // safely ignored + } + } + if (defaultValue == null) { + defaultValue = DsfUIPlugin.getDefault().getDialogSettings().get("gotoSymbol"); //$NON-NLS-1$ + if (defaultValue == null) { + defaultValue = ""; //$NON-NLS-1$ + } + } + String dlgTitle = DisassemblyMessages.Disassembly_GotoSymbolDialog_title; + String dlgLabel = DisassemblyMessages.Disassembly_GotoSymbolDialog_label; + final Shell shell= getDisassemblyPart().getSite().getShell(); + InputDialog dlg = new InputDialog(shell, dlgTitle, dlgLabel, defaultValue, null); + if (dlg.open() == IDialogConstants.OK_ID) { + String value = dlg.getValue(); + DsfUIPlugin.getDefault().getDialogSettings().put("gotoSymbol", value); //$NON-NLS-1$ + getDisassemblyPart().gotoSymbol(value); + } + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/ActionOpenPreferences.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/ActionOpenPreferences.java new file mode 100644 index 00000000000..dd5186fa8d9 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/ActionOpenPreferences.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.actions; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.DisassemblyMessages; +import org.eclipse.jface.action.Action; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.dialogs.PreferencesUtil; + +public final class ActionOpenPreferences extends Action { + private final static String PREF_PAGE_ID = "org.eclipse.cdt.dsf.debug.ui.disassembly.preferencePage"; //$NON-NLS-1$ + private final Shell fShell; + public ActionOpenPreferences(Shell shell) { + fShell= shell; + setText(DisassemblyMessages.Disassembly_action_OpenPreferences_label); + } + @Override + public void run() { + PreferencesUtil.createPreferenceDialogOn(fShell, PREF_PAGE_ID, new String[] { PREF_PAGE_ID }, null).open(); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/BreakpointPropertiesRulerAction.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/BreakpointPropertiesRulerAction.java new file mode 100644 index 00000000000..4aab7e44ea2 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/BreakpointPropertiesRulerAction.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.actions; + +import org.eclipse.cdt.debug.core.model.ICBreakpoint; +import org.eclipse.cdt.debug.internal.ui.CBreakpointContext; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.DisassemblyMessages; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart; +import org.eclipse.debug.core.model.IBreakpoint; +import org.eclipse.debug.ui.DebugUITools; +import org.eclipse.jface.text.source.IVerticalRulerInfo; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.ui.dialogs.PropertyDialogAction; + +/** + * Ruler action to display breakpoint properties. + */ +public class BreakpointPropertiesRulerAction extends AbstractDisassemblyBreakpointRulerAction { + + private Object fContext; + + protected BreakpointPropertiesRulerAction(IDisassemblyPart disassemblyPart, IVerticalRulerInfo rulerInfo) { + super(disassemblyPart, rulerInfo); + setText(DisassemblyMessages.Disassembly_action_BreakpointProperties_label); + } + + /* + * @see org.eclipse.cdt.dsf.debug.internal.ui.disassembly.actions.AbstractDisassemblyAction#run() + */ + @Override + public void run() { + if ( fContext != null ) { + PropertyDialogAction action = new PropertyDialogAction( getDisassemblyPart().getSite(), new ISelectionProvider() { + + public void addSelectionChangedListener( ISelectionChangedListener listener ) { + } + + public ISelection getSelection() { + return new StructuredSelection( fContext ); + } + + public void removeSelectionChangedListener( ISelectionChangedListener listener ) { + } + + public void setSelection( ISelection selection ) { + } + } ); + action.run(); + action.dispose(); + } + } + + /* + * @see IUpdate#update() + */ + @Override + public void update() { + IBreakpoint breakpoint= getBreakpoint(); + if (breakpoint instanceof ICBreakpoint) { + fContext = new CBreakpointContext((ICBreakpoint)breakpoint, getDebugContext()); + } else { + fContext = breakpoint; + } + setEnabled( fContext != null ); + } + + private ISelection getDebugContext() { + return DebugUITools.getDebugContextManager().getContextService(getDisassemblyPart().getSite().getWorkbenchWindow()).getActiveContext(); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/BreakpointPropertiesRulerActionDelegate.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/BreakpointPropertiesRulerActionDelegate.java new file mode 100644 index 00000000000..b5589d0d4f3 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/BreakpointPropertiesRulerActionDelegate.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.actions; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.text.source.IVerticalRulerInfo; + +/** + * Ruler action delegate for the breakpoint properties action. + */ +public class BreakpointPropertiesRulerActionDelegate extends AbstractDisassemblyRulerActionDelegate { + + /* + * @see org.eclipse.cdt.dsf.debug.internal.ui.disassembly.actions.AbstractDisassemblyRulerActionDelegate#createAction(org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart, org.eclipse.jface.text.source.IVerticalRulerInfo) + */ + @Override + protected IAction createAction(IDisassemblyPart disassemblyPart, IVerticalRulerInfo rulerInfo) { + return new BreakpointPropertiesRulerAction(disassemblyPart, rulerInfo); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/TextOperationAction.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/TextOperationAction.java new file mode 100644 index 00000000000..87ac2124c45 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/TextOperationAction.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.actions; + +import org.eclipse.jface.action.Action; +import org.eclipse.jface.text.ITextOperationTarget; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.ui.texteditor.IUpdate; + +/** + * TextOperationAction + */ +public class TextOperationAction extends Action implements IUpdate { + + private int fOperationCode= -1; + private ITextOperationTarget fOperationTarget; + + public TextOperationAction(ITextViewer viewer, int operationCode) { + fOperationCode= operationCode; + fOperationTarget= viewer.getTextOperationTarget(); + update(); + } + + /** + * Updates the enabled state of the action. + * Fires a property change if the enabled state changes. + * + * @see Action#firePropertyChange(String, Object, Object) + */ + public void update() { + + boolean wasEnabled= isEnabled(); + boolean isEnabled= (fOperationTarget != null && fOperationTarget.canDoOperation(fOperationCode)); + setEnabled(isEnabled); + + if (wasEnabled != isEnabled) { + firePropertyChange(ENABLED, wasEnabled ? Boolean.TRUE : Boolean.FALSE, isEnabled ? Boolean.TRUE : Boolean.FALSE); + } + } + + /** + * @see Action#run() + */ + @Override + public void run() { + if (fOperationCode != -1 && fOperationTarget != null) { + fOperationTarget.doOperation(fOperationCode); + } + } + } diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/Addr2Line.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/Addr2Line.java new file mode 100644 index 00000000000..7f72b603188 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/Addr2Line.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.model; + +import java.math.BigInteger; + + +public class Addr2Line { + public BigInteger addr; + public Addr2Line next; + public int first; + public int last; + + public static int hash(BigInteger addr, int size) { + return (int)((addr.shiftRight(2).longValue()) % size); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/AddressRangePosition.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/AddressRangePosition.java new file mode 100644 index 00000000000..7f4b865fdf0 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/AddressRangePosition.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.model; + +import java.math.BigInteger; + +import org.eclipse.jface.text.Position; + +/** + * AddressRangePosition + */ +public class AddressRangePosition extends Position { + + public BigInteger fAddressOffset; + public BigInteger fAddressLength; + public boolean fValid; + + /** + * @param offset + * @param length + */ + public AddressRangePosition(int offset, int length, BigInteger addressOffset, BigInteger addressLength) { + super(offset, length); + fAddressOffset = addressOffset; + fAddressLength = addressLength; + fValid = true; + } + + /** + * @param offset + * @param length + * @param valid + */ + public AddressRangePosition(int offset, int length, BigInteger addressOffset, BigInteger addressLength, boolean valid) { + super(offset, length); + fAddressOffset = addressOffset; + fAddressLength = addressLength; + fValid = valid; + } + + /** + * @param address + * @return + */ + public boolean containsAddress(BigInteger address) { + return address.compareTo(fAddressOffset) >= 0 + && address.compareTo(fAddressOffset.add(fAddressLength)) < 0; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object other) { + // identity comparison + return this == other; + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/BreakpointsAnnotationModel.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/BreakpointsAnnotationModel.java new file mode 100644 index 00000000000..c95b04a817a --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/BreakpointsAnnotationModel.java @@ -0,0 +1,229 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.model; + +import java.math.BigInteger; +import java.util.Iterator; + +import org.eclipse.cdt.debug.core.model.ICAddressBreakpoint; +import org.eclipse.cdt.debug.core.model.ICLineBreakpoint; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IMarkerDelta; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.IBreakpointListener; +import org.eclipse.debug.core.IBreakpointManager; +import org.eclipse.debug.core.model.IBreakpoint; +import org.eclipse.debug.core.model.ILineBreakpoint; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.DocumentEvent; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IDocumentListener; +import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.source.Annotation; +import org.eclipse.jface.text.source.AnnotationModel; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.texteditor.MarkerAnnotation; +import org.eclipse.ui.texteditor.SimpleMarkerAnnotation; + +/** + * Annotation model for breakpoints in the disassembly. + * Works only with {@link DisassemblyDocument}. + */ +public class BreakpointsAnnotationModel extends AnnotationModel implements IBreakpointListener, IDocumentListener { + + private Runnable fCatchup; + + @Override + public void connect(IDocument document) { + super.connect(document); + if (document instanceof DisassemblyDocument) { + final IBreakpointManager bpMgr= DebugPlugin.getDefault().getBreakpointManager(); + addBreakpoints(bpMgr.getBreakpoints()); + bpMgr.addBreakpointListener(this); + document.addDocumentListener(this); + } + } + + @Override + public void disconnect(IDocument document) { + if (document instanceof DisassemblyDocument) { + final IBreakpointManager bpMgr= DebugPlugin.getDefault().getBreakpointManager(); + bpMgr.removeBreakpointListener(this); + document.removeDocumentListener(this); + fCatchup= null; + } + super.disconnect(document); + } + + private void catchupWithBreakpoints() { + removeAllAnnotations(false); + final IBreakpointManager bpMgr= DebugPlugin.getDefault().getBreakpointManager(); + addBreakpoints(bpMgr.getBreakpoints()); + } + + private void addBreakpoints(IBreakpoint[] breakpoints) { + for (IBreakpoint breakpoint : breakpoints) { + addBreakpointAnnotation(breakpoint, false); + } + fireModelChanged(); + } + + /* + * @see org.eclipse.debug.core.IBreakpointListener#breakpointAdded(org.eclipse.debug.core.model.IBreakpoint) + */ + public void breakpointAdded(IBreakpoint breakpoint) { + addBreakpointAnnotation(breakpoint, true); + } + + /* + * @see org.eclipse.debug.core.IBreakpointListener#breakpointChanged(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta) + */ + public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta) { + Annotation a= findAnnotation(breakpoint.getMarker()); + if (a != null) { + if (a instanceof SimpleMarkerAnnotation) { + ((SimpleMarkerAnnotation)a).update(); + } + synchronized (getLockObject()) { + getAnnotationModelEvent().annotationChanged(a); + } + fireModelChanged(); + } + } + + /* + * @see org.eclipse.debug.core.IBreakpointListener#breakpointRemoved(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta) + */ + public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) { + Annotation a= findAnnotation(breakpoint.getMarker()); + if (a != null) { + removeAnnotation(a, true); + } + } + + @SuppressWarnings("unchecked") + private Annotation findAnnotation(IMarker marker) { + for (Iterator<SimpleMarkerAnnotation> it= getAnnotationIterator(false); it.hasNext();) { + SimpleMarkerAnnotation a= it.next(); + if (a.getMarker().equals(marker)) { + return a; + } + } + return null; + } + + private void addBreakpointAnnotation(IBreakpoint breakpoint, boolean fireEvent) { + final IMarker marker= breakpoint.getMarker(); + if (marker == null) { + return; + } + try { + Position position= createPositionFromBreakpoint(breakpoint); + if (position != null) { + addAnnotation(new MarkerAnnotation(marker), position, fireEvent); + } + } catch (CoreException exc) { + // ignore problems accessing attributes + } catch (BadLocationException exc) { + // ignore wrong positions + } + } + + private Position createPositionFromBreakpoint(IBreakpoint breakpoint) throws CoreException { + if (breakpoint instanceof ICAddressBreakpoint) { + ICAddressBreakpoint addressBreakpoint= (ICAddressBreakpoint) breakpoint; + return createPositionFromAddress(decodeAddress(addressBreakpoint.getAddress())); + } else if (breakpoint instanceof ILineBreakpoint) { + ILineBreakpoint lineBreakpoint= (ILineBreakpoint) breakpoint; + Position position= null; + final int lineNumber= lineBreakpoint.getLineNumber() - 1; + final IMarker marker= breakpoint.getMarker(); + if (marker.getResource().getType() == IResource.FILE) { + position= createPositionFromSourceLine((IFile) marker.getResource(), lineNumber); + } else if (breakpoint instanceof ICLineBreakpoint) { + ICLineBreakpoint cBreakpoint= (ICLineBreakpoint) breakpoint; + position= createPositionFromSourceLine(cBreakpoint.getFileName(), lineNumber); + if (position == null) { + position= createPositionFromAddress(decodeAddress(cBreakpoint.getAddress())); + } + } else { + String fileName= marker.getAttribute(ICLineBreakpoint.SOURCE_HANDLE, null); + if (fileName != null) { + position= createPositionFromSourceLine(fileName, lineNumber); + } + } + return position; + } + return null; + } + + private Position createPositionFromSourceLine(String fileName, int lineNumber) { + return getDisassemblyDocument().getSourcePosition(fileName, lineNumber); + } + + private Position createPositionFromSourceLine(IFile file, int lineNumber) { + return getDisassemblyDocument().getSourcePosition(file, lineNumber); + } + + private Position createPositionFromAddress(BigInteger address) { + AddressRangePosition p= getDisassemblyDocument().getDisassemblyPosition(address); + if (p != null && p.fValid) { + return new Position(p.offset, p.length); + } + return null; + } + + private DisassemblyDocument getDisassemblyDocument() { + return (DisassemblyDocument) fDocument; + } + + /** + * Decode given string representation of a non-negative integer. A + * hexadecimal encoded integer is expected to start with <code>0x</code>. + * + * @param string + * decimal or hexadecimal representation of an non-negative integer + * @return address value as <code>BigInteger</code> + */ + private static BigInteger decodeAddress(String string) { + if (string.startsWith("0x")) { //$NON-NLS-1$ + return new BigInteger(string.substring(2), 16); + } + return new BigInteger(string); + } + + /* + * @see org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent) + */ + public void documentAboutToBeChanged(DocumentEvent event) { + } + + /* + * @see org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse.jface.text.DocumentEvent) + */ + public void documentChanged(DocumentEvent event) { + if (fCatchup == null && event.fText != null && event.fText.length() > 0) { + fCatchup= new Runnable() { + public void run() { + if (fCatchup == this) { + catchupWithBreakpoints(); + fCatchup= null; + } + }}; + Display.getCurrent().timerExec(50, fCatchup); + } + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/DisassemblyDocument.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/DisassemblyDocument.java new file mode 100644 index 00000000000..84666b0351c --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/DisassemblyDocument.java @@ -0,0 +1,1423 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.model; + +import java.io.File; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.text.REDDocument; +import org.eclipse.core.resources.IStorage; +import org.eclipse.debug.core.sourcelookup.containers.LocalFileStorage; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.BadPositionCategoryException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.Position; + +/** + * DisassemblyDocument + */ +public class DisassemblyDocument extends REDDocument { + + public final static String CATEGORY_MODEL = "category_model"; //$NON-NLS-1$ + public final static String CATEGORY_DISASSEMBLY = "category_disassembly"; //$NON-NLS-1$ + public final static String CATEGORY_SOURCE = "category_source"; //$NON-NLS-1$ + public final static String CATEGORY_LABELS = "category_labels"; //$NON-NLS-1$ + + private final static boolean DEBUG = false; + + public ArrayList<AddressRangePosition> fInvalidAddressRanges = new ArrayList<AddressRangePosition>(); + public ArrayList<SourcePosition> fInvalidSource = new ArrayList<SourcePosition>(); + private Map<IStorage, SourceFileInfo> fFileInfoMap = new HashMap<IStorage, SourceFileInfo>(); + + private int fMaxFunctionLength = 0; + + private boolean fShowAddresses = false; + private int fRadix = 16; + private boolean fShowRadixPrefix = false; + private String fRadixPrefix; + private int fNumberOfDigits; + private boolean fShowCodeBytes = false; + + private int fNumberOfInstructions; + private double fMeanSizeOfInstructions = 4; + + public DisassemblyDocument() { + super(); + } + + /* + * @see org.eclipse.jface.text.AbstractDocument#completeInitialization() + */ + @Override + protected void completeInitialization() { + super.completeInitialization(); + addPositionCategory(CATEGORY_MODEL); + addPositionCategory(CATEGORY_DISASSEMBLY); + addPositionCategory(CATEGORY_SOURCE); + addPositionCategory(CATEGORY_LABELS); + setRadix(16); + setShowRadixPrefix(false); + } + + /** + * Cleanup. + */ + @Override + public void dispose() { + super.dispose(); + if (fFileInfoMap != null) { + // cleanup source info + for (Iterator<SourceFileInfo> iter = fFileInfoMap.values().iterator(); iter.hasNext();) { + SourceFileInfo fi = iter.next(); + fi.dispose(); + } + fFileInfoMap = null; + } + } + + public List<AddressRangePosition> getInvalidAddressRanges() { + return fInvalidAddressRanges; + } + + public List<SourcePosition> getInvalidSource() { + return fInvalidSource; + } + + public void setMaxOpcodeLength(int opcodeLength) { + fMaxFunctionLength = opcodeLength; + } + + public int getMaxFunctionLength() { + return fMaxFunctionLength; + } + + public int getAddressLength() { + return fNumberOfDigits+2; + } + + public int getMeanSizeOfInstructions() { + return (int)(fMeanSizeOfInstructions+.9); + } + + public Iterator<AddressRangePosition> getModelPositionIterator(BigInteger address) { + try { + return getPositionIterator(CATEGORY_MODEL, address); + } catch (BadPositionCategoryException e) { + // cannot happen + } + return null; + } + + public Iterator<Position> getPositionIterator(String category, int offset) throws BadPositionCategoryException { + @SuppressWarnings("unchecked") + List<Position> positions = (List<Position>) getDocumentManagedPositions().get(category); + if (positions == null) { + throw new BadPositionCategoryException(); + } + int idx = computeIndexInPositionList(positions, offset, true); + return positions.listIterator(idx); + } + + public Iterator<AddressRangePosition> getPositionIterator(String category, BigInteger address) throws BadPositionCategoryException { + @SuppressWarnings("unchecked") + List<AddressRangePosition> positions = (List<AddressRangePosition>) getDocumentManagedPositions().get(category); + if (positions == null) { + throw new BadPositionCategoryException(); + } + int idx = computeIndexInPositionListFirst(positions, address); + return positions.listIterator(idx); + } + + public int computeIndexInCategory(String category, BigInteger address) throws BadPositionCategoryException { + @SuppressWarnings("unchecked") + List<AddressRangePosition> c = (List<AddressRangePosition>) getDocumentManagedPositions().get(category); + if (c == null) { + throw new BadPositionCategoryException(); + } + return computeIndexInPositionListFirst(c, address); + } + + /** + * Computes the index in the list of positions at which a position with the + * given address would be inserted. The position is supposed to become the + * first in this list of all positions with the same offset. + * + * @param positions + * the list in which the index is computed + * @param address + * the address for which the index is computed + * @return the computed index + * + */ + protected int computeIndexInPositionListFirst(List<AddressRangePosition> positions, BigInteger address) { + int size = positions.size(); + if (size == 0) { + return 0; + } + int left = 0; + int right = size - 1; + int mid = 0; + while (left <= right) { + mid = (left + right) / 2; + AddressRangePosition range = positions.get(mid); + if (address.compareTo(range.fAddressOffset) < 0) { + right = mid - 1; + } else if (address.compareTo(range.fAddressOffset) == 0) { + break; + } else if (address.compareTo(range.fAddressOffset.add(range.fAddressLength)) >= 0) { + left = mid + 1; + } else { + break; + } + } + int idx = mid; + AddressRangePosition p = positions.get(idx); + if (address.compareTo(p.fAddressOffset.add(p.fAddressLength)) > 0) { + ++idx; + } else if (address.compareTo(p.fAddressOffset) == 0) { + do { + --idx; + if (idx < 0) { + break; + } + p = positions.get(idx); + } while (address.compareTo(p.fAddressOffset) == 0); + ++idx; + } + return idx; + } + + /** + * Computes the index in the list of positions at which a position with the + * given address would be inserted. The position is supposed to become the + * last but one in this list of all positions with the same address. + * + * @param positions + * the list in which the index is computed + * @param address + * the address for which the index is computed + * @return the computed index + * + */ + protected int computeIndexInPositionListLast(List<AddressRangePosition> positions, BigInteger address) { + int size = positions.size(); + if (size == 0) { + return 0; + } + int left = 0; + int right = size - 1; + int mid = 0; + while (left <= right) { + mid = (left + right) / 2; + AddressRangePosition range = positions.get(mid); + if (address.compareTo(range.fAddressOffset) < 0) { + right = mid - 1; + } else if (address.compareTo(range.fAddressOffset) == 0) { + break; + } else if (address.compareTo(range.fAddressOffset.add(range.fAddressLength)) >= 0) { + left = mid + 1; + } else { + break; + } + } + int idx = mid; + AddressRangePosition p = positions.get(idx); + if (address.compareTo(p.fAddressOffset) > 0) { + ++idx; + } else if (address.compareTo(p.fAddressOffset) == 0 && p.fAddressLength.compareTo(BigInteger.ZERO) == 0) { + do { + ++idx; + if (idx == size) { + break; + } + p = positions.get(idx); + } while (address.compareTo(p.fAddressOffset) == 0 && p.fAddressLength.compareTo(BigInteger.ZERO) == 0); + // --idx; + } + return idx; + } + + /** + * Computes the index in the list of positions at which a position with the + * given offset would be inserted. The position is supposed to become the + * last in this list of all positions with the same offset. + * + * @param positions + * the list in which the index is computed + * @param offset + * the offset for which the index is computed + * @return the computed index + * + * @see IDocument#computeIndexInCategory(String, int) + */ + protected int computeIndexInPositionListLast(List<Position> positions, int offset) { + + if (positions.size() == 0) + return 0; + + int left = 0; + int right = positions.size() - 1; + int mid = 0; + Position p = null; + + while (left < right) { + + mid = (left + right) / 2; + + p = positions.get(mid); + if (offset < p.getOffset()) { + if (left == mid) + right = left; + else + right = mid - 1; + } else if (offset > p.getOffset()) { + if (right == mid) + left = right; + else + left = mid + 1; + } else if (offset == p.getOffset()) { + left = right = mid; + } + + } + + int pos = left; + p = positions.get(pos); + while (offset >= p.getOffset()) { + // entry will become the last of all entries with the same offset + ++pos; + if (pos == positions.size()) { + break; + } + p = positions.get(pos); + } + + assert 0 <= pos && pos <= positions.size(); + + return pos; + } + + /** + * Get the position for the supplied category and index. + * + * @param category + * @param index + * @return a Position matching the category and index, or <code>null</code>. + */ + public Position getPositionOfIndex(String category, int index) throws BadPositionCategoryException { + if (index >= 0) { + @SuppressWarnings("unchecked") + List<Position> positions = (List<Position>) getDocumentManagedPositions().get(category); + if (positions == null) { + throw new BadPositionCategoryException(); + } + if (index < positions.size()) { + return positions.get(index); + } + } + return null; + } + + /** + * @param address + * @return + */ + public AddressRangePosition getPositionOfAddress(BigInteger address) { + AddressRangePosition pos = getPositionOfAddress(CATEGORY_DISASSEMBLY, address); + return pos; + } + + /** + * @param category + * @param address + * @return + */ + public AddressRangePosition getPositionOfAddress(String category, BigInteger address) { + @SuppressWarnings("unchecked") + List<AddressRangePosition> positions = (List<AddressRangePosition>) getDocumentManagedPositions().get(category); + if (positions == null) { + return null; + } + int index = computeIndexInPositionListFirst(positions, address); + if (index < positions.size()) { + AddressRangePosition p = positions.get(index); + if (address.compareTo(p.fAddressOffset) == 0 || p.containsAddress(address)) { + return p; + } + } + return null; + } + + /** + * @param category + * @param range + * @return + */ + public AddressRangePosition getPositionInAddressRange(String category, AddressRangePosition range) { + @SuppressWarnings("unchecked") + List<AddressRangePosition> positions = (List<AddressRangePosition>) getDocumentManagedPositions().get(category); + if (positions == null) { + return null; + } + BigInteger endAddress = range.fAddressOffset.add(range.fAddressLength); + int index = computeIndexInPositionListFirst(positions, range.fAddressOffset); + if (index < positions.size()) { + do { + AddressRangePosition p = positions.get(index); + if (p.fAddressOffset.compareTo(endAddress) >= 0) { + --index; + } else { + return p; + } + } while (index >= 0); + } + return null; + } + + /** + * Compute the address of the given document line number. + * + * @param line + * @return the address of the given document line number, -1 if no valid + * address can be computed + */ + public BigInteger getAddressOfLine(int line) { + try { + int offset = getLineOffset(line); + return getAddressOfOffset(offset); + } catch (BadLocationException e) { + // intentionally ignored + } + return BigInteger.valueOf(-1); + } + + /** + * Compute the address off the given document offset. + * + * @param offset + * @return the address of the given document offset, -1 if no valid address + * can be computed + */ + public BigInteger getAddressOfOffset(int offset) { + AddressRangePosition pos; + try { + pos = getModelPosition(offset); + } catch (BadLocationException e) { + internalError(e); + return BigInteger.valueOf(-1); + } + if (pos == null) { + return BigInteger.valueOf(-1); + } + return pos.fAddressOffset; + } + + /** + * @param offset + * @return + */ + public AddressRangePosition getDisassemblyPosition(int offset) throws BadLocationException { + Position p = null; + try { + p = getPosition(CATEGORY_DISASSEMBLY, offset, false); + } catch (BadPositionCategoryException e) { + // cannot happen + } + return (AddressRangePosition) p; + } + + /** + * @param address + * @return + */ + public AddressRangePosition getDisassemblyPosition(BigInteger address) { + return getPositionOfAddress(CATEGORY_DISASSEMBLY, address); + } + + + /** + * @param offset + * @return + * @throws BadLocationException + */ + public AddressRangePosition getModelPosition(int offset) throws BadLocationException { + Position p = null; + try { + p = getPosition(CATEGORY_MODEL, offset, false); + } catch (BadPositionCategoryException e) { + // cannot happen + } + return (AddressRangePosition) p; + } + + /** + * @param offset + * @return + * @throws BadLocationException + */ + public SourcePosition getSourcePosition(int offset) throws BadLocationException { + Position p = null; + try { + p = getPosition(CATEGORY_SOURCE, offset, true); + } catch (BadPositionCategoryException e) { + // cannot happen + } + return (SourcePosition) p; + } + + /** + * @param address + * @return + */ + public SourcePosition getSourcePosition(BigInteger address) { + return (SourcePosition) getPositionOfAddress(CATEGORY_SOURCE, address); + } + + /** + * @param address + * @return + */ + public LabelPosition getLabelPosition(BigInteger address) { + return (LabelPosition) getPositionOfAddress(CATEGORY_LABELS, address); + } + + /** + * @param range + * @return + */ + public SourcePosition getSourcePositionInAddressRange(AddressRangePosition range) { + return (SourcePosition) getPositionInAddressRange(CATEGORY_SOURCE, range); + } + + /** + * Compute document position of the given source line. + * + * @param file the file as an <code>IStorage</code> + * @param lineNumber the 0-based line number + * @return the document position or <code>null</code> + */ + public Position getSourcePosition(IStorage file, int lineNumber) { + SourceFileInfo info= getSourceInfo(file); + return getSourcePosition(info, lineNumber); + } + + /** + * Compute document position of the given source line. + * + * @param fileName the file name, may be a raw debugger path or the path to an external file + * @param lineNumber the 0-based line number + * @return the document position or <code>null</code> + */ + public Position getSourcePosition(String fileName, int lineNumber) { + SourceFileInfo info= getSourceInfo(fileName); + if (info == null) { + info= getSourceInfo(new LocalFileStorage(new File(fileName))); + } + return getSourcePosition(info, lineNumber); + } + + /** + * Compute document position of the given source line. + * + * @param info + * @param lineNumber the 0-based line number + * @return the document position or <code>null</code> + */ + protected Position getSourcePosition(SourceFileInfo info, int lineNumber) { + if (info == null || info.fSource == null) { + return null; + } + try { + SourcePosition srcPos= null; + IRegion stmtLineRegion= info.fSource.getLineInformation(lineNumber); + final int lineOffset = stmtLineRegion.getOffset(); + final int lineLength = stmtLineRegion.getLength() + 1; + BigInteger stmtAddress = info.fLine2Addr[lineNumber]; + if (stmtAddress != null && stmtAddress.compareTo(BigInteger.ZERO) > 0) { + srcPos = getSourcePosition(stmtAddress); + } + if (srcPos == null) { + for (Iterator<Position> iterator = getPositionIterator(CATEGORY_SOURCE, 0); iterator.hasNext(); ) { + SourcePosition pos= (SourcePosition) iterator.next(); + if (pos.fFileInfo == info && pos.fValid && lineNumber >= pos.fLine) { + int baseOffset= info.fSource.getLineOffset(pos.fLine); + if (lineOffset + lineLength - baseOffset <= pos.length) { + srcPos= pos; + break; + } + } + } + if (srcPos == null) { + return null; + } + } else if (!srcPos.fValid) { + return null; + } + assert lineNumber >= srcPos.fLine; + int baseOffset = info.fSource.getLineOffset(srcPos.fLine); + int offset = srcPos.offset + lineOffset - baseOffset; + if (offset >= srcPos.offset && offset < srcPos.offset + srcPos.length) { + return new Position(offset, lineLength); + } + } catch (BadLocationException exc) { + // TLETODO Auto-generated catch block + exc.printStackTrace(); + } catch (BadPositionCategoryException exc) { + // TLETODO Auto-generated catch block + exc.printStackTrace(); + } + return null; + } + + + /** + * @param category + * @param offset + * @return + * @throws BadPositionCategoryException + * @throws BadLocationException + */ + public Position getPosition(String category, int offset, boolean allowZeroLength) throws BadLocationException, BadPositionCategoryException { + @SuppressWarnings("unchecked") + List<Position> list = (List<Position>) getDocumentManagedPositions().get(category); + int idx; + idx = computeIndexInPositionList(list, offset, true); + if (idx > 0) { + --idx; + } + while (idx < list.size()) { + Position pos = list.get(idx); + if (pos.offset > offset) { + break; + } + if (pos.includes(offset)) { + return pos; + } + if (allowZeroLength && pos.offset == offset) { + return pos; + } + ++idx; + } + return null; + } + + /** + * @param pos + */ + public void addModelPosition(AddressRangePosition pos) { + try { + addPositionLast(CATEGORY_MODEL, pos); + } catch (BadPositionCategoryException e) { + // cannot happen + } + } + + /** + * @param pos + */ + public void addModelPositionFirst(AddressRangePosition pos) { + @SuppressWarnings("unchecked") + List<AddressRangePosition> list = (List<AddressRangePosition>) getDocumentManagedPositions().get(CATEGORY_MODEL); + int idx; + idx = computeIndexInPositionListFirst(list, pos.fAddressOffset.add(pos.fAddressLength)); + if (idx < list.size()) { + AddressRangePosition nextPos = list.get(idx); + assert nextPos.fAddressOffset.compareTo(pos.fAddressOffset.add(pos.fAddressLength)) == 0; + } + list.add(idx, pos); + } + + /** + * @param pos + * @throws BadLocationException + */ + public void addDisassemblyPosition(AddressRangePosition pos) throws BadLocationException { + try { + addPositionLast(CATEGORY_DISASSEMBLY, pos); + } catch (BadPositionCategoryException e) { + // cannot happen + } + if (pos instanceof DisassemblyPosition) { + int functionLength = ((DisassemblyPosition)pos).fFunction.length; + if (functionLength > fMaxFunctionLength) { + fMaxFunctionLength = functionLength; + } + if (fNumberOfInstructions < 100) { + fMeanSizeOfInstructions = (fMeanSizeOfInstructions * fNumberOfInstructions + pos.fAddressLength.floatValue()) / (++fNumberOfInstructions); + } + } + } + + /** + * @param pos + * @throws BadPositionCategoryException + */ + public void addPositionLast(String category, AddressRangePosition pos) throws BadPositionCategoryException { + @SuppressWarnings("unchecked") + List<AddressRangePosition> list = (List<AddressRangePosition>) getDocumentManagedPositions().get(category); + if (list == null) { + throw new BadPositionCategoryException(); + } + int idx; + idx = computeIndexInPositionListLast(list, pos.fAddressOffset); + list.add(idx, pos); + } + + /** + * @param pos + * @throws BadLocationException + */ + public void addLabelPosition(AddressRangePosition pos) throws BadLocationException { + try { + addPositionLast(CATEGORY_LABELS, pos); + } catch (BadPositionCategoryException e) { + // cannot happen + } + } + + /** + * @param pos + */ + public void addSourcePosition(AddressRangePosition pos) throws BadLocationException { + try { + addPositionLast(CATEGORY_SOURCE, pos); + } catch (BadPositionCategoryException e) { + // cannot happen + } + } + + /** + * @param pos + */ + public void removeDisassemblyPosition(AddressRangePosition pos) { + try { + removePosition(CATEGORY_DISASSEMBLY, pos); + } catch (BadPositionCategoryException e) { + // cannot happen + } + } + + /** + * @param pos + */ + public void removeSourcePosition(AddressRangePosition pos) { + try { + removePosition(CATEGORY_SOURCE, pos); + } catch (BadPositionCategoryException e) { + // cannot happen + } + } + + /** + * @param pos + */ + public void removeModelPosition(AddressRangePosition pos) { + try { + removePosition(getCategory(pos), pos); + } catch (BadPositionCategoryException e) { + // cannot happen + } + } + + /** + * @param pos + * @return + */ + private static String getCategory(AddressRangePosition pos) { + if (pos instanceof LabelPosition) { + return CATEGORY_LABELS; + } else if (pos instanceof SourcePosition) { + return CATEGORY_SOURCE; + } + return CATEGORY_DISASSEMBLY; + } + + /* + * @see org.eclipse.jface.text.IDocument#removePosition(java.lang.String, + * org.eclipse.jface.text.Position) + */ + @Override + public void removePosition(String category, Position position) throws BadPositionCategoryException { + super.removePosition(category, position); + if (category != CATEGORY_MODEL && position instanceof AddressRangePosition) { + super.removePosition(CATEGORY_MODEL, position); + } + } + + @SuppressWarnings("unchecked") + public void removePositions(String category, List<AddressRangePosition> toRemove) { + if (toRemove.isEmpty()) { + return; + } + List<Position> positions = (List<Position>) getDocumentManagedPositions().get(category); + if (positions != null) { + positions.removeAll(toRemove); + } + if (category != CATEGORY_MODEL) { + positions = (List<Position>) getDocumentManagedPositions().get(CATEGORY_MODEL); + if (positions != null) { + positions.removeAll(toRemove); + } + } + } + + public void addPositionLast(String category, Position position) throws BadLocationException, + BadPositionCategoryException { + + if ((0 > position.offset) || (0 > position.length) || (position.offset + position.length > getLength())) + throw new BadLocationException(); + + if (category == null) + throw new BadPositionCategoryException(); + + @SuppressWarnings("unchecked") + List<Position> list = (List<Position>) getDocumentManagedPositions().get(category); + if (list == null) + throw new BadPositionCategoryException(); + + list.add(computeIndexInPositionListLast(list, position.offset), position); + } + + public void checkConsistency() { + AddressRangePosition last = null; + try { + for (Iterator<Position> it = getPositionIterator(CATEGORY_MODEL, 0); it.hasNext();) { + AddressRangePosition pos = (AddressRangePosition) it.next(); + if (last != null) { + assert last.fAddressOffset.compareTo(pos.fAddressOffset) <= 0; + assert last.fAddressOffset.add(last.fAddressLength).compareTo(pos.fAddressOffset) == 0; + assert last.offset <= pos.offset; + assert last.offset + last.length == pos.offset; + } + last = pos; + } + } catch (BadPositionCategoryException e) { + assert false; + } + } + + /** + * @param insertPos + * @param replaceLength + * @param text + * @throws BadLocationException + */ + public void replace(AddressRangePosition insertPos, int replaceLength, String text) throws BadLocationException { + int delta = (text != null ? text.length() : 0) - replaceLength; + if (delta != 0) { + BigInteger address = insertPos.fAddressOffset; + Iterator<AddressRangePosition> it = getModelPositionIterator(address); + while (it.hasNext()) { + AddressRangePosition pos = it.next(); + assert pos.fAddressOffset.compareTo(address) >= 0; + if (pos.fAddressOffset.compareTo(address) > 0) { + break; + } + if (pos.offset > insertPos.offset) { + break; + } + if (pos == insertPos) { + break; + } + } + while (it.hasNext()) { + AddressRangePosition pos = it.next(); + pos.offset += delta; + } + } + super.replace(insertPos.offset, replaceLength, text); + } + + /** + * @param pos + * @param insertPos + * @param line + * @throws BadPositionCategoryException + * @throws BadLocationException + */ + public AddressRangePosition insertAddressRange(AddressRangePosition pos, AddressRangePosition insertPos, String line, boolean addToModel) + throws BadLocationException { + final BigInteger address = insertPos.fAddressOffset; + BigInteger length = insertPos.fAddressLength; + if (pos == null) { + pos = getPositionOfAddress(address); + } + assert !pos.isDeleted && !pos.fValid && (length.compareTo(BigInteger.ZERO) == 0 || pos.containsAddress(address)); + int insertOffset; + int replaceLength = 0; + if (length.compareTo(BigInteger.ONE) > 0 && !pos.containsAddress(address.add(length.subtract(BigInteger.ONE)))) { + // merge with successor positions + Iterator<AddressRangePosition> it = getModelPositionIterator(pos.fAddressOffset.add(pos.fAddressLength)); + assert it.hasNext(); + do { + AddressRangePosition overlap = it.next(); + BigInteger posEndAddress= pos.fAddressOffset.add(pos.fAddressLength); + assert pos.offset <= overlap.offset && overlap.fAddressOffset.compareTo(posEndAddress) == 0; + if (overlap instanceof LabelPosition || overlap instanceof SourcePosition) { + // don't override label or source positions, instead fix + // length of disassembly line to insert + length = insertPos.fAddressLength = posEndAddress.subtract(address.max(pos.fAddressOffset)); + break; + } + pos.fAddressLength = pos.fAddressLength.add(overlap.fAddressLength); + replaceLength = overlap.offset + overlap.length - pos.offset - pos.length; + it.remove(); + removeModelPosition(overlap); + if (!overlap.fValid) { + fInvalidAddressRanges.remove(overlap); + } + } while(!pos.containsAddress(address.add(length.subtract(BigInteger.ONE)))); + } + BigInteger newEndAddress = pos.fAddressOffset.add(pos.fAddressLength); + BigInteger newStartAddress = address.add(length); + assert newEndAddress.compareTo(newStartAddress) >= 0; + if (address.compareTo(pos.fAddressOffset) == 0) { + // insert at start of range + insertOffset = pos.offset; + if (replaceLength == 0 && newEndAddress.compareTo(newStartAddress) > 0) { + // optimization: shrink position in place + pos.fAddressOffset = newStartAddress; + pos.fAddressLength = pos.fAddressLength.subtract(length); + // don't insert new pos + newEndAddress = newStartAddress; + } else { + replaceLength += pos.length; + fInvalidAddressRanges.remove(pos); + removeDisassemblyPosition(pos); + pos = null; + } + } else { + // insert in mid/end of range + insertOffset = pos.offset + pos.length; + pos.fAddressLength = address.subtract(pos.fAddressOffset); + assert pos.fAddressLength.compareTo(BigInteger.ZERO) > 0; + pos = null; + } + if (newEndAddress.compareTo(newStartAddress) > 0) { + pos = insertInvalidAddressRange(insertOffset+replaceLength, 0, newStartAddress, newEndAddress); + } + assert pos == null || pos.fAddressLength.compareTo(BigInteger.ZERO) > 0 && pos.containsAddress(address.add(length)); + assert insertOffset + replaceLength <= getLength(); + + insertPos.offset = insertOffset; + if (addToModel) { + addModelPosition(insertPos); + } + replace(insertPos, replaceLength, line); + if (DEBUG) checkConsistency(); + return pos; + } + + /** + * @param pos + * @param address + * @param length + * @param instruction + * @throws BadPositionCategoryException + * @throws BadLocationException + */ + public AddressRangePosition insertDisassemblyLine(AddressRangePosition pos, BigInteger address, int length, String opcode, String instruction, String file, int lineNr) + throws BadLocationException { + String disassLine = null; + if (instruction == null || instruction.length() == 0) { + disassLine = ""; //$NON-NLS-1$ + } else { + disassLine = buildDisassemblyLine(address, opcode, instruction); + } + AddressRangePosition disassPos; + if (lineNr < 0) { + disassPos = new DisassemblyPosition(0, disassLine.length(), address, BigInteger.valueOf(length), opcode); + } else { + disassPos = new DisassemblyWithSourcePosition(0, disassLine.length(), address, BigInteger.valueOf(length), + opcode, file, lineNr); + } + pos = insertAddressRange(pos, disassPos, disassLine, true); + addDisassemblyPosition(disassPos); + return pos; + } + /** + * @param address + * @param opcode + * @param instruction + */ + private String buildDisassemblyLine(BigInteger address, String opcode, String instruction) { + StringBuffer buf = new StringBuffer(40); + if (fShowAddresses) { + if (fRadixPrefix != null) { + buf.append(fRadixPrefix); + } + String str = address.toString(fRadix); + for (int i=str.length(); i<fNumberOfDigits; ++i) + buf.append('0'); + buf.append(str); + buf.append(':'); + buf.append(' '); + } + if (fShowCodeBytes && opcode != null && opcode.length() > 0) { + buf.append(opcode); + int tab = 16; + if (opcode.length() >= 16) { + tab = (opcode.length() + 8) & ~7; + } + int diff = tab - opcode.length(); + while (diff-- > 0) { + buf.append(' '); + } + } else if (!fShowAddresses) { + buf.append(' '); + buf.append(' '); + } + int n = instruction.length(); + int prefixLen = buf.length(); + for (int j = 0; j < n; j++) { + char ch = instruction.charAt(j); + if (ch == '\t') { + int tab = (buf.length()-prefixLen + 8) & ~0x7; + do + buf.append(' '); + while (buf.length()-prefixLen < tab); + } else { + buf.append(ch); + } + } + buf.append('\n'); + return buf.toString(); + } + + public void setRadix(int radix) { + fRadix = radix; + fNumberOfDigits = (int)(Math.log(1L<<32)/Math.log(radix)+0.9); + setShowRadixPrefix(fShowRadixPrefix); + } + + public void setShowRadixPrefix(boolean showRadixPrefix) { + fShowRadixPrefix = showRadixPrefix; + if (!fShowRadixPrefix) { + fRadixPrefix = null; + } else if (fRadix == 16) { + fRadixPrefix = "0x"; //$NON-NLS-1$ + } else if (fRadix == 8) { + fRadixPrefix = "0"; //$NON-NLS-1$ + } else { + fRadixPrefix = null; + } + } + + public AddressRangePosition insertErrorLine(AddressRangePosition pos, BigInteger address, BigInteger length, String line) + throws BadLocationException { + int hashCode = line.hashCode(); + final long alignment = 0x1L; + if (alignment > 1 && !(pos instanceof ErrorPosition)) { + AddressRangePosition before = getPositionOfAddress(address.subtract(BigInteger.ONE)); + if (before instanceof ErrorPosition && before.hashCode() == hashCode && before.offset + before.length == pos.offset) { + assert before.fAddressOffset.add(before.fAddressLength).compareTo(address) == 0; + assert pos.fAddressOffset.compareTo(address) == 0; + // merge with previous error position + BigInteger pageOffset = before.fAddressOffset.and(BigInteger.valueOf(~(alignment-1))); + BigInteger mergeLen = pageOffset.add(BigInteger.valueOf(alignment)) + .subtract((before.fAddressOffset.add(before.fAddressLength))).min(length); + if (mergeLen.compareTo(BigInteger.ZERO) > 0) { + pos.fAddressLength = pos.fAddressLength.subtract(mergeLen); + if (pos.fAddressLength.compareTo(BigInteger.ZERO) == 0) { + replace(pos, pos.length, null); + removeModelPosition(pos); + fInvalidAddressRanges.remove(pos); + pos = null; + } else { + pos.fAddressOffset = pos.fAddressOffset.add(mergeLen); + } + before.fAddressLength = before.fAddressLength.add(mergeLen); + address = address.add(mergeLen); + length = length.subtract(mergeLen); + if (DEBUG) checkConsistency(); + if (length.compareTo(BigInteger.ZERO) == 0) { + return pos; + } + } + } + AddressRangePosition after = getPositionOfAddress(address.add(length)); + if (after instanceof ErrorPosition && after.hashCode() == hashCode && pos.offset + pos.length == after.offset) { + assert after.fAddressOffset == address.add(length); + assert pos.fAddressOffset.add(pos.fAddressLength).compareTo(after.fAddressOffset) == 0; + // merge with next error position + BigInteger pageOffset = after.fAddressOffset.add(BigInteger.valueOf(~(alignment-1))); + BigInteger mergeLen = after.fAddressOffset.subtract(pageOffset).min(length); + if (mergeLen.compareTo(BigInteger.ZERO) > 0) { + after.fAddressOffset = after.fAddressOffset.subtract(mergeLen); + after.fAddressLength = after.fAddressLength.add(mergeLen); + pos.fAddressLength = pos.fAddressLength.subtract(mergeLen); + if (pos.fAddressLength.compareTo(BigInteger.ZERO) == 0) { + replace(pos, pos.length, null); + removeModelPosition(pos); + fInvalidAddressRanges.remove(pos); + pos = null; + } + if (DEBUG) checkConsistency(); + length = length.subtract(mergeLen); + if (length.compareTo(BigInteger.ZERO) == 0) { + return pos; + } + } + } + } + BigInteger pageOffset = address.and(BigInteger.valueOf(~(alignment-1))); + BigInteger posLen = pageOffset.add(BigInteger.valueOf(alignment)).subtract(address).min(length); + while (length.compareTo(BigInteger.ZERO) > 0) { + AddressRangePosition errorPos = new ErrorPosition(0, 0, address, posLen, hashCode); + String errorLine = buildDisassemblyLine(address, null, line); + // TLEHACK: check for error messages, which occur only temporarily: + // "Target is busy. Try again later" + // "Cannot Perform requested Operation" + if (line.startsWith("Target is busy") || line.startsWith("Cannot perform")) { //$NON-NLS-1$ //$NON-NLS-2$ + // try again only once... + if (!(pos instanceof ErrorPosition)) { + errorLine = "...\n"; //$NON-NLS-1$ + errorPos.fValid = false; + } + } + errorPos.length = errorLine.length(); + pos = insertAddressRange(pos, errorPos, errorLine, true); + addDisassemblyPosition(errorPos); + if (!errorPos.fValid) { + fInvalidAddressRanges.add(errorPos); + } + length = length.subtract(posLen); + address = address.add(posLen); + posLen = BigInteger.valueOf(alignment).min(length); + } + return pos; + } + + /** + * @param pos + * @param address + * @param label + * @throws BadLocationException + * @throws BadPositionCategoryException + */ + public AddressRangePosition insertLabel(AddressRangePosition pos, BigInteger address, String label, boolean showLabels) + throws BadLocationException { + String labelLine = showLabels ? label + ":\n" : ""; //$NON-NLS-1$ //$NON-NLS-2$ + LabelPosition labelPos = getLabelPosition(address); + if (labelPos != null) { + assert labelPos.fAddressOffset.compareTo(address) == 0; + if (labelPos.length != labelLine.length()) { + int oldLength = labelPos.length; + labelPos.length = labelLine.length(); + replace(labelPos, oldLength, labelLine); + } + return pos; + } + labelPos = new LabelPosition(0, labelLine.length(), address, null); + pos = insertAddressRange(pos, labelPos, labelLine, true); + addLabelPosition(labelPos); + return pos; + } + + /** + * @param pos + * @param address + * @param source + * @param line + * @param endOfSource + * @throws BadLocationException + * @throws BadPositionCategoryException + */ + public SourcePosition insertSource(SourcePosition pos, String source, int line, boolean endOfSource) { +// System.out.println("insertSource at "+getAddressText(pos.fAddressOffset)); +// System.out.println(source); + String sourceLines = source; + if (source.length() > 0 && sourceLines.charAt(source.length() - 1) != '\n') { + sourceLines += "\n"; //$NON-NLS-1$ + } + try { + assert !pos.fValid; + int oldLength = pos.length; + pos.length = sourceLines.length(); + pos.fLine = line; + pos.fValid = true; + fInvalidSource.remove(pos); + replace(pos, oldLength, sourceLines); + if (!endOfSource) { + if (pos.length > 0) { + SourcePosition oldPos = getSourcePosition(pos.offset+pos.length); + if (oldPos == null || oldPos.fAddressOffset.compareTo(pos.fAddressOffset) != 0) { + pos = new SourcePosition(pos.offset+pos.length, 0, pos.fAddressOffset, pos.fFileInfo, line, false); + addSourcePosition(pos); + addModelPosition(pos); + fInvalidSource.add(pos); + } else { + //TLETODO need more checks for correct source pos + pos = oldPos; + } + } + } + } catch (BadLocationException e) { + internalError(e); + } + return pos; + } + + /** + * @param pos + * @param address + * @param fi + * @param lineNr + * @return + */ + public AddressRangePosition insertInvalidSource(AddressRangePosition pos, BigInteger address, SourceFileInfo fi, int lineNr) { + SourcePosition sourcePos = getSourcePosition(address); + if (sourcePos != null) { + return pos; + } + String sourceLine = ""; //$NON-NLS-1$ + sourcePos = new SourcePosition(0, sourceLine.length(), address, fi, lineNr, false); + try { + pos = insertAddressRange(pos, sourcePos, sourceLine, true); + addSourcePosition(sourcePos); + assert !fInvalidSource.contains(sourcePos); + fInvalidSource.add(sourcePos); + } catch (BadLocationException e) { + internalError(e); + } + return pos; + } + + /** + * @param offset + * @param replaceLength + * @param startAddress + * @param endAddress + * @return + */ + public AddressRangePosition insertInvalidAddressRange(int offset, int replaceLength, BigInteger startAddress, BigInteger endAddress) { + String periods = "...\n"; //$NON-NLS-1$ + AddressRangePosition newPos = new AddressRangePosition(offset, periods.length(), startAddress, endAddress + .subtract(startAddress), false); + try { + addModelPositionFirst(newPos); + replace(newPos, replaceLength, periods); + addDisassemblyPosition(newPos); + fInvalidAddressRanges.add(newPos); + } catch (BadLocationException e) { + internalError(e); + } + return newPos; + } + + public void invalidateAddressRange(BigInteger startAddress, BigInteger endAddress, boolean collapse) { + deleteDisassemblyRange(startAddress, endAddress, true, collapse); + } + + public void deleteDisassemblyRange(BigInteger startAddress, BigInteger endAddress, boolean invalidate, boolean collapse) { + String replacement = invalidate ? "...\n" : null; //$NON-NLS-1$ + int replaceLen = replacement != null ? replacement.length() : 0; + AddressRangePosition lastPos = null; + ArrayList<AddressRangePosition> toRemove = new ArrayList<AddressRangePosition>(); + Iterator<AddressRangePosition> it = getModelPositionIterator(startAddress); + while (it.hasNext()) { + AddressRangePosition pos = it.next(); + BigInteger posEndAddress = pos.fAddressOffset.add(pos.fAddressLength); + if (pos instanceof LabelPosition) { + if (!invalidate && pos.length > 0 && posEndAddress.compareTo(endAddress) > 0) { + try { + int oldLength = pos.length; + pos.length = 0; + replace(pos, oldLength, null); + } catch (BadLocationException e) { + internalError(e); + } + } + pos = null; + } else if (pos instanceof SourcePosition) { + pos = null; + } else if (pos instanceof ErrorPosition) { + pos = null; + } else if (pos instanceof DisassemblyPosition) { + // optimization: join adjacent positions + if (collapse && lastPos != null + && (invalidate || lastPos.fValid == pos.fValid) + && lastPos.offset+lastPos.length == pos.offset) { + assert lastPos.fAddressOffset.add(lastPos.fAddressLength).compareTo(pos.fAddressOffset) == 0; + lastPos.length += pos.length; + lastPos.fAddressLength = lastPos.fAddressLength.add(pos.fAddressLength); + toRemove.add(pos); + if (!pos.fValid) { + fInvalidAddressRanges.remove(pos); + } + pos = null; + if (posEndAddress.compareTo(endAddress) < 0) { + continue; + } + } + } + if (lastPos != null) { + try { + if (lastPos.length > 0 || replaceLen > 0) { + int oldLength = lastPos.length; + lastPos.length = replaceLen; + replace(lastPos, oldLength, replacement); + } + } catch (BadLocationException e) { + internalError(e); + } + } + if (pos == null && posEndAddress.compareTo(endAddress) >= 0) { + break; + } + lastPos = null; + if (pos != null) { + if (pos.fValid && invalidate) { + pos.fValid = false; + fInvalidAddressRanges.add(pos); + } + lastPos = pos; + } + } + removePositions(CATEGORY_DISASSEMBLY, toRemove); + if (DEBUG) checkConsistency(); + } + + public void invalidateSource() { + Iterator<Position> it; + try { + it = getPositionIterator(CATEGORY_SOURCE, 0); + } catch (BadPositionCategoryException e) { + internalError(e); + return; + } + while (it.hasNext()) { + SourcePosition srcPos = (SourcePosition)it.next(); + if (srcPos != null && srcPos.fValid) { + srcPos.fValid = false; + assert !getInvalidSource().contains(srcPos); + getInvalidSource().add(srcPos); + } + } + } + + public void invalidateDisassemblyWithSource(boolean removeDisassembly) { + for (Iterator<SourceFileInfo> it = fFileInfoMap.values().iterator(); it.hasNext();) { + SourceFileInfo info = it.next(); + if (info.fLine2Addr != null) { + deleteDisassemblyRange(info.fStartAddress, info.fEndAddress.add(BigInteger.ONE), !removeDisassembly, !removeDisassembly); + } + } + } + + /** + * @param start + * @param end + * @throws BadLocationException + */ + public void deleteLineRange(int start, int end) throws BadLocationException { + if (start >= end) { + return; + } + int startOffset = getLineOffset(start); + int endOffset = getLineOffset(end); + int replaceLength = 0; + AddressRangePosition startPos = getDisassemblyPosition(startOffset); + if (startPos == null) { + return; + } + startOffset = startPos.offset; + AddressRangePosition endPos = getDisassemblyPosition(endOffset); + if (endPos == null) { + return; + } + BigInteger startAddress = BigInteger.ZERO; + BigInteger addressLength = BigInteger.ZERO; + ArrayList<AddressRangePosition> toRemove = new ArrayList<AddressRangePosition>(); + try { + Iterator<AddressRangePosition> it = getPositionIterator(DisassemblyDocument.CATEGORY_MODEL, startAddress); + while (it.hasNext()) { + AddressRangePosition p = it.next(); + addressLength = addressLength.add(p.fAddressLength); + replaceLength += p.length; + toRemove.add(p); + if (!p.fValid) { + if (p instanceof SourcePosition) { + getInvalidSource().remove(p); + } else { + getInvalidAddressRanges().remove(p); + } + } + if (addressLength.compareTo(BigInteger.ZERO) > 0 && p.fAddressOffset.compareTo(endPos.fAddressOffset) >= 0) { + break; + } + } + } catch (BadPositionCategoryException e) { + // cannot happen + } + for (Iterator<AddressRangePosition> iter = toRemove.iterator(); iter.hasNext();) { + AddressRangePosition pos = iter.next(); + removeModelPosition(pos); + } + if (addressLength.compareTo(BigInteger.ZERO) > 0) { + insertInvalidAddressRange(startOffset, replaceLength, startAddress, startAddress.add(addressLength)); + } + } + + public SourceFileInfo getSourceInfo(BigInteger address) { + AddressRangePosition pos = getDisassemblyPosition(address); + if (pos instanceof DisassemblyPosition) { + DisassemblyPosition disassPos = (DisassemblyPosition)pos; + return getSourceInfo(disassPos.getFile()); + } + return null; + } + + public SourceFileInfo getSourceInfo(String file) { + if (fFileInfoMap == null || file == null) { + return null; + } + for (Iterator<SourceFileInfo> iter = fFileInfoMap.values().iterator(); iter.hasNext();) { + SourceFileInfo info = iter.next(); + if (file.equals(info.fFileKey)) { + return info; + } + } + return null; + } + + public SourceFileInfo getSourceInfo(IStorage sourceElement) { + if (fFileInfoMap == null) { + return null; + } + SourceFileInfo fi = fFileInfoMap.get(sourceElement); + return fi; + } + + public SourceFileInfo createSourceInfo(String fileKey, IStorage sourceElement, Runnable done) { + SourceFileInfo fi = new SourceFileInfo(fileKey, sourceElement); + assert fFileInfoMap != null; + if (fFileInfoMap != null) { + fFileInfoMap.put(sourceElement, fi); + new SourceReadingJob(fi, done); + } + return fi; + } + + private void internalError(Throwable e) { + if (DEBUG) { + System.err.println("Disassembly: Internal error"); //$NON-NLS-1$ + e.printStackTrace(); + } + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/DisassemblyPosition.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/DisassemblyPosition.java new file mode 100644 index 00000000000..d07f699353a --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/DisassemblyPosition.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.model; + +import java.math.BigInteger; + +/** + * DisassemblyPosition + */ +public class DisassemblyPosition extends AddressRangePosition { + + public char[] fFunction; + + /** + * + * @param offset + * @param length + * @param addressOffset + * @param addressLength + * @param opcodes + */ + public DisassemblyPosition(int offset, int length, BigInteger addressOffset, BigInteger addressLength, String opcodes) { + super(offset, length, addressOffset, addressLength); + fFunction = opcodes.toCharArray(); + } + + /** + * @return source file + */ + public String getFile() { + return null; + } + + /** + * @return source line number + */ + public int getLine() { + return -1; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/DisassemblyWithSourcePosition.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/DisassemblyWithSourcePosition.java new file mode 100644 index 00000000000..3b466857f28 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/DisassemblyWithSourcePosition.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.model; + +import java.math.BigInteger; + +/** + * DisassemblyWithSourcePosition + */ +public class DisassemblyWithSourcePosition extends DisassemblyPosition { + + private String fFile; + private int fLine; + + /** + * @param offset + * @param length + * @param addressOffset + * @param addressLength + * @param opcodes + */ + public DisassemblyWithSourcePosition(int offset, int length, BigInteger addressOffset, BigInteger addressLength, String opcodes, String file, int lineNr) { + super(offset, length, addressOffset, addressLength, opcodes); + fFile = file; + fLine = lineNr; + } + + @Override + public String getFile() { + return fFile; + } + + @Override + public int getLine() { + return fLine; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/ErrorPosition.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/ErrorPosition.java new file mode 100644 index 00000000000..3e47f0783c9 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/ErrorPosition.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.model; + +import java.math.BigInteger; + +/** + * ErrorPosition + */ +public class ErrorPosition extends AddressRangePosition { + + public int fHashCode; + + /** + * @param offset + * @param length + * @param addressOffset + * @param addressLength + */ + public ErrorPosition(int offset, int length, BigInteger addressOffset, BigInteger addressLength, int hashCode) { + super(offset, length, addressOffset, addressLength); + fHashCode = hashCode; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return fHashCode; + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/LabelPosition.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/LabelPosition.java new file mode 100644 index 00000000000..5dbaea989c0 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/LabelPosition.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.model; + +import java.math.BigInteger; + +/** + * LabelPosition + */ +public class LabelPosition extends AddressRangePosition { + + public String fLabel; + + /** + * @param offset + * @param length + * @param addressOffset + */ + public LabelPosition(int offset, int length, BigInteger addressOffset, String label) { + super(offset, length, addressOffset, BigInteger.ZERO); + fLabel = label; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/SourceDocumentProvider.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/SourceDocumentProvider.java new file mode 100644 index 00000000000..152566bed25 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/SourceDocumentProvider.java @@ -0,0 +1,117 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.model; + +import java.util.Iterator; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.text.REDDocument; +import org.eclipse.cdt.internal.ui.editor.CDocumentSetupParticipant; +import org.eclipse.core.filesystem.EFS; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFileState; +import org.eclipse.core.resources.IStorage; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.sourcelookup.containers.LocalFileStorage; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.source.IAnnotationModel; +import org.eclipse.ui.IStorageEditorInput; +import org.eclipse.ui.IURIEditorInput; +import org.eclipse.ui.editors.text.StorageDocumentProvider; + +/** + * SourceDocumentProvider + */ +@SuppressWarnings("restriction") +public class SourceDocumentProvider extends StorageDocumentProvider { + + public SourceDocumentProvider() { + super(); + } + + /** + * Dispose all connected documents. + */ + public void dispose() { + Iterator<?> it = getConnectedElements(); + while(it.hasNext()) { + Object element = it.next(); + ElementInfo info = getElementInfo(element); + // force refcount to 1 + info.fCount = 1; + disconnect(element); + } + } + + /* + * @see org.eclipse.ui.texteditor.AbstractDocumentProvider#createDocument(java.lang.Object) + */ + @Override + protected IDocument createEmptyDocument() { + IDocument doc = new REDDocument(); + return doc; + } + + /* + * @see org.eclipse.ui.texteditor.AbstractDocumentProvider#createAnnotationModel(java.lang.Object) + */ + @Override + protected IAnnotationModel createAnnotationModel(Object element) throws CoreException { + return null; + } + + /* + * @see org.eclipse.ui.editors.text.StorageDocumentProvider#setupDocument(java.lang.Object, org.eclipse.jface.text.IDocument) + */ + @Override + protected void setupDocument(Object element, IDocument document) { + super.setupDocument(element, document); + if (element instanceof IStorageEditorInput) { + new CDocumentSetupParticipant().setup(document); + } + } + + /* + * @see org.eclipse.ui.texteditor.AbstractDocumentProvider#disposeElementInfo(java.lang.Object, org.eclipse.ui.texteditor.AbstractDocumentProvider.ElementInfo) + */ + @Override + protected void disposeElementInfo(Object element, ElementInfo info) { + super.disposeElementInfo(element, info); + IDocument doc = info.fDocument; + if (doc instanceof REDDocument) { + ((REDDocument)doc).dispose(); + } + } + + /* + * @see org.eclipse.ui.texteditor.AbstractDocumentProvider#getModificationStamp(java.lang.Object) + */ + @Override + public long getModificationStamp(Object element) { + try { + if (element instanceof IStorageEditorInput) { + IStorage file= ((IStorageEditorInput)element).getStorage(); + if (file instanceof IFile) { + return ((IFile)file).getLocalTimeStamp(); + } else if (file instanceof IFileState) { + return ((IFileState)file).getModificationTime(); + } else if (file instanceof LocalFileStorage) { + return ((LocalFileStorage)file).getFile().lastModified(); + } + } else if (element instanceof IURIEditorInput) { + return EFS.getStore(((IURIEditorInput)element).getURI()).fetchInfo().getLastModified(); + } + } catch (CoreException e) { + // ignore + } + return 0; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/SourceEditorInput.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/SourceEditorInput.java new file mode 100644 index 00000000000..8980e78c637 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/SourceEditorInput.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.model; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.util.StorageEditorInput; +import org.eclipse.core.resources.IStorage; + +/** + * SourceEditorInput + */ +public class SourceEditorInput extends StorageEditorInput { + + /** + * @param storage + */ + public SourceEditorInput(IStorage storage) { + super(storage); + } + + /* + * @see org.eclipse.ui.IEditorInput#exists() + */ + public boolean exists() { + return false; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/SourceFileInfo.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/SourceFileInfo.java new file mode 100644 index 00000000000..f433228e9c4 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/SourceFileInfo.java @@ -0,0 +1,187 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.model; + +import java.math.BigInteger; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.model.ILanguage; +import org.eclipse.cdt.core.model.LanguageManager; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.presentation.ISourcePresentationCreator; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.presentation.SourcePresentationCreatorFactory; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IStorage; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.content.IContentType; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.Region; +import org.eclipse.jface.text.TextPresentation; +import org.eclipse.ui.IEditorInput; + +/** + * Holds information about a source file. + */ +public class SourceFileInfo { + public final String fFileKey; + public final IStorage fFile; + public IStorage fEdition; + public BigInteger[] fLine2Addr; + public Addr2Line[] fAddr2Line; + public volatile IDocument fSource; + public volatile boolean fValid; + public Object fLinesNode; + public Throwable fError; + public volatile SourceReadingJob fReadingJob; + public volatile Job fEditionJob; + public ISourcePresentationCreator fPresentationCreator; + public BigInteger fStartAddress = BigInteger.ONE.shiftLeft(64).subtract(BigInteger.ONE); + public BigInteger fEndAddress= BigInteger.ZERO; + + public SourceFileInfo(String fileKey, IStorage file) { + fFileKey = fileKey; + fFile = fEdition = file; + } + + /** + * Initialize source document. + * @throws CoreException + */ + public void initSource() throws CoreException { + SourceDocumentProvider provider = DsfUIPlugin.getSourceDocumentProvider(); + IEditorInput input = new SourceEditorInput(fEdition); + synchronized (provider) { + provider.connect(input); + } + IStatus status = provider.getStatus(input); + if (status != null && !status.isOK()) { + throw new CoreException(status); + } + } + + /** + * Initialize presentation creator. + * @param viewer + */ + public void initPresentationCreator(ITextViewer viewer) { + SourceDocumentProvider provider = DsfUIPlugin.getSourceDocumentProvider(); + IEditorInput input = new SourceEditorInput(fEdition); + IDocument doc = provider.getDocument(input); + if (doc != null) { + IContentType contentType= null; + if (fEdition instanceof IFile) { + IFile file= (IFile)fEdition; + contentType= CCorePlugin.getContentType(file.getProject(), file.getName()); + } else { + contentType= CCorePlugin.getContentType(fEdition.getName()); + } + ILanguage language= null; + if (contentType != null) { + language= LanguageManager.getInstance().getLanguage(contentType); + } + if (language != null) { + fPresentationCreator= SourcePresentationCreatorFactory.create(language, fEdition, viewer); + } + int lines = doc.getNumberOfLines(); + fLine2Addr = new BigInteger[lines]; + fAddr2Line = new Addr2Line[lines / 10 + 1]; + // assign fSource last, triggering source update + fSource = doc; + } + } + + /** + * Dispose this object. + */ + public void dispose() { + if (fReadingJob != null) { + if (!fReadingJob.cancel()) { + fReadingJob.dispose(); + } + fReadingJob = null; + } + if (fPresentationCreator != null) { + fPresentationCreator.dispose(); + fPresentationCreator = null; + } + SourceDocumentProvider provider = DsfUIPlugin.getSourceDocumentProvider(); + synchronized (provider) { + provider.disconnect(new SourceEditorInput(fEdition)); + } + fSource = null; + fValid = false; +// fLinesNode = null; + } + + public String getLine(int lineNr) { + return getLines(lineNr, lineNr); + } + + public String getLines(int first, int last) { + try { + int startOffset = fSource.getLineOffset(first); + int endOffset; + if (last < fSource.getNumberOfLines()-1) { + IRegion lastRegion = fSource.getLineInformation(last+1); + endOffset = lastRegion.getOffset(); + } else { + // last line + IRegion lastRegion = fSource.getLineInformation(last); + endOffset = lastRegion.getOffset() + lastRegion.getLength(); + } + return fSource.get(startOffset, endOffset - startOffset); + } catch (BadLocationException e) { + return null; + } + } + + public IRegion getRegion(int line, int length) { + try { + IRegion lineRegion = fSource.getLineInformation(line); + return new Region(lineRegion.getOffset(), length); + } catch (BadLocationException e) { + return null; + } + } + + /** + * Get or create text presentation for the given region. + * Must be called in display thread. + * @param region + * @return text presentation + */ + public TextPresentation getPresentation(IRegion region) { + if (fSource != null && fPresentationCreator != null) { + return fPresentationCreator.getPresentation(region, fSource); + } + return null; + } + + /** + * @return offset of given line + */ + public int getLineOffset(int line) { + if (fSource != null) { + try { + return fSource.getLineOffset(line); + } catch (BadLocationException e) { + // ignored + } + } + return -1; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/SourcePosition.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/SourcePosition.java new file mode 100644 index 00000000000..f5138df554b --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/SourcePosition.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.model; + +import java.math.BigInteger; + +/** + * SourcePosition + */ +public class SourcePosition extends AddressRangePosition { + + public SourceFileInfo fFileInfo; + public int fLine; + + /** + * + * @param offset + * @param length + * @param addressOffset + * @param fileInfo + * @param line + */ + public SourcePosition(int offset, int length, BigInteger addressOffset, SourceFileInfo fileInfo, int line) { + this(offset, length, addressOffset, fileInfo, line, true); + } + + /** + * + * @param offset + * @param length + * @param addressOffset + * @param fileInfo + * @param line + * @param valid + */ + public SourcePosition(int offset, int length, BigInteger addressOffset, SourceFileInfo fileInfo, int line, boolean valid) { + super(offset, length, addressOffset, BigInteger.ZERO, valid); + fFileInfo = fileInfo; + fLine = line; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/SourceReadingJob.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/SourceReadingJob.java new file mode 100644 index 00000000000..2fb06aa1d53 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/SourceReadingJob.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.model; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.DisassemblyMessages; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.ISchedulingRule; +import org.eclipse.core.runtime.jobs.Job; + + +/** + * Low-level job to read source files in the background. + */ +public class SourceReadingJob extends Job { + + private final static String NAME = DisassemblyMessages.SourceReadingJob_name; + + private SourceFileInfo fFileInfo; + private Runnable fDone; + + public SourceReadingJob(SourceFileInfo fi, Runnable done) { + super(NAME); + fFileInfo = fi; + fFileInfo.fReadingJob = this; + fDone = done; + if (fi.fFile instanceof ISchedulingRule) { + setRule((ISchedulingRule)fi.fFile); + } + setSystem(true); + // usually short lived job + setPriority(SHORT); + if (fi.fFile.getFullPath() != null) { + String fileName = fi.fFile.getFullPath().lastSegment(); + setName(NAME + " (" + fileName + ')'); //$NON-NLS-1$ + } + } + + public synchronized void dispose() { + fDone = null; + Thread thread = getThread(); + if (thread != null && thread.isAlive()) { + thread.interrupt(); + } + } + + @Override + public IStatus run(IProgressMonitor monitor) { + if (fFileInfo.fEditionJob != null) { + try { + fFileInfo.fEditionJob.join(); + } catch (InterruptedException e) { + // ignore + } + } + try { + fFileInfo.initSource(); + } catch (Throwable e) { + fFileInfo.fError = e; + } finally { + fFileInfo.fReadingJob = null; + synchronized (this) { + if (fDone != null && !getThread().isInterrupted()) { + fDone.run(); + } + } + } + // errors are handled elsewhere + return Status.OK_STATUS; + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/preferences/DisassemblyPreferenceConstants.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/preferences/DisassemblyPreferenceConstants.java new file mode 100644 index 00000000000..e06a20dd61b --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/preferences/DisassemblyPreferenceConstants.java @@ -0,0 +1,84 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.preferences; + +import java.math.BigInteger; + +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.preference.PreferenceConverter; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.ui.editors.text.EditorsUI; +import org.eclipse.ui.editors.text.TextEditorPreferenceConstants; + +/** + * DisassemblyPreferenceConstants + */ +public class DisassemblyPreferenceConstants { + + public static final String START_ADDRESS = "disassembly.startAddress"; //$NON-NLS-1$ + public static final String END_ADDRESS = "disassembly.endAddress"; //$NON-NLS-1$ + public static final String PC_HISTORY_SIZE = "disassembly.pcHistorySize"; //$NON-NLS-1$ + public static final String SHOW_SOURCE = "disassembly.showSource"; //$NON-NLS-1$ + public static final String SHOW_LABELS = "disassembly.showLabels"; //$NON-NLS-1$ + public static final String SHOW_SYMBOLS = "disassembly.showSymbols"; //$NON-NLS-1$ + public static final String SIMPLIFIED = "disassembly.simplified"; //$NON-NLS-1$ + public static final String INSTRUCTION_RADIX = "disassembly.instructionRadix"; //$NON-NLS-1$ + public static final String ADDRESS_RADIX = "disassembly.addressRadix"; //$NON-NLS-1$ + public static final String SHOW_ADDRESS_RADIX = "disassembly.showAddressRadix"; //$NON-NLS-1$ + public static final String SHOW_ADDRESS_RULER = "disassembly.showAddressRuler"; //$NON-NLS-1$ + public static final String ADDRESS_COLOR = "disassembly.addressColor"; //$NON-NLS-1$ + public static final String SHOW_FUNCTION_OFFSETS = "disassembly.showFunctionOffsetRuler"; //$NON-NLS-1$ + public static final String OPCODE_COLOR = "disassembly.opcodeColor"; //$NON-NLS-1$ + public static final String USE_SOURCE_ONLY_MODE = "disassembly.useSourceOnlyMode"; //$NON-NLS-1$ + public static final String AVOID_READ_BEFORE_PC = "disassembly.avoidReadBeforePC"; //$NON-NLS-1$ + + /** + * + */ + private DisassemblyPreferenceConstants() { + // not intended to be subclassed or instatiated + } + + /** + * Initialize preference default values. + * @param store + */ + public static void initializeDefaults(IPreferenceStore store) { + TextEditorPreferenceConstants.initializeDefaultValues(store); + store.setDefault(START_ADDRESS, 0x0L); + store.setDefault(END_ADDRESS, "0x" + BigInteger.ONE.shiftLeft(64).toString(16)); //$NON-NLS-1$ + store.setDefault(PC_HISTORY_SIZE, 4); + store.setDefault(SHOW_SOURCE, true); + store.setDefault(SHOW_FUNCTION_OFFSETS, false); + store.setDefault(SHOW_LABELS, true); + store.setDefault(SHOW_SYMBOLS, true); + store.setDefault(SIMPLIFIED, true); + store.setDefault(INSTRUCTION_RADIX, 16); + store.setDefault(ADDRESS_RADIX, 16); + store.setDefault(SHOW_ADDRESS_RADIX, false); + store.setDefault(SHOW_ADDRESS_RULER, true); + store.setDefault(AVOID_READ_BEFORE_PC, false); + store.setDefault(USE_SOURCE_ONLY_MODE, false); + PreferenceConverter.setDefault(store, ADDRESS_COLOR, new RGB(0, 96, 0)); + PreferenceConverter.setDefault(store, OPCODE_COLOR, new RGB(96, 0, 0)); + } + + public static class Initializer extends AbstractPreferenceInitializer { + @Override + public void initializeDefaultPreferences() { + IPreferenceStore store = DsfUIPlugin.getDefault().getPreferenceStore(); + initializeDefaults(store); + EditorsUI.useAnnotationsPreferencePage(store); + } + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/preferences/DisassemblyPreferencePage.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/preferences/DisassemblyPreferencePage.java new file mode 100644 index 00000000000..b41dcd46e39 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/preferences/DisassemblyPreferencePage.java @@ -0,0 +1,313 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.preferences; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.DisassemblyMessages; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyHelpContextIds; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.preference.PreferencePage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; +import org.eclipse.ui.PlatformUI; + +/** + * DisassemblyPreferencePage + */ +public class DisassemblyPreferencePage extends PreferencePage implements IWorkbenchPreferencePage { + + private List<Button> fCheckBoxes = new ArrayList<Button>(); + private List<Combo> fComboBoxes = new ArrayList<Combo>(); + private ArrayList<Text> fNumberFields = new ArrayList<Text>(); + private ModifyListener fNumberFieldListener = new ModifyListener() { + public void modifyText(ModifyEvent e) { + numberFieldChanged((Text)e.widget); + } + }; + private final static String[] fcRadixItems = { + DisassemblyMessages.DisassemblyPreferencePage_radix_octal, + DisassemblyMessages.DisassemblyPreferencePage_radix_decimal, + DisassemblyMessages.DisassemblyPreferencePage_radix_hexadecimal, + }; + private final static int[] fcRadixValues = { + 8, 10, 16 + }; + + /** + * Create the Disassembly preference page. + */ + public DisassemblyPreferencePage() { + super(); + setPreferenceStore(DsfUIPlugin.getDefault().getPreferenceStore()); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite) + */ + @Override + public void createControl(Composite parent) { + super.createControl(parent); + PlatformUI.getWorkbench().getHelpSystem().setHelp(getControl(), IDisassemblyHelpContextIds.DISASSEMBLY_PREFERENCE_PAGE); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.preference.PreferencePage#createContents(org.eclipse.swt.widgets.Composite) + */ + @Override + protected Control createContents(Composite parent) { + Composite composite = new Composite(parent, SWT.NULL); + GridLayout layout = new GridLayout(); + layout.marginHeight = 0; + layout.marginWidth = 0; + layout.numColumns = 2; + composite.setLayout(layout); + composite.setFont(parent.getFont()); + + String label; + +// label = DisassemblyMessages.DisassemblyPreferencePage_startAddress; //$NON-NLS-1$ +// addTextField(composite, label, DisassemblyPreferenceConstants.START_ADDRESS, 20, 0, true); +// label = DisassemblyMessages.DisassemblyPreferencePage_endAddress; //$NON-NLS-1$ +// addTextField(composite, label, DisassemblyPreferenceConstants.END_ADDRESS, 20, 0, true); + + label = DisassemblyMessages.DisassemblyPreferencePage_addressRadix; + addComboBox(composite, label, DisassemblyPreferenceConstants.ADDRESS_RADIX, fcRadixItems); +// label = DisassemblyMessages.DisassemblyPreferencePage_instructionRadix; +// addComboBox(composite, label, DisassemblyPreferenceConstants.INSTRUCTION_RADIX, fcRadixItems); + + label = DisassemblyMessages.DisassemblyPreferencePage_showSource; + addCheckBox(composite, label, DisassemblyPreferenceConstants.SHOW_SOURCE, 0); + label = DisassemblyMessages.DisassemblyPreferencePage_showSymbols; + addCheckBox(composite, label, DisassemblyPreferenceConstants.SHOW_SYMBOLS, 0); +// label = DisassemblyMessages.DisassemblyPreferencePage_simplifiedMnemonics; +// addCheckBox(composite, label, DisassemblyPreferenceConstants.SIMPLIFIED, 0); + label = DisassemblyMessages.DisassemblyPreferencePage_showAddressRadix; + addCheckBox(composite, label, DisassemblyPreferenceConstants.SHOW_ADDRESS_RADIX, 0); + label = DisassemblyMessages.DisassemblyPreferencePage_showFunctionOffsets; + addCheckBox(composite, label, DisassemblyPreferenceConstants.SHOW_FUNCTION_OFFSETS, 0); + label = DisassemblyMessages.DisassemblyPreferencePage_showAddress; + addCheckBox(composite, label, DisassemblyPreferenceConstants.SHOW_ADDRESS_RULER, 0); +// label = DisassemblyMessages.DisassemblyPreferencePage_avoidReadBeforePC; +// addCheckBox(composite, label, DisassemblyPreferenceConstants.AVOID_READ_BEFORE_PC, 0); + + // horizontal line +// Label separator = new Label(composite, SWT.SEPARATOR|SWT.HORIZONTAL); +// GridData data; +// data = new GridData(GridData.FILL_HORIZONTAL); +// data.horizontalSpan = layout.numColumns; +// separator.setLayoutData(data); +// +// label = DisassemblyMessages.DisassemblyPreferencePage_useSourceOnlyMode; +// addCheckBox(composite, label, DisassemblyPreferenceConstants.USE_SOURCE_ONLY_MODE, 0); +// +// // note +// String noteTitle = DisassemblyMessages.DisassemblyPreferencePage_useSourceOnlyMode_noteTtitle; +// String noteMessage = DisassemblyMessages.DisassemblyPreferencePage_useSourceOnlyMode_noteMessage; +// Composite note = createNoteComposite(composite.getFont(), composite, noteTitle, noteMessage); +// data = (GridData)note.getLayoutData(); +// if (data == null) { +// data = new GridData(GridData.HORIZONTAL_ALIGN_FILL); +// note.setLayoutData(data); +// } +// data.horizontalSpan = layout.numColumns; +// Control msgControl = note.getChildren()[1]; +// data = new GridData(GridData.FILL_HORIZONTAL); +// data.widthHint = convertWidthInCharsToPixels(65); +// data.heightHint = convertHeightInCharsToPixels(3); +// msgControl.setLayoutData(data); + + Dialog.applyDialogFont(parent); + + initialize(); + + return composite; + } + + /* (non-Javadoc) + * @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench) + */ + public void init(IWorkbench workbench) { + } + + private Button addCheckBox(Composite parent, String label, String key, int indentation) { + Button checkBox = new Button(parent, SWT.CHECK); + checkBox.setText(label); + + GridData gd = new GridData(GridData.FILL_HORIZONTAL); + gd.horizontalIndent = indentation; + gd.horizontalSpan = 2; + checkBox.setLayoutData(gd); + checkBox.setData(key); + fCheckBoxes.add(checkBox); + + return checkBox; + } + + private Combo addComboBox(Composite parent, String label, String key, String[] items) { + Label labelControl= new Label(parent, SWT.NONE); + labelControl.setText(label); + GridData gd= new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); + labelControl.setLayoutData(gd); + + Combo combo = new Combo(parent, SWT.READ_ONLY); + gd= new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); + combo.setLayoutData(gd); + combo.setItems(items); + combo.setData(key); + fComboBoxes.add(combo); + + return combo; + } + + protected Text addTextField(Composite composite, String label, String key, int textLimit, int indentation, boolean isNumber) { + return getTextControl(addLabelledTextField(composite, label, key, textLimit, indentation, isNumber)); + } + +// private static Label getLabelControl(Control[] labelledTextField){ +// return (Label)labelledTextField[0]; +// } + + private static Text getTextControl(Control[] labelledTextField){ + return (Text)labelledTextField[1]; + } + + /** + * Returns an array of size 2: + * - first element is of type <code>Label</code> + * - second element is of type <code>Text</code> + * Use <code>getLabelControl</code> and <code>getTextControl</code> to get the 2 controls. + */ + private Control[] addLabelledTextField(Composite composite, String label, String key, int textLimit, int indentation, boolean isNumber) { + Label labelControl= new Label(composite, SWT.NONE); + labelControl.setText(label); + GridData gd= new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); + gd.horizontalIndent= indentation; + labelControl.setLayoutData(gd); + + Text textControl= new Text(composite, SWT.BORDER | SWT.SINGLE); + gd= new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); + gd.widthHint= convertWidthInCharsToPixels(textLimit + 1); + textControl.setLayoutData(gd); + textControl.setTextLimit(textLimit); + textControl.setData(key); + if (isNumber) { + fNumberFields.add(textControl); + textControl.addModifyListener(fNumberFieldListener); + } + + return new Control[]{labelControl, textControl}; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.preference.IPreferencePage#performOk() + */ + @Override + public boolean performOk() { + IPreferenceStore store = getPreferenceStore(); + for (Iterator<Button> iter = fCheckBoxes.iterator(); iter.hasNext();) { + Button btn = iter.next(); + store.setValue((String)btn.getData(), btn.getSelection()); + } + for (Iterator<Text> iter = fNumberFields.iterator(); iter.hasNext();) { + Text field = iter.next(); + store.setValue((String)field.getData(), Long.decode(field.getText()).longValue()); + } + for (Iterator<Combo> iter = fComboBoxes.iterator(); iter.hasNext();) { + Combo combo = iter.next(); + store.setValue((String)combo.getData(), fcRadixValues[combo.getSelectionIndex()]); + } + return super.performOk(); + } + /* (non-Javadoc) + * @see org.eclipse.jface.preference.PreferencePage#performDefaults() + */ + @Override + protected void performDefaults() { + IPreferenceStore store = getPreferenceStore(); + for (Iterator<Button> iter = fCheckBoxes.iterator(); iter.hasNext();) { + Button btn = iter.next(); + btn.setSelection(store.getDefaultBoolean((String)btn.getData())); + } + for (Iterator<Text> iter = fNumberFields.iterator(); iter.hasNext();) { + Text field = iter.next(); + long value = store.getDefaultLong((String)field.getData()); + field.setText("0x"+Long.toHexString(value)); //$NON-NLS-1$ + } + for (Iterator<Combo> iter = fComboBoxes.iterator(); iter.hasNext();) { + Combo combo = iter.next(); + int value = store.getDefaultInt((String)combo.getData()); + for (int i = 0; i < fcRadixValues.length; i++) { + if (fcRadixValues[i] == value) { + combo.select(i); + } + } + } + super.performDefaults(); + } + /** + * Initialize widget values from preference store. + */ + private void initialize() { + IPreferenceStore store = getPreferenceStore(); + for (Iterator<Button> iter = fCheckBoxes.iterator(); iter.hasNext();) { + Button btn = iter.next(); + btn.setSelection(store.getBoolean((String)btn.getData())); + } + for (Iterator<Text> iter = fNumberFields.iterator(); iter.hasNext();) { + Text field = iter.next(); + long value = store.getLong((String)field.getData()); + field.setText("0x"+Long.toHexString(value)); //$NON-NLS-1$ + } + for (Iterator<Combo> iter = fComboBoxes.iterator(); iter.hasNext();) { + Combo combo = iter.next(); + int value = store.getInt((String)combo.getData()); + for (int i = 0; i < fcRadixValues.length; i++) { + if (fcRadixValues[i] == value) { + combo.select(i); + break; + } + } + } + } + + /** + * @param text + */ + protected void numberFieldChanged(Text text) { + try { + long value = Long.decode(text.getText()).longValue(); + if (value < 0) { + setErrorMessage(DisassemblyMessages.DisassemblyPreferencePage_error_negative_number); + } else { + setErrorMessage(null); + } + } catch(NumberFormatException nfe) { + setErrorMessage(DisassemblyMessages.DisassemblyPreferencePage_error_not_a_number); + } + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/presentation/CSourcePresentationCreator.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/presentation/CSourcePresentationCreator.java new file mode 100644 index 00000000000..8fb4f3620f3 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/presentation/CSourcePresentationCreator.java @@ -0,0 +1,350 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.presentation; + +import org.eclipse.cdt.core.model.CModelException; +import org.eclipse.cdt.core.model.CoreModel; +import org.eclipse.cdt.core.model.IAsmLanguage; +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.core.model.ICModel; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.core.model.ILanguage; +import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.internal.ui.editor.CDocumentProvider; +import org.eclipse.cdt.internal.ui.editor.ITranslationUnitEditorInput; +import org.eclipse.cdt.internal.ui.text.CCommentScanner; +import org.eclipse.cdt.internal.ui.text.CTextTools; +import org.eclipse.cdt.internal.ui.text.ICColorConstants; +import org.eclipse.cdt.internal.ui.text.IColorManager; +import org.eclipse.cdt.internal.ui.text.SimpleCSourceViewerConfiguration; +import org.eclipse.cdt.internal.ui.text.TokenStore; +import org.eclipse.cdt.internal.ui.util.EditorUtility; +import org.eclipse.cdt.ui.CUIPlugin; +import org.eclipse.cdt.ui.text.ICPartitions; +import org.eclipse.cdt.ui.text.ITokenStore; +import org.eclipse.cdt.ui.text.ITokenStoreFactory; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFileState; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IStorage; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.Region; +import org.eclipse.jface.text.TextPresentation; +import org.eclipse.jface.text.presentation.PresentationReconciler; +import org.eclipse.jface.text.rules.ITokenScanner; +import org.eclipse.jface.text.rules.RuleBasedScanner; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.texteditor.IDocumentProvider; +import org.eclipse.ui.texteditor.ITextEditor; + +/** + * A presentation creator based on CDT syntax highlighting. + */ +@SuppressWarnings("restriction") +public class CSourcePresentationCreator extends PresentationReconciler implements ISourcePresentationCreator, IPropertyChangeListener { + + /** + * + */ + private final static class CustomCSourceViewerConfiguration extends SimpleCSourceViewerConfiguration { + /** + * Comment for <code>fLanguage</code> + */ + private final ILanguage fLanguage; + + /** + * @param colorManager + * @param preferenceStore + * @param language + */ + private CustomCSourceViewerConfiguration( + IColorManager colorManager, IPreferenceStore preferenceStore, + ILanguage language) { + super(colorManager, preferenceStore, null, ICPartitions.C_PARTITIONING, false); + fLanguage = language; + } + + public void dispose() { + } + + /* + * @see org.eclipse.cdt.internal.ui.text.CSourceViewerConfiguration#getLanguage() + */ + @Override + protected ILanguage getLanguage() { + return fLanguage; + } + + /** + * @param contentType + * @return + */ + public ITokenScanner getScannerForContentType(String contentType) { + if (IDocument.DEFAULT_CONTENT_TYPE.equals(contentType)) { + return getLanguage() != null ? getCodeScanner(getLanguage()) : null; + } else if (ICPartitions.C_CHARACTER.equals(contentType)) { + return getStringScanner(); + } else if (ICPartitions.C_STRING.equals(contentType)) { + return getStringScanner(); + } else if (ICPartitions.C_SINGLE_LINE_COMMENT.equals(contentType)) { + return getSinglelineCommentScanner(); + } else if (ICPartitions.C_SINGLE_LINE_DOC_COMMENT.equals(contentType)) { + return getSinglelineDocCommentScanner(getProject()); + } else if (ICPartitions.C_MULTI_LINE_COMMENT.equals(contentType)) { + return getMultilineCommentScanner(); + } else if (ICPartitions.C_MULTI_LINE_DOC_COMMENT.equals(contentType)) { + return getMultilineDocCommentScanner(getProject()); + } else if (ICPartitions.C_PREPROCESSOR.equals(contentType)) { + return getPreprocessorScanner(getLanguage()); + } + return null; + } + + private ITokenScanner getMultilineCommentScanner() { + return new CCommentScanner(getTokenStoreFactory(), ICColorConstants.C_SINGLE_LINE_COMMENT); + } + + private ITokenScanner getSinglelineCommentScanner() { + return new CCommentScanner(getTokenStoreFactory(), ICColorConstants.C_MULTI_LINE_COMMENT); + } + + /** + * Returns the ICProject associated with this CSourceViewerConfiguration, or null if + * no ICProject could be determined + * @return + */ + private ICProject internalGetCProject() { + ITextEditor editor= getEditor(); + if (editor == null) + return null; + + ICElement element= null; + IEditorInput input= editor.getEditorInput(); + IDocumentProvider provider= editor.getDocumentProvider(); + if (provider instanceof CDocumentProvider) { + CDocumentProvider cudp= (CDocumentProvider) provider; + element= cudp.getWorkingCopy(input); + } + + if (element == null) + return null; + + return element.getCProject(); + } + + + /** + * @return the IProject associated with this CSourceViewerConfiguration, or null if + * no IProject could be determined + */ + private IProject getProject() { + ICProject cproject= internalGetCProject(); + return cproject!=null ? cproject.getProject() :null; + } + + private ITokenStoreFactory getTokenStoreFactory() { + return new ITokenStoreFactory() { + public ITokenStore createTokenStore(String[] propertyColorNames) { + return new TokenStore(getColorManager(), fPreferenceStore, propertyColorNames); + } + }; + } + + /* + * @see org.eclipse.cdt.internal.ui.text.CSourceViewerConfiguration#getCodeScanner(org.eclipse.cdt.core.model.ILanguage) + */ + @Override + protected RuleBasedScanner getCodeScanner(ILanguage language) { + if (language instanceof IAsmLanguage) { + return CUIPlugin.getDefault().getAsmTextTools().getCodeScanner(); + } + return super.getCodeScanner(language); + } + + /* + * @see org.eclipse.cdt.internal.ui.text.CSourceViewerConfiguration#getPreprocessorScanner(org.eclipse.cdt.core.model.ILanguage) + */ + @Override + protected RuleBasedScanner getPreprocessorScanner(ILanguage language) { + if (language instanceof IAsmLanguage) { + return CUIPlugin.getDefault().getAsmTextTools().getPreprocessorScanner(); + } + return super.getPreprocessorScanner(language); + } + } + + private ITextViewer fViewer; + private ISourceTagProvider fSourceTagProvider; + private SourceTagDamagerRepairer fDamagerRepairer; + private ISourceTagListener fSourceTagListener; + private TextPresentation fPresentation; + private CustomCSourceViewerConfiguration fSourceViewerConfiguration; + private IPreferenceStore fPreferenceStore; + + /** + * @param language + * @param storage + * @param textViewer + */ + public CSourcePresentationCreator(ILanguage language, IStorage storage, ITextViewer textViewer) { + if (language != null) { + fViewer= textViewer; + fPreferenceStore= CUIPlugin.getDefault().getCombinedPreferenceStore(); + CTextTools textTools = CUIPlugin.getDefault().getTextTools(); + fSourceViewerConfiguration= new CustomCSourceViewerConfiguration(textTools.getColorManager(), fPreferenceStore, language); + setDocumentPartitioning(fSourceViewerConfiguration.getConfiguredDocumentPartitioning(null)); + initializeDamagerRepairer(storage, textTools.getColorManager(), fPreferenceStore); + fPreferenceStore.addPropertyChangeListener(this); + } + } + + private void initializeDamagerRepairer(IStorage storage, IColorManager colorManager, IPreferenceStore store) { + String[] contentTypes= fSourceViewerConfiguration.getConfiguredContentTypes(null); + for (int i = 0; i < contentTypes.length; ++i) { + String contentType = contentTypes[i]; + ITokenScanner scanner; + scanner = fSourceViewerConfiguration.getScannerForContentType(contentType); + if (scanner != null) { + if (fDamagerRepairer == null) { + fSourceTagProvider = createSourceTagProvider(storage); + fDamagerRepairer= new SourceTagDamagerRepairer(scanner, fSourceTagProvider, colorManager, store); + if (fSourceTagProvider != null) { + if (fSourceTagListener == null) { + fSourceTagListener= new ISourceTagListener() { + public void sourceTagsChanged(ISourceTagProvider provider) { + handleSourceTagsChanged(); + }}; + } + fSourceTagProvider.addSourceTagListener(fSourceTagListener); + } + } + fDamagerRepairer.setScanner(contentType, scanner); + setDamager(fDamagerRepairer, contentType); + setRepairer(fDamagerRepairer, contentType); + } + } + } + + /* + * @see org.eclipse.cdt.dsf.debug.internal.ui.disassembly.presentation.ISourcePresentationCreator#dispose() + */ + public void dispose() { + fViewer= null; + fPresentation= null; + if (fPreferenceStore != null) { + fPreferenceStore.removePropertyChangeListener(this); + fPreferenceStore= null; + } + if (fSourceViewerConfiguration != null) { + fSourceViewerConfiguration.dispose(); + fSourceViewerConfiguration= null; + } + if (fSourceTagProvider != null) { + if (fSourceTagListener != null) { + fSourceTagProvider.removeSourceTagListener(fSourceTagListener); + fSourceTagListener= null; + } + fSourceTagProvider= null; + } + } + + /* + * @see org.eclipse.cdt.dsf.debug.internal.ui.disassembly.presentation.ISourcePresentationCreator#getPresentation(org.eclipse.jface.text.IRegion, org.eclipse.jface.text.IDocument) + */ + public TextPresentation getPresentation(IRegion region, IDocument document) { + assert fViewer != null; + if (fViewer == null) { + return null; + } + if (fPresentation == null) { + setDocumentToDamagers(document); + setDocumentToRepairers(document); + int docLength= document.getLength(); + if (docLength <= 128*1024) { + IRegion all= new Region(0, docLength); + fPresentation= createPresentation(all, document); + } else { + return createPresentation(region, document); + } + } + fPresentation.setResultWindow(region); + return fPresentation; + } + + protected void handleSourceTagsChanged() { + invalidateTextPresentation(); + } + + private void invalidateTextPresentation() { + if (fPresentation != null) { + fPresentation= null; + if (fViewer != null) { + Display display= fViewer.getTextWidget().getDisplay(); + if (display.getThread() != Thread.currentThread()) { + display.asyncExec(new Runnable() { + public void run() { + if (fViewer != null) { + fViewer.invalidateTextPresentation(); + } + }}); + } else { + fViewer.invalidateTextPresentation(); + } + } + } + } + + private ISourceTagProvider createSourceTagProvider(IStorage storage) { + ITranslationUnit tUnit= null; + if (storage instanceof IFile) { + tUnit= (ITranslationUnit) CoreModel.getDefault().create((IFile)storage); + } else if (storage instanceof IFileState) { + ICModel cModel= CoreModel.getDefault().getCModel(); + ICProject[] cProjects; + try { + cProjects = cModel.getCProjects(); + if (cProjects.length > 0) { + tUnit= CoreModel.getDefault().createTranslationUnitFrom(cProjects[0], storage.getFullPath()); + } + } catch (CModelException e) { + } + } else { + IEditorInput input= EditorUtility.getEditorInputForLocation(storage.getFullPath(), null); + if (input instanceof ITranslationUnitEditorInput) { + tUnit= ((ITranslationUnitEditorInput)input).getTranslationUnit(); + } + } + if (tUnit != null) { + return new CSourceTagProvider(tUnit); + } + return null; + } + + /* + * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent) + */ + public void propertyChange(PropertyChangeEvent event) { + if (fSourceViewerConfiguration.affectsBehavior(event)) { + fSourceViewerConfiguration.handlePropertyChangeEvent(event); + invalidateTextPresentation(); + } else if (fDamagerRepairer.affectsBahvior(event)) { + fDamagerRepairer.handlePropertyChangeEvent(event); + invalidateTextPresentation(); + } + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/presentation/CSourceTag.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/presentation/CSourceTag.java new file mode 100644 index 00000000000..9cfd0021933 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/presentation/CSourceTag.java @@ -0,0 +1,207 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.presentation; + +import org.eclipse.cdt.core.model.CModelException; +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.core.model.ISourceReference; + +/** + * A source tag based on C Model elements. + */ +public class CSourceTag implements ISourceTag { + + /** + * The zero-length source range. + */ + public class NullRange implements ISourceRange { + + public boolean contains(int offset) { + return false; + } + + public int getBeginOffset() { + return 0; + } + + public int getEndOffset() { + return -1; + } + + public int compareTo(ISourceRange other) { + if (this == other) { + return 0; + } + return -1; + } + + } + + /** + * The source range. + */ + public class CSourceRange implements ISourceRange { + + private org.eclipse.cdt.core.model.ISourceRange fRange; + + /** + * @param sourceRange + */ + public CSourceRange(org.eclipse.cdt.core.model.ISourceRange sourceRange) { + fRange= sourceRange; + } + + public boolean contains(int offset) { + return fRange.getStartPos() <= offset && offset - fRange.getStartPos() < fRange.getLength(); + } + + public int getBeginOffset() { + return fRange.getStartPos(); + } + + public int getEndOffset() { + return fRange.getStartPos() + fRange.getLength() - 1; + } + + public int compareTo(ISourceRange other) { + int delta= this.getBeginOffset() - other.getBeginOffset(); + if (delta == 0) { + delta= this.getEndOffset() - other.getEndOffset(); + } + return delta; + } + + } + + /** + * The identifier range. + */ + public class CIdentifierRange implements ISourceRange { + + private org.eclipse.cdt.core.model.ISourceRange fRange; + + public CIdentifierRange(org.eclipse.cdt.core.model.ISourceRange sourceRange) { + fRange= sourceRange; + } + + public boolean contains(int offset) { + return fRange.getIdStartPos() <= offset && offset - fRange.getIdStartPos() < fRange.getIdLength(); + } + + public int getBeginOffset() { + return fRange.getIdStartPos(); + } + + public int getEndOffset() { + return fRange.getIdStartPos() + fRange.getIdLength() - 1; + } + + public int compareTo(ISourceRange other) { + int delta= this.getBeginOffset() - other.getBeginOffset(); + if (delta == 0) { + delta= this.getEndOffset() - other.getEndOffset(); + } + return delta; + } + + } + + private ISourceReference fReference; + private int fType; + + /** + * Create a new source tag for the given element and type. + * + * @param element + * @param elementType + */ + public CSourceTag(ISourceReference element, int elementType) { + fReference= element; + fType= elementType; + } + + public ISourceRange getFullRange() { + try { + return new CSourceRange(fReference.getSourceRange()); + } catch (CModelException e) { + } + return new NullRange(); + } + + public String getName() { + return ((ICElement)fReference).getElementName(); + } + + public String getQualifiedName() { + return getName(); + } + + public ISourceRange getRangeOfIdentifier() { + try { + return new CIdentifierRange(fReference.getSourceRange()); + } catch (CModelException e) { + } + return new NullRange(); + } + + public long getSnapshotTime() { + return 0; + } + + public int getStyleCode() { + switch (fType) { + case ICElement.C_METHOD : + case ICElement.C_METHOD_DECLARATION: + case ICElement.C_TEMPLATE_METHOD: + case ICElement.C_TEMPLATE_METHOD_DECLARATION: + return ISourceTag.STYLE_Method; + case ICElement.C_FUNCTION: + case ICElement.C_FUNCTION_DECLARATION: + case ICElement.C_TEMPLATE_FUNCTION: + case ICElement.C_TEMPLATE_FUNCTION_DECLARATION: + return ISourceTag.STYLE_Function; + case ICElement.C_FIELD : + return ISourceTag.STYLE_MemberVariable; + case ICElement.C_VARIABLE: + case ICElement.C_VARIABLE_DECLARATION: + return ISourceTag.STYLE_Variable; + case ICElement.C_CLASS: + case ICElement.C_TEMPLATE_CLASS: + case ICElement.C_TEMPLATE_CLASS_DECLARATION: + return ISourceTag.STYLE_Class; + case ICElement.C_STRUCT: + case ICElement.C_TEMPLATE_STRUCT: + case ICElement.C_TEMPLATE_STRUCT_DECLARATION: + return ISourceTag.STYLE_Struct; + case ICElement.C_UNION: + case ICElement.C_TEMPLATE_UNION: + case ICElement.C_TEMPLATE_UNION_DECLARATION: + return ISourceTag.STYLE_Union; + case ICElement.C_ENUMERATION: + return ISourceTag.STYLE_Enumeration; + case ICElement.C_ENUMERATOR: + return ISourceTag.STYLE_Enumerator; + case ICElement.C_NAMESPACE: + return ISourceTag.STYLE_None; + case ICElement.C_TYPEDEF: + return ISourceTag.STYLE_Typedef; + case ICElement.C_MACRO: + return ISourceTag.STYLE_Macro; + default: + return ISourceTag.STYLE_None; + } + } + + public ISourceTag getSourceTagAdapter() { + return this; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/presentation/CSourceTagProvider.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/presentation/CSourceTagProvider.java new file mode 100644 index 00000000000..3d90ad3dc92 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/presentation/CSourceTagProvider.java @@ -0,0 +1,95 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.presentation; + +import java.util.Collection; + +import org.eclipse.cdt.core.model.CModelException; +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.core.model.IParent; +import org.eclipse.cdt.core.model.ISourceReference; +import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.core.runtime.ListenerList; + +/** + * A source tag provider based on the C Model. + */ +public class CSourceTagProvider implements ISourceTagProvider { + + private ListenerList fListenerList= new ListenerList(ListenerList.IDENTITY); + private ITranslationUnit fUnit; + + /** + * Create a new source tag provider for the given translation unit. + * + * @param unit + */ + public CSourceTagProvider(ITranslationUnit unit) { + fUnit= unit; + } + + public void addSourceTagListener(ISourceTagListener listener) { + fListenerList.add(listener); + } + + public int[] getActiveCodePositions() { + // unsupported + return null; + } + + public long getSnapshotTime() { + return 0; + } + + public void getSourceTags(Collection<ISourceTag> target) { + try { + convertToSourceTags(fUnit.getChildren(), target); + } catch (CModelException e) { + } + } + + /** + * @param element + * @return + */ + private ISourceTag convertToSourceTag(ICElement element) { + if (element instanceof ISourceReference) { + return new CSourceTag((ISourceReference)element, element.getElementType()); + } + return null; + } + + /** + * @param children + * @param target + * @throws CModelException + */ + private void convertToSourceTags(ICElement[] children, Collection<ISourceTag> target) throws CModelException { + for (int i = 0; i < children.length; i++) { + ICElement element= children[i]; + ISourceTag tag= convertToSourceTag(element); + if (tag != null) { + target.add(tag); + } + if (element instanceof IParent) { + convertToSourceTags(((IParent)element).getChildren(), target); + } + } + } + + /* + * @see org.eclipse.cdt.dsf.debug.internal.ui.disassembly.presentation.ISourceTagProvider#removeSourceTagListener(org.eclipse.cdt.dsf.debug.internal.ui.disassembly.presentation.ISourceTagListener) + */ + public void removeSourceTagListener(ISourceTagListener listener) { + fListenerList.remove(listener); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/presentation/DisassemblyIPAnnotation.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/presentation/DisassemblyIPAnnotation.java new file mode 100644 index 00000000000..27e6dca2a36 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/presentation/DisassemblyIPAnnotation.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.internal.ui.disassembly.presentation; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.DisassemblyMessages; +import org.eclipse.debug.ui.DebugUITools; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.jface.text.source.Annotation; +import org.eclipse.jface.text.source.IAnnotationPresentation; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Canvas; + +/** + * DisassemblyIPAnnotation + */ +public class DisassemblyIPAnnotation extends Annotation implements IAnnotationPresentation { + + public static final String ID_TOP = "org.eclipse.cdt.dsf.debug.currentIP"; //$NON-NLS-1$ + public static final String ID_SECONDARY = "org.eclipse.cdt.dsf.debug.secondaryIP"; //$NON-NLS-1$ + + private Image fImage; + private int fContext = Integer.MIN_VALUE; + + /** + * Annotation denoting the current instruction pointer. + */ + public DisassemblyIPAnnotation(boolean isTopFrame, int context) { + super( + isTopFrame ? ID_TOP : ID_SECONDARY, + false, + isTopFrame ? DisassemblyMessages.DisassemblyIPAnnotation_primary + : DisassemblyMessages.DisassemblyIPAnnotation_secondary + ); + setContext(context); + } + + public boolean isTopFrame() { + return ID_TOP.equals(getType()); + } + + public void setContext(int context) { + if (context == fContext) { + return; + } + fContext = context; + // TLETODO [disassembly] context dependent IP icon + if (isTopFrame()) { + fImage = DebugUITools.getImage(IDebugUIConstants.IMG_OBJS_INSTRUCTION_POINTER_TOP); + } else { + fImage = DebugUITools.getImage(IDebugUIConstants.IMG_OBJS_INSTRUCTION_POINTER); + } + } + + /* + * @see org.eclipse.jface.text.source.IAnnotationPresentation#getLayer() + */ + public int getLayer() { + return 5; + } + + /* + * @see org.eclipse.jface.text.source.IAnnotationPresentation#paint(org.eclipse.swt.graphics.GC, org.eclipse.swt.widgets.Canvas, org.eclipse.swt.graphics.Rectangle) + */ + public void paint(GC gc, Canvas canvas, Rectangle bounds) { + Rectangle imageBounds = fImage.getBounds(); + gc.drawImage(fImage, bounds.x + (bounds.width - imageBounds.width) / 2 , bounds.y + (bounds.height - imageBounds.height) / 2); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/presentation/ISourcePresentationCreator.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/presentation/ISourcePresentationCreator.java new file mode 100644 index 00000000000..c174ca3d972 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/presentation/ISourcePresentationCreator.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.presentation; + +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.TextPresentation; + +/** + * A source presentation creator is used to create a {@link TextPresentation} of a document range. + */ +public interface ISourcePresentationCreator { + + /** + * Dispose of this presentation creator. + */ + public abstract void dispose(); + + /** + * Get a text presentation for the given region and document. + * @param region + * @param document + * @return a text presentation + */ + public abstract TextPresentation getPresentation(IRegion region, IDocument document); + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/presentation/ISourceRange.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/presentation/ISourceRange.java new file mode 100644 index 00000000000..6e4e71d5c8d --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/presentation/ISourceRange.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.presentation; + +/** + * Represents a range within a source file. + */ + +public interface ISourceRange extends Comparable<ISourceRange> { + /** + * Returns 0-based absolute number for the inclusive start of the range. + */ + int getBeginOffset(); + /** + * Returns 0-based absolute number for the inclusive end of the range. + */ + int getEndOffset(); + /** + * Checks whether the range contains the given offset. + */ + boolean contains(int offset); +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/presentation/ISourceTag.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/presentation/ISourceTag.java new file mode 100644 index 00000000000..4232362c241 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/presentation/ISourceTag.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.presentation; + +/** + * Specifies the style of part of some text source. + */ +public interface ISourceTag { + // style codes + final static int STYLE_None = 0; + final static int STYLE_Class = 1; + final static int STYLE_Struct = 2; + final static int STYLE_Union = 3; + final static int STYLE_Interface = 4; + final static int STYLE_Package = 5; + final static int STYLE_Function = 6; + final static int STYLE_ProtectedFunction = 7; + final static int STYLE_Method = 8; + final static int STYLE_Exception = 9; + final static int STYLE_Variable = 10; + final static int STYLE_MemberVariable = 11; + final static int STYLE_Enumerator = 12; + final static int STYLE_Macro = 13; + final static int STYLE_Include = 14; + final static int STYLE_Undefined = 15; + final static int STYLE_Enumeration = 16; + final static int STYLE_Typedef = 17; + final static int STYLE_Type3 = 18; + final static int STYLE_Type4 = 19; + final static int STYLE_Type5 = 20; + final static int STYLE_File = 21; + final static int STYLE_Project = 22; + final static int STYLE_IncludeContainer = 23; + final static int STYLE_LocalVariable = 24; + final static int STYLE_Label = 25; + final static int STYLE_Record = 26; + final static int STYLE_TaggedType = 27; + final static int STYLE_Subtype = 28; + final static int STYLE_Warning = 29; + final static int STYLE_Count = 30; + + /** + * Returns the unqualified name of the source tag. Files return their base name. + */ + String getName(); + + /** + * Returns the fully qualified name of the source tag. Files return their path. + */ + String getQualifiedName(); + + /** + * Returns the range of the symbol within the file. + */ + ISourceRange getFullRange(); + + /** + * Returns the range of the identifier of the symbol within the file. + */ + ISourceRange getRangeOfIdentifier(); + + /** + * Computes the style code. Style codes are language dependent. You + * cannot derive any information from the style-code of a symbol. It + * may only be used to influence the visualization of a symbol. You + * may select color, font or icon depending on the style code. + * @return the style code of the symbol + */ + int getStyleCode(); + + /** + * Returns the timestamp of the file at the time the sourcetag was generated. + */ + long getSnapshotTime(); +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/presentation/ISourceTagListener.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/presentation/ISourceTagListener.java new file mode 100644 index 00000000000..f471cf862ae --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/presentation/ISourceTagListener.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.presentation; + +/** + * ISourceTagListener + */ +public interface ISourceTagListener { + + /** + * Notifies this listener that the source tags have changed. + * @param provider the provider generating this event. + */ + void sourceTagsChanged(ISourceTagProvider provider); + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/presentation/ISourceTagProvider.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/presentation/ISourceTagProvider.java new file mode 100644 index 00000000000..08b5b9a0d3e --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/presentation/ISourceTagProvider.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.presentation; + +import java.util.Collection; + +/** + * A source tag provider provides access to source tags. + */ +public interface ISourceTagProvider { + + /** + * Add a source tag listener to receive source tag changed notifications. + * @param listener + */ + public void addSourceTagListener(ISourceTagListener listener); + + /** + * Remove a source tag listener to stop receiving source tag changed notifications. + * @param listener + */ + public void removeSourceTagListener(ISourceTagListener listener); + + /** + * Retrieves all symbols of the current file. + */ + public void getSourceTags(Collection<ISourceTag> target); + + /** + * Get the time stamp of the current symbol content. + * @return the modification time of the source file or 0L if no symbols available. + */ + public long getSnapshotTime(); + + /** + * Retrieves the active code positions of the current file. Null if the + * information cannot be obtained. + */ + public int[] getActiveCodePositions(); + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/presentation/SourcePresentationCreatorFactory.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/presentation/SourcePresentationCreatorFactory.java new file mode 100644 index 00000000000..fe06d6ec698 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/presentation/SourcePresentationCreatorFactory.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.presentation; + +import org.eclipse.cdt.core.model.ILanguage; +import org.eclipse.core.resources.IStorage; +import org.eclipse.jface.text.ITextViewer; + +/** + * A factory for source presentation creators. + */ +public final class SourcePresentationCreatorFactory { + + public static ISourcePresentationCreator create(ILanguage language, IStorage storage, ITextViewer textViewer) { + return new CSourcePresentationCreator(language, storage, textViewer); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/presentation/SourceTagDamagerRepairer.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/presentation/SourceTagDamagerRepairer.java new file mode 100644 index 00000000000..5aa049b3856 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/presentation/SourceTagDamagerRepairer.java @@ -0,0 +1,375 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.presentation; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.cdt.internal.ui.editor.SemanticHighlightings; +import org.eclipse.cdt.internal.ui.text.IColorManager; +import org.eclipse.cdt.internal.ui.text.IColorManagerExtension; +import org.eclipse.cdt.ui.PreferenceConstants; +import org.eclipse.cdt.ui.text.ICPartitions; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.preference.PreferenceConverter; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.ITypedRegion; +import org.eclipse.jface.text.TextAttribute; +import org.eclipse.jface.text.TextPresentation; +import org.eclipse.jface.text.rules.DefaultDamagerRepairer; +import org.eclipse.jface.text.rules.IToken; +import org.eclipse.jface.text.rules.ITokenScanner; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StyleRange; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.RGB; + +/** + * + */ +@SuppressWarnings("restriction") +public class SourceTagDamagerRepairer extends DefaultDamagerRepairer implements ISourceTagListener { + + private ISourceTagProvider fSourceTagProvider; + private Map<String, ITokenScanner> fScannerMap= new HashMap<String, ITokenScanner>(); + private List<ISourceTag> fSourceTags = new ArrayList<ISourceTag>(); + private IColorManager fColorManager; + private IPreferenceStore fPreferenceStore; + private Map<String, TextAttribute> fAttributeMap= new HashMap<String, TextAttribute>(); + + private final static String[] KEYS= { + SemanticHighlightings.CLASS, + SemanticHighlightings.METHOD_DECLARATION, + SemanticHighlightings.FUNCTION_DECLARATION, + SemanticHighlightings.FIELD, + SemanticHighlightings.GLOBAL_VARIABLE, + SemanticHighlightings.TYPEDEF, + SemanticHighlightings.MACRO_DEFINITION, + SemanticHighlightings.ENUMERATOR, + SemanticHighlightings.ENUM, + }; + + /** + * @param scanner + * @param sourceTagProvider + */ + public SourceTagDamagerRepairer(ITokenScanner scanner, ISourceTagProvider sourceTagProvider, IColorManager colorManager, IPreferenceStore store) { + super(scanner); + fSourceTagProvider= sourceTagProvider; + fColorManager= colorManager; + fPreferenceStore= store; + fDefaultTextAttribute = new TextAttribute(null, null, SWT.NORMAL); + if (fSourceTagProvider != null) { + fSourceTagProvider.addSourceTagListener(this); + sourceTagsChanged(fSourceTagProvider); + } + } + + private void initTextAttributes() { + boolean shEnabled= fPreferenceStore.getBoolean(PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_ENABLED); + for (int i= 0; i < KEYS.length; i++) { + String enabledKey= PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_PREFIX + KEYS[i] + PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_ENABLED_SUFFIX; + String colorKey= PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_PREFIX + KEYS[i] + PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_COLOR_SUFFIX; + boolean enabled= shEnabled && fPreferenceStore.getBoolean(enabledKey); + if (enabled) { + String boldKey= PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_PREFIX + KEYS[i] + PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_BOLD_SUFFIX; + String italicKey= PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_PREFIX + KEYS[i] + PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_ITALIC_SUFFIX; + String strikethroughKey= PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_PREFIX + KEYS[i] + PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_STRIKETHROUGH_SUFFIX; + String underlineKey= PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_PREFIX + KEYS[i] + PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_UNDERLINE_SUFFIX; + addTextAttribute(KEYS[i], colorKey, boldKey, italicKey, strikethroughKey, underlineKey); + } else { + removeTextAttribute(KEYS[i], colorKey); + } + } + } + + private void removeTextAttribute(String key, String colorKey) { + if (fColorManager != null && colorKey != null) { + Color color= fColorManager.getColor(colorKey); + if (fColorManager instanceof IColorManagerExtension && color != null) { + IColorManagerExtension ext= (IColorManagerExtension) fColorManager; + ext.unbindColor(colorKey); + } + } + + fAttributeMap.remove(key); + } + + private void addTextAttribute(String key, String colorKey, String boldKey, String italicKey, String strikethroughKey, String underlineKey) { + if (fColorManager != null && colorKey != null && fColorManager instanceof IColorManagerExtension) { + RGB rgb= PreferenceConverter.getColor(fPreferenceStore, colorKey); + Color color= fColorManager.getColor(colorKey); + if (color == null || !rgb.equals(color.getRGB())) { + IColorManagerExtension ext= (IColorManagerExtension) fColorManager; + ext.unbindColor(colorKey); + ext.bindColor(colorKey, rgb); + } + } + + TextAttribute textAttribute= createTextAttribute(colorKey, boldKey, italicKey, strikethroughKey, underlineKey); + fAttributeMap.put(key, textAttribute); + } + + private TextAttribute createTextAttribute(String colorKey, String boldKey, String italicKey, String strikethroughKey, String underlineKey) { + Color color= null; + if (colorKey != null) + color= fColorManager.getColor(colorKey); + + int style= fPreferenceStore.getBoolean(boldKey) ? SWT.BOLD : SWT.NORMAL; + if (fPreferenceStore.getBoolean(italicKey)) + style |= SWT.ITALIC; + + if (fPreferenceStore.getBoolean(strikethroughKey)) + style |= TextAttribute.STRIKETHROUGH; + + if (fPreferenceStore.getBoolean(underlineKey)) + style |= TextAttribute.UNDERLINE; + + return new TextAttribute(color, null, style); + } + + /** + * Set scanner for contentType. + * @param contentType + * @param scanner + */ + public void setScanner(String contentType, ITokenScanner scanner) { + fScannerMap.put(contentType, scanner); + } + + /* + * @see org.eclipse.jface.text.presentation.IPresentationRepairer#createPresentation(org.eclipse.jface.text.TextPresentation, org.eclipse.jface.text.ITypedRegion) + */ + @Override + public void createPresentation(TextPresentation presentation, ITypedRegion region) { + if (fAttributeMap.isEmpty()) { + initTextAttributes(); + } + String contentType= region.getType(); + fScanner = fScannerMap.get(contentType); + if (!contentType.equals(IDocument.DEFAULT_CONTENT_TYPE) && !contentType.equals(ICPartitions.C_PREPROCESSOR)) { + super.createPresentation(presentation, region); + return; + } + if (fScanner == null) { + return; + } + + int lastStart = region.getOffset(); + int regionEnd = lastStart + region.getLength(); + int length = 0; + + int sourceTagCount = fSourceTags.size(); + int sourceTagIdx = 0; + + ISourceTag sourceTag = null; + ISourceRange range = null; + int sourceTagStart = 0; + int sourceTagEnd = 0; + + if (sourceTagCount > 0 && fDocument.getLength() > 0) { + int left = 0; + int mid = (int) (sourceTagCount * ((float)lastStart / fDocument.getLength())); + int right = sourceTagCount - 1; + while (true) { + sourceTag = fSourceTags.get(mid); + range = sourceTag.getRangeOfIdentifier(); + sourceTagStart = range.getBeginOffset(); + sourceTagEnd = range.getEndOffset() + 1; + if (mid == left) { + break; + } else if (mid < right && sourceTagEnd < lastStart) { + left = mid; + mid = (mid + right) / 2; + } else if (sourceTagStart >= regionEnd) { + right = mid; + mid = (left + mid) / 2; + } else if (sourceTagStart > lastStart) { + --mid; + right = mid; + } else { + break; + } + } + // set to next index + sourceTagIdx = mid + 1; + } + + TextAttribute lastAttribute = fDefaultTextAttribute; + + fScanner.setRange(fDocument, lastStart, region.getLength()); + + while (true) { + IToken token = fScanner.nextToken(); + + // if the attribute is the same as the previous, extend range and continue + TextAttribute attribute = getTokenTextAttribute(token); + int tokenLength = fScanner.getTokenLength(); + if (tokenLength > 0 + && (lastAttribute == attribute || lastAttribute != null && lastAttribute.equals(attribute))) { + length += tokenLength; + continue; + } + // attribute has changed, now add the style range + while (sourceTag != null && length > 0) { + if (sourceTagStart >= regionEnd) { + // we are past the region boundary -> no more source tags + sourceTag = null; + break; + } + if (sourceTagStart >= lastStart) { + if (sourceTagStart < lastStart + length) { + String sourceTagStyle = getSourceTagStyle(sourceTag.getStyleCode()); + if (sourceTagStyle != null) { + if (sourceTagStart > lastStart) { + addRange(presentation, lastStart, Math.min(sourceTagStart - lastStart, length), lastAttribute); + } + int rangeEnd = Math.min(sourceTagEnd, regionEnd); + addRange( + presentation, + sourceTagStart, + rangeEnd - sourceTagStart, + getSourceTagTextAttribute(sourceTagStyle)); + length = lastStart + length - rangeEnd; + lastStart = rangeEnd; + } else { + fSourceTags.remove(--sourceTagIdx); + --sourceTagCount; + } + } else { + break; + } + } + sourceTag = sourceTagIdx < sourceTagCount ? fSourceTags.get(sourceTagIdx++) : null; + if (sourceTag != null) { + range = sourceTag.getRangeOfIdentifier(); + sourceTagStart = range.getBeginOffset(); + sourceTagEnd = range.getEndOffset() + 1; + } + } + if (token.isEOF()) { + break; + } + + if (length > 0) { + addRange(presentation, lastStart, length, lastAttribute); + lastAttribute = attribute; + lastStart = fScanner.getTokenOffset(); + length = tokenLength; + } else { + lastAttribute = attribute; + length = fScanner.getTokenOffset() - lastStart + tokenLength; + } + + } + + addRange(presentation, lastStart, length, lastAttribute); + } + + /** + * @param sourceTagStyle + * @return + */ + private TextAttribute getSourceTagTextAttribute(String sourceTagStyle) { + return fAttributeMap.get(sourceTagStyle); + } + + /** + * Get the style id for a source tag style code. + * @param styleCode + * @return the associated style id or <code>null</code> + */ + private String getSourceTagStyle(int styleCode) { + switch (styleCode) { + case ISourceTag.STYLE_None : + return null; + case ISourceTag.STYLE_Class : + return SemanticHighlightings.CLASS; + case ISourceTag.STYLE_Struct : + return SemanticHighlightings.CLASS; + case ISourceTag.STYLE_Union : + return SemanticHighlightings.CLASS; + case ISourceTag.STYLE_Function : + return SemanticHighlightings.FUNCTION_DECLARATION; + case ISourceTag.STYLE_Method : + return SemanticHighlightings.METHOD_DECLARATION; + case ISourceTag.STYLE_Variable : + return SemanticHighlightings.GLOBAL_VARIABLE; + case ISourceTag.STYLE_MemberVariable : + return SemanticHighlightings.FIELD; + case ISourceTag.STYLE_Enumerator : + return SemanticHighlightings.ENUMERATOR; + case ISourceTag.STYLE_Macro : + return SemanticHighlightings.MACRO_DEFINITION; + case ISourceTag.STYLE_Include : + // include is colored by the scanner + return null; + case ISourceTag.STYLE_Enumeration : + return SemanticHighlightings.ENUM; + case ISourceTag.STYLE_Undefined : + return null; + case ISourceTag.STYLE_Typedef : + return SemanticHighlightings.TYPEDEF; + default : + return null; + } + } + + public void sourceTagsChanged(ISourceTagProvider sourceTagProvider) { + fSourceTags.clear(); + if (sourceTagProvider != null) { + sourceTagProvider.getSourceTags(fSourceTags); + Collections.sort(fSourceTags, new Comparator<Object>() { + public int compare(Object o1, Object o2) { + ISourceRange sr1 = ((ISourceTag)o1).getRangeOfIdentifier(); + ISourceRange sr2 = ((ISourceTag)o2).getRangeOfIdentifier(); + return (sr1.getBeginOffset() - sr2.getBeginOffset()); + } + }); + } + } + + /* + * @see org.eclipse.jface.text.rules.DefaultDamagerRepairer#addRange(org.eclipse.jface.text.TextPresentation, int, int, org.eclipse.jface.text.TextAttribute) + */ + @Override + protected void addRange(TextPresentation presentation, int offset, int length, TextAttribute attr) { + if (length > 0 && attr != null) { + presentation.addStyleRange( + new StyleRange(offset, length, attr.getForeground(), attr.getBackground(), attr.getStyle())); + } + } + + /** + * Test whether the given preference change affects us. + * + * @param event + * @return <code>true</code> if the given event affects the behavior. + */ + public boolean affectsBahvior(PropertyChangeEvent event) { + return event.getProperty().startsWith(PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_PREFIX); + } + + /** + * Adapt to changes in the preferences. + * + * @param event + */ + public void handlePropertyChangeEvent(PropertyChangeEvent event) { + initTextAttributes(); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/text/IFileRider.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/text/IFileRider.java new file mode 100644 index 00000000000..80a36ad0d58 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/text/IFileRider.java @@ -0,0 +1,119 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.text; + +import java.io.IOException; + +/** + * IFileRider + */ +public interface IFileRider { + + /** Set rider to position + * @param pos is normalized to be in range [0, f.length()] + */ + public abstract void seek(int pos) throws IOException; + + /** + * Write a char. + * @param c + */ + public abstract void writeChar(char c) throws IOException; + + /** + * Write a character array. + * @param buf + * @throws IOException + */ + public abstract void writeChars(char[] buf) throws IOException; + + /** + * Write n characters of an array of characters. + * @param buf + * @param n + * @throws IOException + */ + public abstract void writeChars(char[] buf, int n) throws IOException; + + /** + * Write n characters of an array of characters starting at an offset. + * @param buf + * @param off + * @param n + * @throws IOException + */ + public abstract void writeChars(char[] buf, int off, int n) throws IOException; + + /** + * Write n characters of a String starting at an offset. + * @param buf + * @param off + * @param n + * @throws IOException + */ + public abstract void writeChars(String buf, int off, int n) throws IOException; + + /** + * Read next character. + * @return next char in buffer. + * @throws IOException + */ + public abstract char readChar() throws IOException; + + /** + * Read as much characters as possible into a char array. + * @param buf + * @throws IOException + */ + public abstract void readChars(char[] buf) throws IOException; + + /** + * Read n characters into character array. + * @param buf + * @param n + * @throws IOException + */ + public abstract void readChars(char[] buf, int n) throws IOException; + + /** + * Read n characters into char array. + * @param buf + * @param off + * @param n + * @throws IOException + */ + public abstract void readChars(char[] buf, int off, int n) throws IOException; + + /** + * Read n characters into StringBuffer. + * @param buf + * @param from + * @param n + * @throws IOException + */ + public abstract void readChars(StringBuffer buf, int n) throws IOException; + + /** + * @return length of file + */ + public abstract int length(); + + /** + * @return length limit of file + */ + public abstract int limit(); + + /** + * @return whether this rider is readonly or not + */ + public abstract boolean isReadonly(); + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/text/REDDocument.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/text/REDDocument.java new file mode 100644 index 00000000000..21d8a2b8e2f --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/text/REDDocument.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.text; + +import org.eclipse.jface.text.AbstractDocument; +import org.eclipse.jface.text.DefaultLineTracker; +import org.eclipse.jface.text.ITextStore; + +/** + * Standard Document implementation with REDTextStore (splice texts) + * as text storage. + */ +public class REDDocument extends AbstractDocument { + + public REDDocument() { + setTextStore(new REDTextStore()); + setLineTracker(new DefaultLineTracker()); + completeInitialization(); + } + + @Override + protected void finalize() { + dispose(); + } + + /** + * Free text store (delete scratchfiles). + */ + public void dispose() { + ITextStore store = getStore(); + if (store instanceof REDTextStore) { + ((REDTextStore)store).dispose(); + setTextStore(new StringTextStore()); + getTracker().set(""); //$NON-NLS-1$ + } + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/text/REDFile.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/text/REDFile.java new file mode 100644 index 00000000000..1f76daba692 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/text/REDFile.java @@ -0,0 +1,480 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.text; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; + +/** + * File with buffered character access. + */ +public final class REDFile { + + /** + * File cache object. + * TLETODO Use CharBuffer? + * @invariant fSize <= fcBufSize + * @invariant fSize >= 0 + * @invariant fOffset <= fFile.length() + */ + private final static class Buffer { + + final static int fcBufSize = 2048; + + boolean fDirty; + int fOffset = -1; + int fPos; + int fSize; + char fData[] = new char[fcBufSize]; + + Buffer() { + } + + /** + * Check if the file offset is contained in this buffer. + * @param pos + * @return + */ + boolean containsOffset(int pos) { + return fOffset < pos || fOffset + fSize <= pos; + } + + /** + * @return number of available characters. + */ + public int avail() { + return fSize - fPos; + } + + /** + * @return number of free space. + */ + public int free() { + return fcBufSize - fPos; + } + } + + final static private boolean DEBUG = false; + + /** The maximum number of buffers for this file. */ + final static public int fcNrBufs = 4; + + private RandomAccessFile fFile; + private int fPosition; + private int fLength; + private Buffer fBuffer[] = new Buffer[fcNrBufs]; + private byte[] fByteBuffer = new byte[2 * Buffer.fcBufSize]; + private int fSwapper; + private String fName; + private boolean fReadonly; + private boolean fDeleteOnDispose; + + private REDFile(File file, boolean readonly) { + assert !readonly || file != null; + fReadonly = readonly; + fDeleteOnDispose = file == null; + if (file != null) { + try { + setFile(file); + fLength = (int)(fFile.length() / 2); + } catch (IOException ioe) { + throw new Error(ioe); + } + } + } + + public REDFile() { + this((File)null, false); + } + + public REDFile(String name, boolean readonly) { + this(new File(name), readonly); + } + + public REDFile(String name) { + this(name, false); + } + + private void setFile(File file) throws IOException { + assert file != null; + if (fReadonly) { + fFile = new RandomAccessFile(file, "r"); //$NON-NLS-1$ + fName = file.toString(); + } else if (file != null) { + fFile = new RandomAccessFile(file, "rw"); //$NON-NLS-1$ + fName = file.toString(); + } + } + + /** + * Free resources. + */ + public void dispose() { + if (fFile != null) { + try { + close(); + } catch (IOException e) { + } + fFile = null; + if (fDeleteOnDispose) { + new File(fName).delete(); + } + } + } + + public void close() throws IOException { + flush(); + if (fFile != null) { + fFile.close(); + } + } + + /** + * Flush buffers. + * @throws IOException + */ + public void flush() throws IOException { + for (int i = 0; i < fcNrBufs; i++) { + if (fBuffer[i] != null) { + if (fBuffer[i].fDirty) { + flush(fBuffer[i]); + } + fBuffer[i] = null; + } + } + } + + /** + * Flush a dirty buffer. + * @param buffer + */ + private void flush(Buffer buffer) throws IOException { + assert buffer.fDirty; + write(buffer.fOffset, buffer.fData, 0, buffer.fSize); + buffer.fDirty = false; + } + + /** + * @return true if this file is readonly. + */ + public boolean isReadonly() { + return fReadonly; + } + + /** + * @return the length in char units. + */ + public int length() { + if (fLength < 0) { + if (fFile == null) { + fLength = 0; + } else { + try { + fLength = (int)(fFile.length() / 2); + } catch (IOException e) { + fLength = 0; + } + } + } + return fLength; + } + + /** erase file content + * @return true, if successful; false otherwise + * @post return == true implies length() == 0 + */ + public boolean purge() throws IOException { + if (isReadonly()) { + return false; + } + fFile.setLength(0); + for (int i = 0; i < fcNrBufs; i++) { + if (fBuffer[i] != null) { + fBuffer[i].fOffset = -1; + fBuffer[i].fDirty = false; + if (i > 0) { + fBuffer[i] = null; + } + } + } + fLength = 0; + return true; + } + + @Override + protected void finalize() { + dispose(); + } + + private File createTmpFile() { + try { + File file = File.createTempFile("scratch", ".tmp"); //$NON-NLS-1$ //$NON-NLS-2$ + file.deleteOnExit(); + return file; + } catch (IOException e) { + throw new Error(e); + } + } + + /** + * copy file - Convenience method. + * @pre src != null + * @pre dest != null + */ + public static void copyFile(REDFile src, REDFile dest) throws IOException { + dest.purge(); + byte buf[] = new byte[4096]; + int n = src.fFile.read(buf); + while (n >= 0) { + if (n > 0) { + dest.fFile.write(buf, 0, n); + } + n = src.fFile.read(buf); + } + dest.fLength = src.length(); + } + + /** + * @param offset + */ + public void seek(int offset) throws IOException { + if (offset < 0) { + throw new IOException("Negative seek position"); //$NON-NLS-1$ + } + fPosition = offset; + } + + /** + * Write char array as 16-bit Unicode at absolute position. + * @param position File position + * @param data + * @param offset + * @param length + * @throws IOException + */ + private void write(int position, char[] data, int offset, int length) throws IOException { + if (DEBUG) + System.out.println("REDFile.write " + length + " at " + position); //$NON-NLS-1$ //$NON-NLS-2$ + if (fFile == null) { + setFile(createTmpFile()); + } + fFile.seek(position * 2); + int blen = 0; + for (int clen = 0; clen < length; ++clen) { + char c = data[offset + clen]; + fByteBuffer[blen++] = (byte) ((c >>> 8) & 0xff); + fByteBuffer[blen++] = (byte) ((c >>> 0) & 0xff); + if (blen == fByteBuffer.length) { + fFile.write(fByteBuffer, 0, blen); + blen = 0; + } + } + if (blen > 0) { + fFile.write(fByteBuffer, 0, blen); + } + } + + /** + * Write char array as UTF-16 bytes (2 bytes each). + * @param data + * @param offset + * @param length + * @throws IOException + */ + public void writeBuffered(char[] data, int offset, int length) throws IOException { + if (isReadonly()) { + throw new IOException("Cannot write to readonly file"); //$NON-NLS-1$ + } + Buffer buffer = null; + int clen = 0; + while (clen < length) { + buffer = getBufferForOffset(fPosition, true); + int count = Math.min(length - clen, buffer.free()); + if (count == 0) { + break; + } + System.arraycopy(data, offset, buffer.fData, buffer.fPos, count); + buffer.fPos += count; + if (buffer.fPos > buffer.fSize) { + buffer.fSize = buffer.fPos; + } + buffer.fDirty = true; + offset += count; + clen += count; + fPosition += count; + } + if (fPosition > fLength) { + fLength = fPosition; + } + } + + /** + * Write String as UTF-16 bytes (2 bytes each). + * @param data + * @param offset + * @param length + * @throws IOException + */ + public void writeBuffered(String data, int offset, int length) throws IOException { + if (isReadonly()) { + throw new IOException("Cannot write to readonly file"); //$NON-NLS-1$ + } + Buffer buffer = null; + int clen = 0; + while (clen < length) { + buffer = getBufferForOffset(fPosition, true); + int count = Math.min(length - clen, buffer.free()); + if (count == 0) { + break; + } + data.getChars(offset, offset + count, buffer.fData, buffer.fPos); + buffer.fPos += count; + if (buffer.fPos > buffer.fSize) { + buffer.fSize = buffer.fPos; + } + buffer.fDirty = true; + offset += count; + clen += count; + fPosition += count; + } + if (fPosition > fLength) { + fLength = fPosition; + } + } + + /** + * Update the content of a buffer. + * @param buffer + * @throws IOException + */ + private void update(Buffer buffer) throws IOException { + assert !buffer.fDirty; + buffer.fSize = read(buffer.fOffset, buffer.fData, 0, buffer.fData.length); + } + + /** + * Read an array of characters at absolute position. + * @param position File position + * @param data + * @param offset + * @param length + * @return number of characters read. + */ + private int read(int position, char[] data, int offset, int length) throws IOException { + fFile.seek(position * 2); + int blen = 0; + int bsize = length * 2; + while (blen < bsize) { + int count = fFile.read(fByteBuffer, 0, Math.min(fByteBuffer.length, bsize - blen)); + if (count < 0) { + break; + } + for (int i = 0; i < count; i += 2) { + int hiByte = fByteBuffer[i] & 0xff; + int loByte = fByteBuffer[i + 1] & 0xff; + data[offset++] = (char) ((hiByte << 8) | (loByte << 0)); + } + blen += count; + } + if (DEBUG) + System.out.println("REDFile.read " + length + " at " + position + " = " + blen / 2); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + // convert to character length + return blen / 2; + } + + /** + * Read an array of characters. + * @param data + * @param offset + * @param length + * @return number of characters read. + */ + public int readBuffered(char[] data, int offset, int length) throws IOException { + Buffer buffer; + int clen = 0; + while (clen < length) { + buffer = getBufferForOffset(fPosition, false); + int count = Math.min(length - clen, buffer.avail()); + if (count <= 0) { + break; + } + System.arraycopy(buffer.fData, buffer.fPos, data, offset, count); + buffer.fPos += count; + offset += count; + clen += count; + fPosition += count; + } + return clen; + } + + /** + * Read characters into StringBuffer. + * @param strBuf + * @param length + * @return number of characters read. + */ + public int readBuffered(StringBuffer strBuf, int length) throws IOException { + Buffer buffer; + int clen = 0; + while (clen < length) { + buffer = getBufferForOffset(fPosition, false); + int count = Math.min(length - clen, buffer.avail()); + if (count <= 0) { + break; + } + strBuf.append(buffer.fData, buffer.fPos, count); + buffer.fPos += count; + clen += count; + fPosition += count; + } + return clen; + } + + /** + * Get a REDFileBuffer for an offset. + * @param pos + * @return + */ + private Buffer getBufferForOffset(int offset, boolean write) throws IOException { + Buffer buffer = null; + int bufferOffset = (offset / Buffer.fcBufSize) * Buffer.fcBufSize; + int i; + for (i = 0; i < fcNrBufs && fBuffer[i] != null; ++i) { + if (bufferOffset == fBuffer[i].fOffset) { + buffer = fBuffer[i]; + break; + } + } + if (buffer == null) { + if (i < REDFile.fcNrBufs) { + buffer = new Buffer(); + fBuffer[i] = buffer; + } else { + fSwapper = (fSwapper + 1) % REDFile.fcNrBufs; + buffer = fBuffer[fSwapper]; + if (buffer.fDirty) { + flush(buffer); + } + } + buffer.fOffset = bufferOffset; + buffer.fSize = 0; + } + buffer.fPos = offset - buffer.fOffset; + if (write && buffer.fSize < buffer.fPos || !write && buffer.avail() <= 0) { + if (buffer.fDirty) { + flush(buffer); + } + update(buffer); + } + return buffer; + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/text/REDFileRider.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/text/REDFileRider.java new file mode 100644 index 00000000000..b75a5f7ff75 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/text/REDFileRider.java @@ -0,0 +1,149 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.text; + +import java.io.IOException; + +/** + * Accessor to <code>REDFile</code>s. + */ +public final class REDFileRider implements IFileRider { + + private REDFile fFile; + private int fLimit = Integer.MAX_VALUE; + private int fResult; + private boolean fEof; + private char[] fOneCharBuf = new char[1]; + + /** @pre f != null */ + public REDFileRider(REDFile f) throws IOException { + set(f, 0); + } + public REDFileRider(REDFile f, int limit) throws IOException { + fLimit = limit; + set(f, 0); + } + + /* + * @see org.eclipse.cdt.dsf.debug.internal.ui.disassembly.text.IFileRider#seek(int) + */ + public void seek(int pos) throws IOException { + fFile.seek(pos); + fEof = false; + fResult = 0; + } + + /** + * Set rider to file and position + * + * @param f the file the rider should operate on + * @param pos is normalized to be in range [0, f.length()] + * @pre f != null + * @pre pos >= 0 && pos <= f.length() + * @post fBuffer != null + */ + private void set(REDFile f, int pos) throws IOException { + + assert f != null; + assert pos >= 0 && pos <= f.length(); + + fFile = f; + fFile.seek(pos); + fEof = false; + fResult = 0; + } + + /** + * Get end of file status + * @return true, if rider has tried to read beyond the end of file + */ + public boolean eof() { + return fEof; + } + + /** + * Get result of last operation + * will be 0 after successful write operation + * will contain nr. of characters requested but unavailable read after read operation + */ + public int getResult() { + return fResult; + } + + /** + * Get the REDFile the rider operates on. + * @post return != null + */ + public REDFile getFile() { + return fFile; + } + + public void writeChar(char c) throws IOException { + fOneCharBuf[0] = c; + writeChars(fOneCharBuf, 0, 1); + } + + public void writeChars(char[] buf) throws IOException { + writeChars(buf, 0, buf.length); + } + + public void writeChars(char[] buf, int n) throws IOException { + writeChars(buf, 0, n); + } + + public void writeChars(char[] buf, int off, int n) throws IOException { + fFile.writeBuffered(buf, off, n); + fResult = 0; + } + + public void writeChars(String buf, int off, int n) throws IOException { + fFile.writeBuffered(buf, off, n); + fResult = 0; + } + + public char readChar() throws IOException { + readChars(fOneCharBuf, 0, 1); + return fEof ? '\0' : fOneCharBuf[0]; + } + + public void readChars(char[] buf) throws IOException { + readChars(buf, 0, buf.length); + } + + public void readChars(char[] buf, int n) throws IOException { + readChars(buf, 0, n); + } + + public void readChars(char[] buf, int off, int n) throws IOException { + int count = fFile.readBuffered(buf, off, n); + fResult = n-count; + fEof = fResult > 0; + } + + public void readChars(StringBuffer buf, int n) throws IOException { + int count = fFile.readBuffered(buf, n); + fResult = n-count; + fEof = fResult > 0; + } + + public int length() { + return fFile.length(); + } + + public int limit() { + return fLimit; + } + + public boolean isReadonly() { + return fFile.isReadonly(); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/text/REDRun.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/text/REDRun.java new file mode 100644 index 00000000000..9bd27fb423c --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/text/REDRun.java @@ -0,0 +1,149 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.text; + +import java.io.IOException; + +/** + * A piece of text on a scratch file. + */ +public class REDRun implements CharSequence { + + IFileRider fRider; + int fOffset; + int fLength; + + /** + * @pre rider != null + * @pre style != null + * @pre length > 0 + */ + public REDRun(IFileRider rider, int offset, int length) { + fRider = rider; + fOffset = offset; + fLength = length; + } + + /** + * @pre rider != null + * @pre style != null + * @pre str.length() > 0 + */ + public REDRun(IFileRider rider, String str) throws IOException { + fRider = rider; + fLength = str.length(); + fOffset = fRider.length(); + fRider.seek(fOffset); + fRider.writeChars(str, 0, fLength); + } + + /** + * @param rider + * @param buf + * @param off + * @param n + */ + public REDRun(IFileRider rider, char[] buf, int off, int n) throws IOException { + fRider = rider; + fLength = n; + fOffset = fRider.length(); + fRider.seek(fOffset); + fRider.writeChars(buf, off, n); + } + + /** + * @post return.length() == length() + */ + public String asString() throws IOException { + String retVal; + char[] buf = new char[fLength]; + fRider.seek(fOffset); + fRider.readChars(buf); + retVal = new String(buf); + return retVal; + } + + /** + * Copy parts of run into char-array + * @param arr array to copy into + * @param from offset of arr to copy bytes to + * @param arrSize max offset of arr to write into + * @param myOff offset of run to start reading at + * @return the number of bytes copied + */ + public int copyInto(char[] arr, int from, int arrSize, int myOff) throws IOException { + fRider.seek(fOffset + myOff); + int readAmount = Math.min(arrSize - from, fLength - myOff); + fRider.readChars(arr, from, readAmount); + return readAmount; + } + + /** + * Append parts of run to a StringBuffer + * @param buffer StringBuffer to append to + * @param length number of characters to append + * @param myOff offset of run to start reading at + * @return the number of bytes appended + */ + public int appendTo(StringBuffer buffer, int length, int myOff) throws IOException { + fRider.seek(fOffset + myOff); + int readAmount = Math.min(length, fLength - myOff); + fRider.readChars(buffer, readAmount); + return readAmount; + } + + /** + * A run is mergable with another if the other a direct successor in the scratch file. + * @pre r != null + */ + public boolean isMergeableWith(REDRun r) { + return r.fRider == fRider && r.fOffset == fOffset + fLength; + } + + + /* + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + try { + return asString(); + } catch (IOException e) { + return null; + } + } + + /* + * @see java.lang.CharSequence#charAt(int) + */ + public char charAt(int pos) { + try { + fRider.seek(fOffset + pos); + return fRider.readChar(); + } catch (IOException e) { + return 0; + } + } + + /* + * @see java.lang.CharSequence#subSequence(int, int) + */ + public CharSequence subSequence(int start, int end) { + return new REDRun(fRider, fOffset + start, end - start); + } + + /* + * @see java.lang.CharSequence#length() + */ + public int length() { + return fLength; + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/text/REDTextStore.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/text/REDTextStore.java new file mode 100644 index 00000000000..cbd85dc3763 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/text/REDTextStore.java @@ -0,0 +1,874 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.text; + +import java.io.IOException; +import java.io.PrintStream; +import java.nio.CharBuffer; + +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.jface.text.ITextStore; + +/** + * Piece list text store implementation with scratch files. + */ +public final class REDTextStore implements ITextStore { + + private static final int SCRATCH_FILE_THRESHOLD = 1024 * 1024; + private static final int MAX_SCRATCH_FILES = 4; + private static final int RECYCLE_THRESHOLD = 20; + private static final int IN_MEMORY_LIMIT = 1024 * 32; + private final static int CHUNK_SIZE = 1024 * 4; + private REDFileRider[] fScratchFiles = new REDFileRider[MAX_SCRATCH_FILES]; + private LinkedRun fHead; + private LinkedRun fSpare; + private LinkedRun fCache; + private int fCachePos; + private int fLength; + private int fDeadLength; + private final RunSpec fRunSpec = new RunSpec(); + private Job fSwapper; + + /** + * This job swaps readonly IFileRider to disk. + */ + private final class TextStoreSwapper extends Job { + private IFileRider fRider; + private String fText; + + private TextStoreSwapper(IFileRider rider, String text) { + super(""); //$NON-NLS-1$ + fRider = rider; + fText = text; + setName("Swapping editor buffer to disk"); //$NON-NLS-1$ + setPriority(Job.LONG); +// setSystem(true); + } + + @Override + public IStatus run(IProgressMonitor monitor) { + REDFileRider fileRider = null; + if (!monitor.isCanceled()) { + try { +// System.out.println("TextStoreSwapper.run() creating swap file"); + fileRider = new REDFileRider(new REDFile()); + int size = fText.length(); + monitor.beginTask(getName(), size+1); + int written = 0; + while (written < size && !monitor.isCanceled()) { + int n = Math.min(size-written, CHUNK_SIZE); + fileRider.writeChars(fText, written, n); + monitor.worked(n); + written += n; + } + } catch (IOException e) { + cancel(); + } + } + if (!monitor.isCanceled()) { +// System.out.println("TextStoreSwapper.run() swapping"); + fileRider = swap(fRider, fileRider); + monitor.done(); + } + // something went wrong, dispose the file + if (fileRider != null) { +// System.out.println("TextStoreSwapper.run() disposing"); + fileRider.getFile().dispose(); + } + // remove references + fText = null; + fRider = null; +// System.out.println("TextStoreSwapper.run() done"); + return Status.OK_STATUS; + } + } + + private final static class LinkedRun extends REDRun { + LinkedRun fNext; + LinkedRun fPrev; + + LinkedRun(IFileRider rider, String str) throws IOException { + super(rider, str); + } + LinkedRun(IFileRider rider, char[] buf, int off, int n) throws IOException { + super(rider, buf, off, n); + } + LinkedRun(IFileRider rider, int offset, int length) { + super(rider, offset, length); + } + } + + /** + * Create an empty text store. + */ + public REDTextStore() { + } + + /** + * Create a text store with intial content. + */ + public REDTextStore(String text) { + set(text); + } + + @Override + protected void finalize() { + dispose(); + } + + /** + * Free resources. + * Can be reactivated by calling <code>set(String)</code>. + */ + public void dispose() { + synchronized (fRunSpec) { + if (fSwapper != null) { + fSwapper.cancel(); + fSwapper = null; + } + for (int i = 0; i < fScratchFiles.length; ++i) { + if (fScratchFiles[i] != null) { + fScratchFiles[i].getFile().dispose(); + fScratchFiles[i] = null; + } + } + fHead = null; + fCache = null; + fSpare = null; + fRunSpec.fRun = null; + fCachePos = 0; + fLength = 0; + fDeadLength = 0; + } + } + + // ---- ITextStore interface ------------------------------------ + + /* (non-Javadoc) + * @see org.eclipse.jface.text.ITextStore#get(int) + */ + public char get(int offset) { + synchronized (fRunSpec) { + RunSpec spec = findNextRun(offset, null); + if (spec.fRun != null) { + return spec.fRun.charAt(spec.fOff); + } + return 0; + } + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.ITextStore#get(int, int) + */ + public String get(int offset, int length) { + synchronized (fRunSpec) { + // special case: long in-memory text in full length (about to be swapped) + if (length == fLength && fSwapper != null && fHead != null && fHead.fNext == null) { + ((StringRider)fHead.fRider).fBuffer.position(0); + return ((StringRider)fHead.fRider).fBuffer.toString(); + } + return toString(offset, offset + length); + } + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.ITextStore#getLength() + */ + public int getLength() { + synchronized (fRunSpec) { + return fLength; + } + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.ITextStore#set(java.lang.String) + */ + public void set(String text) { + synchronized (fRunSpec) { + dispose(); + if (text != null) { + fHead = new LinkedRun(new StringRider(text), 0, text.length()); + fLength = text.length(); + if (fLength > IN_MEMORY_LIMIT) { + fSwapper = new TextStoreSwapper(fHead.fRider, text); + fSwapper.schedule(1000); + } + } + } + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.ITextStore#replace(int, int, java.lang.String) + */ + public void replace(int offset, int length, String text) { + synchronized (fRunSpec) { + if (text == null || text.length() == 0) { + // delete only + replace(offset, length, null, 0, 0); + } else { + replace(offset, length, text, 0, text.length()); + } + } + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + synchronized (fRunSpec) { + return toString(0, getLength()); + } + } + + // --- implementation ---------------------------------------------------- + + /** Get part of the text as string. + * The parameters from and to are normalized to be in range: [0, fLength] + * Not MT-safe! + * @param from The beginning of the stretch of text to be returned; for from == n, the nth character is included. + * @param to The end of the stretch of text to be returned; for to == n, the nth character is not included. + * @return The stretch [from, to[ as String. + */ + private String toString(int from, int to) { + assert from >= 0 && from <= to && to <= fLength; + + int len = to - from; + StringBuffer strBuf = new StringBuffer(len); + if (len > 0) { + RunSpec spec = findPrevRun(from, fRunSpec); + try { + int done = spec.fRun.appendTo(strBuf, len, spec.fOff); + while (done < len) { + spec.fRun = spec.fRun.fNext; + assert spec.fRun != null; + done += spec.fRun.appendTo(strBuf, len - done, 0); + } + } catch (IOException e) { + internalError(e); + } + } + assert strBuf.length() == len; + return strBuf.toString(); + } + + /** + * Replace [from;deleteLen[ with buf[off;insertLen[ + * Not MT-safe! + * @param from + * @param deleteLen + * @param buf + * @param off + * @param insertLen + */ + private void replace(int from, int deleteLen, Object buf, int off, int insertLen) { + assert from >= 0 && from <= fLength; + assert deleteLen >= 0; + assert from + deleteLen <= fLength; + + RunPair split = null; + if (deleteLen > 0) { + split = delete(from, from + deleteLen); + } + if (buf == null || insertLen == 0) { + return; + } +// assert off >= 0 && off < buf.length; +// assert insertLen >= 0 && off+insertLen <= buf.length; + if (split == null) { + split = splitRun(from); + } + RunPair insert = makeRuns(split.fBefore, buf, off, insertLen); +// assert runLength(insert.fBefore, insert.fAfter) == insertLen; + insertRuns(split, insert.fBefore, insert.fAfter); + fLength += insertLen; +// assert runLength(fHead, null) == fLength; + fCache = insert.fAfter; + fCachePos = from+insertLen-insert.fAfter.fLength; +// assert checkConsistency(); + if (split.fBefore != null) { + mergeRuns(split.fBefore, split.fAfter); + } else { + mergeRuns(fHead, split.fAfter); + } + if (fDeadLength > fLength / 10) { + reconcile(); + } + } + + /** + * Recreate text store by reinserting all runs. + * Not MT-safe! + */ + public void reconcile() { + LinkedRun run = fHead; + REDFileRider[] scratchFiles = fScratchFiles; + fScratchFiles = new REDFileRider[MAX_SCRATCH_FILES]; + fHead = null; + fCache = null; + fCachePos = -1; + fSpare = null; + fLength = 0; + fDeadLength = 0; + char[] buf = new char[CHUNK_SIZE]; + int offset = 0; + int runOffset = 0; + while (run != null) { + int n; + try { + do { + n = run.copyInto(buf, 0, buf.length, runOffset); + replace(offset, 0, buf, 0, n); + offset += n; + runOffset += n; + } while (runOffset < run.fLength); + } catch (IOException e) { + internalError(e); + } + run = run.fNext; + runOffset = 0; + } + for (int i = 0; i < scratchFiles.length; ++i) { + if (scratchFiles[i] != null) { + scratchFiles[i].getFile().dispose(); + scratchFiles[i] = null; + } + } + } + + // ******************************************************************************************************************************************************* + // P R I V A T E - L I N E + // ******************************************************************************************************************************************************* + + /** + * Create a new LinkedRun + * @param before LinkedRun before new run + * @param n length of content + * @return new LinkedRun + */ + private LinkedRun createRun(LinkedRun before, int n) { + IFileRider scratchFile; + if (before != null && before.fRider.length() == before.fOffset + before.fLength && before.fRider.limit() >= before.fRider.length() + n) { + scratchFile = before.fRider; + } else { + scratchFile = getScratchFile(); + } + return new LinkedRun(scratchFile, scratchFile.length(), n); + } + + private REDFileRider getScratchFile() { + REDFileRider rider = null; + for (int i = 0; i < fScratchFiles.length; ++i) { + rider = fScratchFiles[i]; + if (rider == null) { + try { + rider = new REDFileRider(new REDFile()); + } catch (IOException e) { + internalError(e); + } + fScratchFiles[i] = rider; + break; + } else if (rider.length() < SCRATCH_FILE_THRESHOLD) { + break; + } + } + return rider; + } + + /** + * Save run for later recycling. + * @param run + */ + private void spareRun(LinkedRun run, LinkedRun last) { + // remove readonly runs first + if (last != null) { + last.fNext = null; + } + LinkedRun cur = run; + LinkedRun prev = null; + while (cur != null) { + if (cur.fRider.isReadonly()) { + if (prev != null) { + prev.fNext = cur.fNext; + } else { + run = cur.fNext; + } + if (cur.fNext != null) { + cur.fNext.fPrev = prev; + } + } else { + prev = cur; + } + cur = cur.fNext; + } + if (run == null) { + return; + } + last = prev; + if (last != null) { + last.fNext = fSpare; + } + if (fSpare != null) { + fSpare.fPrev = last; + } + fSpare = run; + fSpare.fPrev = null; + } + + /** + * Recycle a run. + * @returns run + */ + private LinkedRun recycleRun() { + LinkedRun recycled = fSpare; + fSpare = null; + return recycled; + } + + /** + * @param e + */ + private void internalError(Exception e) { + throw new Error("Internal error", e); //$NON-NLS-1$ + } + + /** + * REDRunSpec represents a specification of a run, including the run itself, its origin and offset. + * It is used for findRun - operations. + */ + private final static class RunSpec { + public LinkedRun fRun = null; + public int fOrg = -1; + public int fOff = -1; + public boolean isValid() { + return fRun != null; + } + } + + /** + * auxiliary class: pair of red runs + */ + private final static class RunPair { + public LinkedRun fBefore; + public LinkedRun fAfter; + } + + /** + * Auxiliary method to delete part of the text. + * from and to have gap semantics. + * @param from start of the stretch to be deleted. + * @param to end of the stretch to be deleted. + * @return split pos for insertion + */ + private RunPair delete(int from, int to) { + RunPair start = splitRun(from); + RunPair end = splitRun(to); + if (start.fBefore != null) { + start.fBefore.fNext = end.fAfter; + } else { + fHead = end.fAfter; + } + if (end.fAfter != null) { + end.fAfter.fPrev = start.fBefore; + } + if (end.fAfter != null) { + fCache = end.fAfter; + fCachePos = from; + } else { + fCache = fHead; + fCachePos = 0; + } + fLength -= (to - from); + if (fLength == 0) { + dispose(); + return null; + } + spareRun(start.fAfter, end.fBefore); + start.fAfter = end.fAfter; + return start; + } + + /** + * Find the run which contains given position. + * caveat: if the given position lies between run a and b, a is returned + * @param pos The position to find the run for + * @return A run specification representing the found run. May be invalid (if given position was larger than text) + * @pre pos >= 0 + * @pre pos <= length() + * @post return != null + * @post return.fOff > 0 || pos == 0 + * @post return.fOff <= return.fRun.fLength + * @post return.fOrg >= 0 + */ + private RunSpec findPrevRun(int pos, RunSpec spec) { + assert pos >= 0 && pos <= fLength; + LinkedRun cur; + int curPos; + + if (fCache != null && fCachePos - pos < pos) { + assert fCache != null; + cur = fCache; + curPos = fCachePos; + } else { + cur = fHead; + curPos = 0; + } + while (cur != null && pos - curPos > cur.fLength) { + curPos += cur.fLength; + cur = cur.fNext; + } + if (pos != 0) { + while (pos - curPos <= 0) { + cur = cur.fPrev; + curPos -= cur.fLength; + } + } + + fCache = cur; + fCachePos = curPos; + + if (spec == null) { + spec = fRunSpec; + } + spec.fRun = cur; + spec.fOrg = curPos; + spec.fOff = pos - curPos; + + return spec; + } + + /** + * Find the run which contains given position. + * caveat: if the given position lies between run a and b, b is returned + * @param pos The position to find the run for + * @return A run specification representing the found run. May be invalid (if given position was larger than text) + * @pre pos >= 0 + * @pre pos <= length() + * @post return != null + * @post return.fOff >= 0 + * @post return.fOff < return.fRun.fLength + */ + private RunSpec findNextRun(int pos, RunSpec spec) { + if (pos < fLength) { + spec = findPrevRun(pos + 1, spec); + spec.fOff--; + } else { + spec = findPrevRun(pos, spec); + } + return spec; + } + + /** Split run at pos and return pair of runs. */ + private RunPair splitRun(int pos) { + RunPair p = new RunPair(); + if (pos == 0) { + p.fBefore = null; + p.fAfter = fHead; + } else { + RunSpec spec = findPrevRun(pos, null); + assert spec.isValid(); + p.fBefore = spec.fRun; + int len = spec.fRun.length(); + if (spec.fOff != len) { // need to split + p.fAfter = new LinkedRun(p.fBefore.fRider, p.fBefore.fOffset + spec.fOff, p.fBefore.fLength - spec.fOff); + p.fBefore.fLength = spec.fOff; + p.fAfter.fNext = p.fBefore.fNext; + if (p.fAfter.fNext != null) { + p.fAfter.fNext.fPrev = p.fAfter; + } + p.fBefore.fNext = p.fAfter; + p.fAfter.fPrev = p.fBefore; + } else { // we already have a split + p.fAfter = p.fBefore.fNext; + } + } + return p; + } + + /** + * Merge all runs between start and end where possible. + * @pre start != null + */ + private void mergeRuns(LinkedRun start, LinkedRun end) { + LinkedRun cur = start; + LinkedRun next = cur.fNext; + + while (cur != end && next != null) { + if (cur.isMergeableWith(next)) { + if (next == fCache) { + fCache = cur; + fCachePos -= cur.fLength; + } + cur.fLength += next.fLength; + cur.fNext = next.fNext; + if (cur.fNext != null) { + cur.fNext.fPrev = cur; + } + if (next == end) { + break; + } + } else { + cur = next; + } + next = cur.fNext; + } + } + + private RunPair makeRuns(LinkedRun before, Object buf, int off, int n) { + RunPair result = new RunPair(); + LinkedRun run; + LinkedRun recycled = recycleRun(); + if (recycled != null) { + result.fBefore = recycled; + run = recycled; + do { + int count = Math.min(run.fLength, n); + try { + assert !run.fRider.isReadonly(); + // safeguard + if (run.fRider.isReadonly()) { + run = null; + break; + } + run.fRider.seek(run.fOffset); + if (buf instanceof char[]) { + run.fRider.writeChars((char[])buf, off, count); + } else { + run.fRider.writeChars((String)buf, off, count); + } + if (run.fLength - count >= RECYCLE_THRESHOLD) { + LinkedRun next = run.fNext; + LinkedRun newRun = new LinkedRun(run.fRider, run.fOffset+count, run.fLength-count); + joinRuns(run, newRun); + joinRuns(newRun, next); + } else { + fDeadLength += run.fLength - count; + } + run.fLength = count; + off += count; + n -= count; + before = run; + run = run.fNext; + } catch (IOException e) { + run = null; +// internalError(e); + break; + } + } while (run != null && n > 0); + if (run != null) { + // shortcut for spareRun(run, null) + fSpare = run; + } + } + if (n > 0) { + run = createRun(before, n); + if (buf instanceof char[]) { + try { + run.fRider.seek(run.fOffset); + run.fRider.writeChars((char[])buf, off, n); + } catch (IOException e) { +// internalError(e); + run = new LinkedRun(new StringRider(CharBuffer.wrap((char[])buf, off, off+n)), 0, n); + } + } else { + try { + run.fRider.seek(run.fOffset); + run.fRider.writeChars((String)buf, off, n); + } catch (IOException e) { +// internalError(e); + run = new LinkedRun(new StringRider(CharBuffer.wrap((String)buf, off, off+n)), 0, n); + } + } + if (result.fBefore == null) { + result.fBefore = run; + } else { + joinRuns(before, run); + } + result.fAfter = run; + } else { + result.fAfter = before; + } + return result; + } + + private void joinRuns(LinkedRun start, LinkedRun next) { + assert start != next; + start.fNext = next; + if (next != null) { + next.fPrev = start; + } + } + private void insertRuns(RunPair pos, LinkedRun start, LinkedRun end) { + assert pos.fBefore == null || pos.fBefore != pos.fAfter; + start.fPrev = pos.fBefore; + if (pos.fBefore != null) { + pos.fBefore.fNext = start; + } else { + fHead = start; + } + end.fNext = pos.fAfter; + if (pos.fAfter != null) { + pos.fAfter.fPrev = end; + } + } + + /** + * Swap given old (readonly) rider with the new (writable) one. + * @param oldRider + * @param newRider + * @return <code>null</code> if the new rider was consumed + */ + private REDFileRider swap(IFileRider oldRider, REDFileRider newRider) { + synchronized(fRunSpec) { + // search linked run list starting from head and replace + // all instances of oldRider with newRider + // in the general case, spare list should be searched, too + LinkedRun cur = fHead; + while (cur != null) { + if (cur.fRider == oldRider) { + cur.fRider = newRider; + } + cur = cur.fNext; + } + for (int i = 0; i < fScratchFiles.length; i++) { + if (fScratchFiles[i] == null) { + fScratchFiles[i] = newRider; + newRider = null; + } + } + if (newRider != null) { + // unlikely, but possible: need to increase array + REDFileRider[] scratchFiles = new REDFileRider[fScratchFiles.length+1]; + System.arraycopy(fScratchFiles, 0, scratchFiles, 0, fScratchFiles.length); + scratchFiles[fScratchFiles.length] = newRider; + fScratchFiles = scratchFiles; + newRider = null; + } + // clean out fRunSpec reference (just in case) + fRunSpec.fRun = null; + // clean out swapper reference + fSwapper = null; + } + return newRider; + } + + // ---- For debugging purposes + + public void printStatistics(PrintStream out) { + int nRuns = 0; + int nSpare = 0; + int spareLength = 0; + LinkedRun run = fHead; + while(run != null) { + ++nRuns; + run = run.fNext; + } + run = fSpare; + while(run != null) { + ++nSpare; + spareLength += run.fLength; + run = run.fNext; + } + double runMean = nRuns > 0 ? (double)fLength / nRuns : Double.NaN; + double spareMean = nSpare > 0 ? spareLength / nSpare : Double.NaN; + out.println("Length: "+fLength); //$NON-NLS-1$ + out.println("Number of runs: "+nRuns); //$NON-NLS-1$ + out.println("Mean length of runs: " + runMean); //$NON-NLS-1$ + out.println("Length of spare runs: "+spareLength); //$NON-NLS-1$ + out.println("Number of spare runs: "+nSpare); //$NON-NLS-1$ + out.println("Mean length of spare runs: " + spareMean); //$NON-NLS-1$ + out.println("Length of dead runs: " + fDeadLength); //$NON-NLS-1$ + } + + /** + * Get structure. + * Returns the structure (Runs) as a single string. The Runs are separated + * by "->\n". At the end the string "null" is added to indicate, that no + * more runs exist. This implies that the string "null" is returned for an + * empty text. + */ + String getStructure() { + LinkedRun cur = fHead; + String structure = ""; //$NON-NLS-1$ + while (cur != null) { + try { + structure += cur.asString() + "->\n"; //$NON-NLS-1$ + } catch (IOException e) { + internalError(e); + } + cur = cur.fNext; + } + structure += "null"; //$NON-NLS-1$ + return structure; + } + + /** + * For debugging purposes only. + */ + boolean checkConsistency() { + LinkedRun run = fHead; + int length = 0; + while (run != null) { + LinkedRun prev = run.fPrev; + LinkedRun next = run.fNext; + assert prev != run; + assert next != run; + assert prev == null || prev.fNext == run; + assert next == null || next.fPrev == run; + while (prev != null) { + prev = prev.fPrev; + assert run != prev; + } + LinkedRun spare = fSpare; + while (spare != null) { + assert run != spare; + spare = spare.fPrev; + } + length += run.fLength; + run = next; + } + assert length == fLength; + if (fCache != null) { + int pos = fCachePos; + run = fCache; + while (run.fPrev != null) { + run = run.fPrev; + pos -= run.fLength; + } + assert pos == 0; + pos = fCachePos; + run = fCache; + while (run != null) { + pos += run.fLength; + run = run.fNext; + } + assert pos == fLength; + } + return true; + } + + int runLength(LinkedRun first, LinkedRun last) { + LinkedRun run = first; + int length = 0; + while (run != null) { + length += run.fLength; + if (run == last) { + break; + } + run = run.fNext; + } + return length; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/text/StringRider.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/text/StringRider.java new file mode 100644 index 00000000000..20c2eacac08 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/text/StringRider.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.text; + +import java.io.IOException; +import java.nio.CharBuffer; + +/** + * StringRider + */ +public class StringRider implements IFileRider { + + CharBuffer fBuffer; + + public StringRider(CharSequence text) { + super(); + // create a readonly buffer + fBuffer = CharBuffer.wrap(text); + } + + public StringRider(CharBuffer buffer) { + super(); + fBuffer = buffer; + } + + public void seek(int pos) throws IOException { + fBuffer.position(pos); + } + + public void writeChar(char c) throws IOException { + fBuffer.put(c); + } + + public void writeChars(char[] buf) throws IOException { + fBuffer.put(buf); + } + + public void writeChars(char[] buf, int n) throws IOException { + fBuffer.put(buf, 0, n); + } + + public void writeChars(char[] buf, int off, int n) throws IOException { + fBuffer.put(buf, off, n); + } + + public void writeChars(String buf, int off, int n) throws IOException { + fBuffer.put(buf, off, off+n); + } + + public char readChar() throws IOException { + return fBuffer.get(); + } + + public void readChars(char[] buf) throws IOException { + fBuffer.get(buf, 0, buf.length); + } + + public void readChars(char[] buf, int n) throws IOException { + fBuffer.get(buf, 0, n); + } + + public void readChars(char[] buf, int off, int n) throws IOException { + fBuffer.get(buf, off, n); + } + + public void readChars(StringBuffer buf, int n) throws IOException { + int pos = fBuffer.position(); + if (fBuffer.hasArray()) { + buf.append(fBuffer.array(), fBuffer.arrayOffset() + pos, n); + } else { + fBuffer.limit(pos+n); + String str = fBuffer.toString(); + assert str.length() == n; + buf.append(str); + fBuffer.limit(fBuffer.capacity()); + } + fBuffer.position(pos + n); + } + + public int length() { + return fBuffer.length(); + } + + public int limit() { + return fBuffer.limit(); + } + + public boolean isReadonly() { + return fBuffer.isReadOnly(); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/text/StringTextStore.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/text/StringTextStore.java new file mode 100644 index 00000000000..aea07d43213 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/text/StringTextStore.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.text; + +import org.eclipse.jface.text.ITextStore; + +/** + * Readonly ITextStore implementation. + */ +public class StringTextStore implements ITextStore { + + private String fText = ""; //$NON-NLS-1$ + + public StringTextStore() { + super(); + } + + public StringTextStore(String text) { + super(); + fText = text; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.ITextStore#get(int) + */ + public char get(int offset) { + return fText.charAt(offset); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.ITextStore#get(int, int) + */ + public String get(int offset, int length) { + if (length == fText.length()) { + return fText; + } + return new String(fText.substring(offset, offset+length)); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.ITextStore#getLength() + */ + public int getLength() { + return fText.length(); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.ITextStore#replace(int, int, java.lang.String) + */ + public void replace(int offset, int length, String text) { + // unmodifiable + throw new UnsupportedOperationException(); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.ITextStore#set(java.lang.String) + */ + public void set(String text) { + fText = text != null ? text : ""; //$NON-NLS-1$ + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/util/HSL.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/util/HSL.java new file mode 100644 index 00000000000..152ccffb0c4 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/util/HSL.java @@ -0,0 +1,111 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.util; + +import org.eclipse.swt.graphics.RGB; + +/** + * HSL (Hue, Saturation, Luminance) color model. + */ +public class HSL { + + public double hue; + public double saturation; + public double luminance; + + /** + * Create HSL from RGB. + */ + public HSL(RGB rgb) { + super(); + double red = rgb.red / 255.0; + double green = rgb.green / 255.0; + double blue = rgb.blue / 255.0; + double cmax= Math.max(Math.max(red, green), blue); + double cmin= Math.min(Math.min(red, green), blue); + luminance = (cmax+cmin)/2; + if (cmax == cmin) { + hue = 0; + saturation = 0; + } else { + double delta = cmax-cmin; + if (luminance < 0.5) { + saturation = delta / (cmax + cmin); + } else { + saturation = delta / (2 - cmax - cmin); + } + if (red == cmax) { + hue = (green - blue) / delta; + } else if (green == cmax) { + hue = 2 + (blue - red) / delta; + } else { + hue = 4 + (red - green) / delta; + } + hue /= 6; + if (hue < 0) { + hue += 1; + } else if (hue > 1) { + hue -= 1; + } + } + } + + public RGB toRGB() { + int red,green,blue; + if (saturation == 0) { + red = (int)Math.round(255*luminance); + green = red; + blue = red; + } else { + double m1, m2; + if (luminance <= 0.5) { + m2 = luminance * (1 + saturation); + } else { + m2 = luminance + saturation - luminance * saturation; + } + m1 = 2 * luminance - m2; + red = hueToColorValue(hue + 1./3., m1, m2); + green = hueToColorValue(hue, m1, m2); + blue = hueToColorValue(hue - 1./3., m1, m2); + } + return new RGB(red, green, blue); + } + + private static int hueToColorValue(double hue, double m1, double m2) { + double v; + if (hue < 0) { + hue += 1; + } else if (hue > 1) { + hue -= 1; + } + if (6*hue < 1) { + v = m1 + (m2-m1) * hue * 6; + } else if (2*hue < 1) { + v = m2; + } else if (3*hue < 2) { + v = m1 + (m2-m1) * (2./3. - hue) * 6; + } else { + v = m1; + } + return (int)Math.round(255 * v); + } + + /** + * Returns a string containing a concise, human-readable + * description of the receiver. + * + * @return a string representation of the <code>HSL</code> + */ + @Override + public String toString () { + return "HSL {" + hue + ", " + saturation + ", " + luminance + "}"; //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/util/StorageEditorInput.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/util/StorageEditorInput.java new file mode 100644 index 00000000000..c8b8e9bbeaa --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/util/StorageEditorInput.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * 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.internal.ui.disassembly.util; + +import org.eclipse.core.resources.IStorage; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.ui.IPersistableElement; +import org.eclipse.ui.IStorageEditorInput; + + +/** + * Abstract implementation of <code>IStorageEditorInput</code>. + */ +abstract public class StorageEditorInput implements IStorageEditorInput { + + /** + * Storage associated with this editor input + */ + private IStorage fStorage; + + /** + * Constructs an editor input on the given storage + */ + public StorageEditorInput(IStorage storage) { + fStorage = storage; + } + + /** + * @see IStorageEditorInput#getStorage() + */ + public IStorage getStorage() { + return fStorage; + } + + /** + * Set new storage. For subclasses only. + * @param storage + */ + protected void setStorage(IStorage storage) { + assert storage != null; + fStorage = storage; + } + + /** + * @see IEditorInput#getImageDescriptor() + */ + public ImageDescriptor getImageDescriptor() { + return null; + } + + /** + * @see IEditorInput#getName() + */ + public String getName() { + return getStorage().getName(); + } + + /** + * @see IEditorInput#getPersistable() + */ + public IPersistableElement getPersistable() { + return null; + } + + /** + * @see IEditorInput#getToolTipText() + */ + public String getToolTipText() { + return getStorage().getFullPath().toOSString(); + } + + /** + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + try { + return object instanceof IStorageEditorInput + && getStorage().equals(((IStorageEditorInput)object).getStorage()); + } catch (CoreException e) { + } + return false; + } + + /** + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return getStorage().hashCode(); + } + + /* (non-Javadoc) + * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class) + */ + @SuppressWarnings("unchecked") + public Object getAdapter(Class adapter) { + return null; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/preferences/DecoratingIntegerFieldEditor.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/preferences/DecoratingIntegerFieldEditor.java new file mode 100644 index 00000000000..7d155967292 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/preferences/DecoratingIntegerFieldEditor.java @@ -0,0 +1,95 @@ +/******************************************************************************* + * 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.internal.ui.preferences; + +import org.eclipse.jface.fieldassist.ControlDecoration; +import org.eclipse.jface.fieldassist.FieldDecoration; +import org.eclipse.jface.fieldassist.FieldDecorationRegistry; +import org.eclipse.jface.preference.IntegerFieldEditor; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.FocusAdapter; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Text; + +/** + * An {@link IntegerFieldEditor} with field decoration. + * + * @since 1.1 + */ +public class DecoratingIntegerFieldEditor extends IntegerFieldEditor { + + private ControlDecoration fDecoration; + + protected DecoratingIntegerFieldEditor() { + } + + /** + * Creates an integer field editor. + * + * @param name the name of the preference this field editor works on + * @param labelText the label text of the field editor + * @param parent the parent of the field editor's control + */ + public DecoratingIntegerFieldEditor(String name, String labelText, Composite parent) { + super(name, labelText, parent); + } + + /** + * Creates an integer field editor. + * + * @param name the name of the preference this field editor works on + * @param labelText the label text of the field editor + * @param parent the parent of the field editor's control + * @param textLimit the maximum number of characters in the text. + */ + public DecoratingIntegerFieldEditor(String name, String labelText, Composite parent, int textLimit) { + super(name, labelText, parent, textLimit); + } + + @Override + public Text getTextControl(Composite parent) { + Text control = super.getTextControl(parent); + if (fDecoration == null) { + fDecoration = new ControlDecoration(control, SWT.LEFT | SWT.TOP); + FieldDecoration errorDecoration = FieldDecorationRegistry.getDefault().getFieldDecoration(FieldDecorationRegistry.DEC_ERROR); + fDecoration.setImage(errorDecoration.getImage()); + fDecoration.setDescriptionText(getErrorMessage()); + + // validate on focus gain + control.addFocusListener(new FocusAdapter() { + @Override + public void focusGained(FocusEvent e) { + refreshValidState(); + } + }); + } + return control; + } + + @Override + protected void showErrorMessage(String msg) { + super.showErrorMessage(msg); + if (fDecoration != null) { + fDecoration.setDescriptionText(msg); + fDecoration.show(); + } + } + + @Override + protected void clearErrorMessage() { + super.clearErrorMessage(); + if (fDecoration != null) { + fDecoration.hide(); + } + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/preferences/DecoratingStringFieldEditor.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/preferences/DecoratingStringFieldEditor.java new file mode 100644 index 00000000000..251d63b9cbe --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/preferences/DecoratingStringFieldEditor.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * 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.internal.ui.preferences; + +import org.eclipse.jface.fieldassist.ControlDecoration; +import org.eclipse.jface.fieldassist.FieldDecoration; +import org.eclipse.jface.fieldassist.FieldDecorationRegistry; +import org.eclipse.jface.preference.StringFieldEditor; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.FocusAdapter; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Text; + +/** + * A {@link StringFieldEditor} with field decoration. + * @since 1.1 + */ +public class DecoratingStringFieldEditor extends StringFieldEditor { + + private ControlDecoration fDecoration; + + protected DecoratingStringFieldEditor() { + } + + /** + * Creates a string field editor of unlimited width. + * Use the method <code>setTextLimit</code> to limit the text. + * + * @param name the name of the preference this field editor works on + * @param labelText the label text of the field editor + * @param parent the parent of the field editor's control + */ + public DecoratingStringFieldEditor(String name, String labelText, Composite parent) { + super(name, labelText, parent); + } + + /** + * Creates a string field editor. + * Use the method <code>setTextLimit</code> to limit the text. + * + * @param name the name of the preference this field editor works on + * @param labelText the label text of the field editor + * @param width the width of the text input field in characters, + * or <code>UNLIMITED</code> for no limit + * @param parent the parent of the field editor's control + */ + public DecoratingStringFieldEditor(String name, String labelText, int width, Composite parent) { + super(name, labelText, width, parent); + } + + /** + * Creates a string field editor. + * Use the method <code>setTextLimit</code> to limit the text. + * + * @param name the name of the preference this field editor works on + * @param labelText the label text of the field editor + * @param width the width of the text input field in characters, + * or <code>UNLIMITED</code> for no limit + * @param strategy either <code>VALIDATE_ON_KEY_STROKE</code> to perform + * on the fly checking (the default), or <code>VALIDATE_ON_FOCUS_LOST</code> to + * perform validation only after the text has been typed in + * @param parent the parent of the field editor's control + */ + public DecoratingStringFieldEditor(String name, String labelText, int width, int strategy, Composite parent) { + super(name, labelText, width, strategy, parent); + } + + @Override + public Text getTextControl(Composite parent) { + Text control = super.getTextControl(parent); + if (fDecoration == null) { + fDecoration = new ControlDecoration(control, SWT.LEFT | SWT.TOP); + FieldDecoration errorDecoration = FieldDecorationRegistry.getDefault().getFieldDecoration(FieldDecorationRegistry.DEC_ERROR); + fDecoration.setImage(errorDecoration.getImage()); + fDecoration.setDescriptionText(getErrorMessage()); + + // validate on focus gain + control.addFocusListener(new FocusAdapter() { + @Override + public void focusGained(FocusEvent e) { + refreshValidState(); + } + }); + } + return control; + } + + @Override + protected void showErrorMessage(String msg) { + super.showErrorMessage(msg); + if (fDecoration != null) { + fDecoration.setDescriptionText(msg); + fDecoration.show(); + } + } + + @Override + protected void clearErrorMessage() { + super.clearErrorMessage(); + if (fDecoration != null) { + fDecoration.hide(); + } + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/preferences/DsfDebugPreferencePage.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/preferences/DsfDebugPreferencePage.java new file mode 100644 index 00000000000..257e240bb81 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/preferences/DsfDebugPreferencePage.java @@ -0,0 +1,109 @@ +/******************************************************************************* + * 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.internal.ui.preferences; + +import org.eclipse.cdt.dsf.debug.ui.IDsfDebugUIConstants; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.jface.preference.BooleanFieldEditor; +import org.eclipse.jface.preference.FieldEditorPreferencePage; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.preference.IntegerFieldEditor; +import org.eclipse.jface.preference.StringFieldEditor; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Group; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; +import org.eclipse.ui.PlatformUI; + +/** + * DSF debug preference page. + */ +public class DsfDebugPreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage { + + /** + * Mandatory default constructor (executable extension). + */ + public DsfDebugPreferencePage() { + super(FLAT); + IPreferenceStore store= DsfUIPlugin.getDefault().getPreferenceStore(); + setPreferenceStore(store); + setDescription(MessagesForPreferences.DsfDebugPreferencePage_description); + } + + /* + * @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench) + */ + public void init(IWorkbench workbench) { + } + + @Override + public void createControl(Composite parent) { + super.createControl(parent); + PlatformUI.getWorkbench().getHelpSystem().setHelp(getControl(), IDsfDebugUIConstants.PREFERENCE_PAGE); + } + + @Override + protected void createFieldEditors() { + final Composite parent= getFieldEditorParent(); + final GridLayout layout= new GridLayout(); + layout.marginWidth= 0; + parent.setLayout(layout); + + Group performanceGroup= new Group(parent, SWT.NONE); + performanceGroup.setText(MessagesForPreferences.DsfDebugPreferencePage_performanceGroup_label); + GridLayout groupLayout= new GridLayout(3, false); + performanceGroup.setLayout(groupLayout); + performanceGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + + // stack frame limit + IntegerFieldEditor limitEditor= new IntegerWithBooleanFieldEditor( + IDsfDebugUIConstants.PREF_STACK_FRAME_LIMIT_ENABLE, + IDsfDebugUIConstants.PREF_STACK_FRAME_LIMIT, + MessagesForPreferences.DsfDebugPreferencePage_limitStackFrames_label, + performanceGroup); + + limitEditor.setValidRange(1, Integer.MAX_VALUE); + limitEditor.setValidateStrategy(StringFieldEditor.VALIDATE_ON_FOCUS_LOST); + limitEditor.fillIntoGrid(performanceGroup, 3); + addField(limitEditor); + + // sync stepping speed + BooleanFieldEditor syncSteppingEditor= new BooleanFieldEditor( + IDsfDebugUIConstants.PREF_WAIT_FOR_VIEW_UPDATE_AFTER_STEP_ENABLE, + MessagesForPreferences.DsfDebugPreferencePage_waitForViewUpdate_label, + performanceGroup); + + syncSteppingEditor.fillIntoGrid(performanceGroup, 3); + addField(syncSteppingEditor); + + // minimum step interval + IntegerFieldEditor minIntervalEditor= new DecoratingIntegerFieldEditor( + IDsfDebugUIConstants.PREF_MIN_STEP_INTERVAL, + MessagesForPreferences.DsfDebugPreferencePage_minStepInterval_label, + performanceGroup); + + minIntervalEditor.setValidRange(0, 10000); + minIntervalEditor.fillIntoGrid(performanceGroup, 3); + addField(minIntervalEditor); + + // need to set layout again + performanceGroup.setLayout(groupLayout); +} + + @Override + protected void adjustGridLayout() { + // do nothing + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/preferences/IntegerWithBooleanFieldEditor.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/preferences/IntegerWithBooleanFieldEditor.java new file mode 100644 index 00000000000..758e3ac0947 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/preferences/IntegerWithBooleanFieldEditor.java @@ -0,0 +1,141 @@ +/******************************************************************************* + * 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.internal.ui.preferences; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; + +/** + * An integer field editor with an enablement check box. + */ +public class IntegerWithBooleanFieldEditor extends DecoratingIntegerFieldEditor { + + private final String fEnableKey; + private Button fCheckbox; + private boolean fWasSelected; + + public IntegerWithBooleanFieldEditor(String enableKey, String nameKey, String labelText, Composite parent) { + super(nameKey, labelText, parent); + fEnableKey= enableKey; + } + + public IntegerWithBooleanFieldEditor(String enableKey, String nameKey, String labelText, Composite parent, int textLimit) { + super(nameKey, labelText, parent, textLimit); + fEnableKey= enableKey; + } + + @Override + protected void doFillIntoGrid(Composite parent, int numColumns) { + getCheckboxControl(parent); + super.doFillIntoGrid(parent, numColumns); + } + + private Button getCheckboxControl(Composite parent) { + if (fCheckbox == null) { + Composite inner= new Composite(parent, SWT.NULL); + final GridLayout layout= new GridLayout(2, false); + layout.marginWidth = 0; + inner.setLayout(layout); + fCheckbox= new Button(inner, SWT.CHECK); + fCheckbox.setFont(parent.getFont()); + fCheckbox.setText(getLabelText()); + // create and hide label from base class + Label label = getLabelControl(inner); + label.setText(""); //$NON-NLS-1$ + label.setVisible(false); + fCheckbox.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + boolean isSelected = fCheckbox.getSelection(); + valueChanged(fWasSelected, isSelected); + fWasSelected = isSelected; + } + }); + } else { + checkParent(fCheckbox.getParent(), parent); + } + return fCheckbox; + } + + @Override + public Label getLabelControl(Composite parent) { + final Label label= getLabelControl(); + if (label == null) { + return super.getLabelControl(parent); + } else { + checkParent(label.getParent(), parent); + } + return label; + } + + protected void valueChanged(boolean oldValue, boolean newValue) { + if (oldValue != newValue) { + valueChanged(); + fireStateChanged(VALUE, oldValue, newValue); + getTextControl().setEnabled(newValue); + getLabelControl().setEnabled(newValue); + } + } + + @Override + protected boolean checkState() { + if (fCheckbox != null && !fCheckbox.getSelection()) { + clearErrorMessage(); + return true; + } + return super.checkState(); + } + + @Override + protected void doLoad() { + super.doLoad(); + if (fCheckbox != null) { + boolean value = getPreferenceStore().getBoolean(fEnableKey); + fCheckbox.setSelection(value); + fWasSelected = value; + getTextControl().setEnabled(value); + getLabelControl().setEnabled(value); + } + } + + @Override + protected void doLoadDefault() { + super.doLoadDefault(); + if (fCheckbox != null) { + boolean value = getPreferenceStore().getDefaultBoolean(fEnableKey); + fCheckbox.setSelection(value); + fWasSelected = value; + getTextControl().setEnabled(value); + getLabelControl().setEnabled(value); + } + } + + @Override + protected void doStore() { + super.doStore(); + getPreferenceStore().setValue(fEnableKey, fCheckbox.getSelection()); + } + + /** + * Returns this field editor's current boolean value. + * + * @return the value + */ + public boolean getBooleanValue() { + return fCheckbox.getSelection(); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/preferences/MessagesForPreferences.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/preferences/MessagesForPreferences.java new file mode 100644 index 00000000000..24495fd1cc8 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/preferences/MessagesForPreferences.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * 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.internal.ui.preferences; + +import org.eclipse.osgi.util.NLS; + +/** + * Preference strings. + */ +class MessagesForPreferences extends NLS { + private static final String BUNDLE_NAME= "org.eclipse.cdt.dsf.debug.internal.ui.preferences.messages"; //$NON-NLS-1$ + + public static String DsfDebugPreferencePage_description; + public static String DsfDebugPreferencePage_limitStackFrames_label; + + public static String DsfDebugPreferencePage_minStepInterval_label; + public static String DsfDebugPreferencePage_performanceGroup_label; + + public static String DsfDebugPreferencePage_waitForViewUpdate_label; + + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, MessagesForPreferences.class); + } + + private MessagesForPreferences() { + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/preferences/StringWithBooleanFieldEditor.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/preferences/StringWithBooleanFieldEditor.java new file mode 100644 index 00000000000..0a131ba1ce1 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/preferences/StringWithBooleanFieldEditor.java @@ -0,0 +1,146 @@ +/******************************************************************************* + * 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.internal.ui.preferences; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; + +/** + * A string field editor with an enablement check box. + */ +public class StringWithBooleanFieldEditor extends DecoratingStringFieldEditor { + + private final String fEnableKey; + private Button fCheckbox; + private boolean fWasSelected; + + public StringWithBooleanFieldEditor(String enableKey, String nameKey, String labelText, Composite parent) { + super(nameKey, labelText, parent); + fEnableKey= enableKey; + } + + public StringWithBooleanFieldEditor(String enableKey, String nameKey, String labelText, int width, Composite parent) { + super(nameKey, labelText, width, parent); + fEnableKey= enableKey; + } + + public StringWithBooleanFieldEditor(String enableKey, String nameKey, String labelText, int width, int strategy, Composite parent) { + super(nameKey, labelText, width, strategy, parent); + fEnableKey= enableKey; + } + + @Override + protected void doFillIntoGrid(Composite parent, int numColumns) { + getCheckboxControl(parent); + super.doFillIntoGrid(parent, numColumns); + } + + private Button getCheckboxControl(Composite parent) { + if (fCheckbox == null) { + Composite inner= new Composite(parent, SWT.NULL); + final GridLayout layout= new GridLayout(2, false); + layout.marginWidth = 0; + inner.setLayout(layout); + fCheckbox= new Button(inner, SWT.CHECK); + fCheckbox.setFont(parent.getFont()); + fCheckbox.setText(getLabelText()); + // create and hide label from base class + Label label = getLabelControl(inner); + label.setText(""); //$NON-NLS-1$ + label.setVisible(false); + fCheckbox.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + boolean isSelected = fCheckbox.getSelection(); + valueChanged(fWasSelected, isSelected); + fWasSelected = isSelected; + } + }); + } else { + checkParent(fCheckbox.getParent(), parent); + } + return fCheckbox; + } + + @Override + public Label getLabelControl(Composite parent) { + final Label label= getLabelControl(); + if (label == null) { + return super.getLabelControl(parent); + } else { + checkParent(label.getParent(), parent); + } + return label; + } + + protected void valueChanged(boolean oldValue, boolean newValue) { + if (oldValue != newValue) { + valueChanged(); + fireStateChanged(VALUE, oldValue, newValue); + getTextControl().setEnabled(newValue); + getLabelControl().setEnabled(newValue); + } + } + + @Override + protected boolean checkState() { + if (fCheckbox != null && !fCheckbox.getSelection()) { + clearErrorMessage(); + return true; + } + return super.checkState(); + } + + @Override + protected void doLoad() { + super.doLoad(); + if (fCheckbox != null) { + boolean value = getPreferenceStore().getBoolean(fEnableKey); + fCheckbox.setSelection(value); + fWasSelected = value; + getTextControl().setEnabled(value); + getLabelControl().setEnabled(value); + } + } + + @Override + protected void doLoadDefault() { + super.doLoadDefault(); + if (fCheckbox != null) { + boolean value = getPreferenceStore().getDefaultBoolean(fEnableKey); + fCheckbox.setSelection(value); + fWasSelected = value; + getTextControl().setEnabled(value); + getLabelControl().setEnabled(value); + } + } + + @Override + protected void doStore() { + super.doStore(); + getPreferenceStore().setValue(fEnableKey, fCheckbox.getSelection()); + } + + /** + * Returns this field editor's current boolean value. + * + * @return the value + */ + public boolean getBooleanValue() { + return fCheckbox.getSelection(); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/preferences/messages.properties b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/preferences/messages.properties new file mode 100644 index 00000000000..9452d1f5779 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/preferences/messages.properties @@ -0,0 +1,16 @@ +############################################################################### +# 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 +############################################################################### + +DsfDebugPreferencePage_description=General settings for debuggers using Debug Services Framework (DSF): +DsfDebugPreferencePage_limitStackFrames_label=Limit number of stack frames to +DsfDebugPreferencePage_minStepInterval_label=Minimum interval between steps (in milliseconds) +DsfDebugPreferencePage_performanceGroup_label=Performance +DsfDebugPreferencePage_waitForViewUpdate_label=Wait for views to update after every step diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/actions/MessagesForVMActions.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/actions/MessagesForVMActions.java new file mode 100644 index 00000000000..267f2f0db79 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/actions/MessagesForVMActions.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * 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.internal.ui.viewmodel.actions; + +import org.eclipse.osgi.util.NLS; + +public class MessagesForVMActions extends NLS { + + private static final String BUNDLE_NAME = "org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.actions.messages"; //$NON-NLS-1$ + + public static String RetargetDebugContextAction_ErrorDialog_title; + public static String RetargetDebugContextAction_ErrorDialog_message; + + public static String UpdatePoliciesContribution_EmptyPoliciesList_label; + public static String UpdateScopesContribution_EmptyScopesList_label; + + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, MessagesForVMActions.class); + } + + private MessagesForVMActions() {} +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/actions/RefreshActionDelegate.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/actions/RefreshActionDelegate.java new file mode 100644 index 00000000000..2c8be222e94 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/actions/RefreshActionDelegate.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * 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.internal.ui.viewmodel.actions; + +import org.eclipse.cdt.dsf.debug.ui.viewmodel.actions.AbstractVMProviderActionDelegate; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.update.ICachingVMProvider; +import org.eclipse.debug.ui.contexts.DebugContextEvent; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.ui.IViewPart; + +/** + * + */ +public class RefreshActionDelegate extends AbstractVMProviderActionDelegate { + + public void run(IAction action) { + IVMProvider provider = getVMProvider(); + if (provider instanceof ICachingVMProvider) { + ((ICachingVMProvider)provider).refresh(); + } + } + + @Override + public void init(IViewPart view) { + super.init(view); + getAction().setEnabled(getVMProvider() instanceof ICachingVMProvider); + } + + @Override + public void debugContextChanged(DebugContextEvent event) { + super.debugContextChanged(event); + getAction().setEnabled(getVMProvider() instanceof ICachingVMProvider); + } + + @Override + public void selectionChanged(IAction action, ISelection selection) { + super.selectionChanged(action, selection); + getAction().setEnabled(getVMProvider() instanceof ICachingVMProvider); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/actions/RefreshAllRetargetAction.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/actions/RefreshAllRetargetAction.java new file mode 100644 index 00000000000..b07ab995be6 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/actions/RefreshAllRetargetAction.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.internal.ui.viewmodel.actions; + +import org.eclipse.cdt.dsf.debug.ui.viewmodel.actions.IRefreshAllTarget; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jface.viewers.ISelection; + +/** + * + */ +public class RefreshAllRetargetAction extends RetargetDebugContextAction { + + @Override + protected boolean canPerformAction(Object target, ISelection selection) { + return true; + } + + @Override + protected Class<?> getAdapterClass() { + return IRefreshAllTarget.class; + } + + @Override + protected void performAction(Object target, ISelection debugContext) throws CoreException { + ((IRefreshAllTarget)target).refresh(debugContext); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/actions/RefreshHandler.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/actions/RefreshHandler.java new file mode 100644 index 00000000000..46ab3da66e7 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/actions/RefreshHandler.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.internal.ui.viewmodel.actions; + +import org.eclipse.cdt.dsf.debug.ui.viewmodel.actions.VMHandlerUtils; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.update.ICachingVMProvider; +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; + +public class RefreshHandler extends AbstractHandler { + + public Object execute(ExecutionEvent event) throws ExecutionException { + IVMProvider vmProvider = VMHandlerUtils.getActiveVMProvider(event); + + if (vmProvider instanceof ICachingVMProvider) { + ((ICachingVMProvider)vmProvider).refresh(); + } + + return null; + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/actions/RetargetDebugContextAction.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/actions/RetargetDebugContextAction.java new file mode 100644 index 00000000000..ed46192c29d --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/actions/RetargetDebugContextAction.java @@ -0,0 +1,151 @@ +/******************************************************************************* + * 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, Inc. - initial implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.actions; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.IAdapterManager; +import org.eclipse.core.runtime.Platform; +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.dialogs.ErrorDialog; +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.IWorkbenchWindow; +import org.eclipse.ui.IWorkbenchWindowActionDelegate; + +/** + * Base class for actions which delegate functionality to an adapter retrieved + * from the current debug context. + * + * @since 1.1 + */ +abstract public class RetargetDebugContextAction implements IWorkbenchWindowActionDelegate, IDebugContextListener, IActionDelegate2 { + + private IWorkbenchWindow fWindow = null; + private IAction fAction = null; + private ISelection fDebugContext; + private Object fTargetAdapter = null; + + /* (non-Javadoc) + * @see org.eclipse.ui.IWorkbenchWindowActionDelegate#init(org.eclipse.ui.IWorkbenchWindow) + */ + public void init(IWorkbenchWindow window) { + fWindow = window; + IDebugContextService debugContextService = DebugUITools.getDebugContextManager().getContextService(fWindow); + debugContextService.addPostDebugContextListener(this); + fDebugContext = debugContextService.getActiveContext(); + update(); + } + + + public void selectionChanged(IAction action, ISelection selection) { + if (fAction != action) { + fAction = action; + } + // Update on debug context changed events + } + + public void runWithEvent(IAction action, Event event) { + run(action); + } + + public void run(IAction action) { + if (fTargetAdapter != null) { + try { + performAction(fTargetAdapter, fDebugContext); + } catch (CoreException e) { + ErrorDialog.openError(fWindow.getShell(), MessagesForVMActions.RetargetDebugContextAction_ErrorDialog_title, MessagesForVMActions.RetargetDebugContextAction_ErrorDialog_message, e.getStatus()); + } + } + } + + /** + * Returns whether the specific operation is supported. + * + * @param target the target adapter + * @param selection the selection to verify the operation on + * @param part the part the operation has been requested on + * @return whether the operation can be performed + */ + protected abstract boolean canPerformAction(Object target, ISelection debugContext); + + /** + * Performs the specific breakpoint toggling. + * + * @param selection selection in the active part + * @param part active part + * @throws CoreException if an exception occurrs + */ + protected abstract void performAction(Object target, ISelection debugContext) throws CoreException; + + /** + * Returns the type of adapter (target) this action works on. + * + * @return the type of adapter this action works on + */ + protected abstract Class<?> getAdapterClass(); + + public void init(IAction action) { + fAction = action; + } + + /* (non-Javadoc) + * @see org.eclipse.ui.texteditor.IUpdate#update() + */ + public void update() { + if (fAction == null) { + return; + } + fTargetAdapter = null; + if (fDebugContext instanceof IStructuredSelection) { + IStructuredSelection ss = (IStructuredSelection) fDebugContext; + if (!ss.isEmpty()) { + Object object = ss.getFirstElement(); + if (object instanceof IAdaptable) { + fTargetAdapter = getAdapter((IAdaptable) object); + if (fTargetAdapter != null) { + fAction.setEnabled(canPerformAction(fTargetAdapter, fDebugContext)); + return; + } + } + } + } + fAction.setEnabled(false); + } + + public void dispose() { + DebugUITools.getDebugContextManager().getContextService(fWindow).removePostDebugContextListener(this); + fTargetAdapter = null; + } + + public void debugContextChanged(DebugContextEvent event) { + fDebugContext = event.getContext(); + update(); + } + + protected Object getAdapter(IAdaptable adaptable) { + Object adapter = adaptable.getAdapter(getAdapterClass()); + if (adapter == null) { + IAdapterManager adapterManager = Platform.getAdapterManager(); + if (adapterManager.hasAdapter(adaptable, getAdapterClass().getName())) { + fTargetAdapter = adapterManager.loadAdapter(adaptable, getAdapterClass().getName()); + } + } + return adapter; + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/actions/UpdatePoliciesContribution.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/actions/UpdatePoliciesContribution.java new file mode 100644 index 00000000000..93a79b5d819 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/actions/UpdatePoliciesContribution.java @@ -0,0 +1,110 @@ +/******************************************************************************* + * 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.internal.ui.viewmodel.actions; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.cdt.dsf.debug.ui.viewmodel.actions.VMHandlerUtils; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.update.ICachingVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.update.IVMUpdatePolicy; +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 update policies + * in the current view. + * + * @since 1.1 + */ +public class UpdatePoliciesContribution extends CompoundContributionItem implements IWorkbenchContribution { + + private class SelectUpdatePolicyAction extends Action { + private final ICachingVMProvider fProvider; + private final IVMUpdatePolicy fPolicy; + SelectUpdatePolicyAction(ICachingVMProvider provider, IVMUpdatePolicy policy) { + super(policy.getName(), AS_RADIO_BUTTON); + fProvider = provider; + fPolicy = policy; + } + + @Override + public void run() { + if (isChecked()) { + fProvider.setActiveUpdatePolicy(fPolicy); + } + } + } + + private IServiceLocator fServiceLocator; + + private static IContributionItem[] NO_UPDATE_POLICIES_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(MessagesForVMActions.UpdatePoliciesContribution_EmptyPoliciesList_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 || !(provider instanceof ICachingVMProvider)) { + return NO_UPDATE_POLICIES_CONTRIBUTION_ITEMS; + } + ICachingVMProvider cachingProvider = (ICachingVMProvider)provider; + + IVMUpdatePolicy[] policies = cachingProvider.getAvailableUpdatePolicies(); + IVMUpdatePolicy activePolicy = cachingProvider.getActiveUpdatePolicy(); + + List<Action> actions = new ArrayList<Action>(policies.length); + for (IVMUpdatePolicy policy : policies) { + Action action = new SelectUpdatePolicyAction(cachingProvider, policy); + if (policy.getID().equals(activePolicy.getID())) { + action.setChecked(true); + } + actions.add(action); + } + + if ( actions.isEmpty() ) { + return NO_UPDATE_POLICIES_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/internal/ui/viewmodel/actions/UpdatePoliciesPropertyTester.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/actions/UpdatePoliciesPropertyTester.java new file mode 100644 index 00000000000..dd2d48aa675 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/actions/UpdatePoliciesPropertyTester.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * 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.internal.ui.viewmodel.actions; + +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.cdt.dsf.ui.viewmodel.update.ICachingVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.update.IVMUpdatePolicy; +import org.eclipse.core.expressions.PropertyTester; +import org.eclipse.debug.ui.IDebugView; +import org.eclipse.ui.IWorkbenchPart; + +/** + * Property tester for update policy information available through the given + * object. The object being tested should be either an {@link IVMContext}, + * through which an instance of {@link ICachingVMProvider} 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 Caching View Model provider is used to test the given property. + * <p> + * Three properties are supported: + * <ul> + * <li> "areUpdatePoliciesSupported" - Checks whether update policies are + * available at all given the receiver.</li> + * <li> "isUpdatePolicyAvailable" - Checks whether the update policy in the + * expected value is available for the given receiver.</li> + * <li> "isUpdatePolicyActive" - Checks whether the policy given in the expected + * value is the currently active policy for the given receiver.</li> + * </ul> + * </p> + */ +public class UpdatePoliciesPropertyTester extends PropertyTester { + + private static final String SUPPORTED = "areUpdatePoliciesSupported"; //$NON-NLS-1$ + private static final String AVAILABLE = "isUpdatePolicyAvailable"; //$NON-NLS-1$ + private static final String ACTIVE = "isUpdatePolicyActive"; //$NON-NLS-1$ + + public boolean test(Object receiver, String property, Object[] args, Object expectedValue) { + if (receiver instanceof IVMContext) { + IVMProvider provider = ((IVMContext)receiver).getVMNode().getVMProvider(); + if (provider instanceof ICachingVMProvider) { + return testProvider((ICachingVMProvider)provider, property, expectedValue); + } + } else if (receiver instanceof IDebugView) { + IVMProvider provider = VMHandlerUtils.getVMProviderForPart((IDebugView)receiver); + if (provider instanceof ICachingVMProvider) { + return testProvider((ICachingVMProvider)provider, property, expectedValue); + } + } + return false; + } + + private boolean testProvider(ICachingVMProvider provider, String property, Object expectedValue) { + if (SUPPORTED.equals(property)) { + return true; + } else if (AVAILABLE.equals(property)) { + for (IVMUpdatePolicy policy : provider.getAvailableUpdatePolicies()) { + if (policy.getID().equals(expectedValue)) { + return true; + } + return false; + } + } else if (ACTIVE.equals(property)) { + return expectedValue != null && expectedValue.equals(provider.getActiveUpdatePolicy().getID()); + } + return false; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/actions/UpdateScopesContribution.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/actions/UpdateScopesContribution.java new file mode 100644 index 00000000000..a4770e0ff34 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/actions/UpdateScopesContribution.java @@ -0,0 +1,110 @@ +/******************************************************************************* + * 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.internal.ui.viewmodel.actions; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.cdt.dsf.debug.ui.viewmodel.actions.VMHandlerUtils; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.update.ICachingVMProviderExtension; +import org.eclipse.cdt.dsf.ui.viewmodel.update.IVMUpdateScope; +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 update scopes + * in the current view. + * + * @since 1.1 + */ +public class UpdateScopesContribution extends CompoundContributionItem implements IWorkbenchContribution { + + private class SelectUpdateScopeAction extends Action { + private final ICachingVMProviderExtension fProvider; + private final IVMUpdateScope fScope; + SelectUpdateScopeAction(ICachingVMProviderExtension provider, IVMUpdateScope scope) { + super(scope.getName(), AS_RADIO_BUTTON); + fProvider = provider; + fScope = scope; + } + + @Override + public void run() { + if (isChecked()) { + fProvider.setActiveUpdateScope(fScope); + } + } + } + + 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(MessagesForVMActions.UpdateScopesContribution_EmptyScopesList_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 || !(provider instanceof ICachingVMProviderExtension)) { + return NO_BREAKPOINT_TYPES_CONTRIBUTION_ITEMS; + } + ICachingVMProviderExtension cachingProvider = (ICachingVMProviderExtension)provider; + + IVMUpdateScope[] scopes = cachingProvider.getAvailableUpdateScopes(); + IVMUpdateScope activeScope = cachingProvider.getActiveUpdateScope(); + + List<Action> actions = new ArrayList<Action>(scopes.length); + for (IVMUpdateScope scope : scopes) { + Action action = new SelectUpdateScopeAction(cachingProvider, scope); + if (scope.getID().equals(activeScope.getID())) { + 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/internal/ui/viewmodel/actions/UpdateScopesPropertyTester.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/actions/UpdateScopesPropertyTester.java new file mode 100644 index 00000000000..752c71ce86d --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/actions/UpdateScopesPropertyTester.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * 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.internal.ui.viewmodel.actions; + +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.cdt.dsf.ui.viewmodel.update.ICachingVMProviderExtension; +import org.eclipse.cdt.dsf.ui.viewmodel.update.IVMUpdateScope; +import org.eclipse.core.expressions.PropertyTester; +import org.eclipse.debug.ui.IDebugView; +import org.eclipse.ui.IWorkbenchPart; + +/** + * Property tester for update scope information available through the given + * object. The object being tested should be either an {@link IVMContext}, + * through which an instance of {@link ICachingVMProviderExtension} 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 Caching View Model provider is used to test the given property. + * <p> + * Three properties are supported: + * <ul> + * <li> "areUpdateScopesSupported" - Checks whether update scopes are + * available at all given the receiver.</li> + * <li> "isUpdateScopeAvailable" - Checks whether the update scope in the + * expected value is available for the given receiver.</li> + * <li> "isUpdateScopeActive" - Checks whether the scope given in the expected + * value is the currently active scope for the given receiver.</li> + * </ul> + * </p> + */ +public class UpdateScopesPropertyTester extends PropertyTester { + + private static final String SUPPORTED = "areUpdateScopesSupported"; //$NON-NLS-1$ + private static final String AVAILABLE = "isUpdateScopeAvailable"; //$NON-NLS-1$ + private static final String ACTIVE = "isUpdateScopeActive"; //$NON-NLS-1$ + + public boolean test(Object receiver, String property, Object[] args, Object expectedValue) { + if (receiver instanceof IVMContext) { + IVMProvider provider = ((IVMContext)receiver).getVMNode().getVMProvider(); + if (provider instanceof ICachingVMProviderExtension) { + return testProvider((ICachingVMProviderExtension)provider, property, expectedValue); + } + } else if (receiver instanceof IDebugView) { + IVMProvider provider = VMHandlerUtils.getVMProviderForPart((IDebugView)receiver); + if (provider instanceof ICachingVMProviderExtension) { + return testProvider((ICachingVMProviderExtension)provider, property, expectedValue); + } + } + return false; + } + + private boolean testProvider(ICachingVMProviderExtension provider, String property, Object expectedValue) { + if (SUPPORTED.equals(property)) { + return true; + } else if (AVAILABLE.equals(property)) { + for (IVMUpdateScope scope : provider.getAvailableUpdateScopes()) { + if (scope.getID().equals(expectedValue)) { + return true; + } + return false; + } + } else if (ACTIVE.equals(property)) { + return expectedValue != null && expectedValue.equals(provider.getActiveUpdateScope().getID()); + } + return false; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/actions/messages.properties b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/actions/messages.properties new file mode 100644 index 00000000000..2d2ad435bfd --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/actions/messages.properties @@ -0,0 +1,16 @@ +############################################################################### +# 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 +############################################################################### + +RetargetDebugContextAction_ErrorDialog_title=Error +RetargetDebugContextAction_ErrorDialog_message=Operation failed + +UpdatePoliciesContribution_EmptyPoliciesList_label=No update policies for current selection +UpdateScopesContribution_EmptyScopesList_label=No update scopes for current selection diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/detailsupport/DetailPaneMaxLengthAction.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/detailsupport/DetailPaneMaxLengthAction.java new file mode 100644 index 00000000000..b6ac175becc --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/detailsupport/DetailPaneMaxLengthAction.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * 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, Inc. - extended implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.detailsupport; + +import org.eclipse.cdt.dsf.debug.ui.IDsfDebugUIConstants; +import org.eclipse.jface.action.Action; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.PlatformUI; + +/** + * Opens a dialog so that the user can enter the maximum length in characters that + * the detail pane should display. + * + * @see DetailPaneMaxLengthDialog + * @since 3.0 + */ +public class DetailPaneMaxLengthAction extends Action { + + private Shell fDialogShell; + + public DetailPaneMaxLengthAction(Shell dialogShell){ + super(MessagesForDetailPane.PaneMaxLengthAction_MaxLength); + fDialogShell = dialogShell; + + PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IDsfDebugUIConstants.DETAIL_PANE_MAX_LENGTH_ACTION); + + } + + @Override + public void run() { + DetailPaneMaxLengthDialog dialog = new DetailPaneMaxLengthDialog(fDialogShell); + dialog.open(); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/detailsupport/DetailPaneMaxLengthDialog.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/detailsupport/DetailPaneMaxLengthDialog.java new file mode 100644 index 00000000000..6d12c00ace1 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/detailsupport/DetailPaneMaxLengthDialog.java @@ -0,0 +1,180 @@ +/******************************************************************************* + * 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, Inc. - extended implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.detailsupport; + +import org.eclipse.cdt.dsf.debug.ui.IDsfDebugUIConstants; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.jface.dialogs.IInputValidator; +import org.eclipse.jface.dialogs.TrayDialog; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.PlatformUI; + +/** + * Provides a dialog for changing the maximum length allowed in the detail pane + * + * @since 3.0 + */ +public class DetailPaneMaxLengthDialog extends TrayDialog { + + private static final String SETTINGS_ID = DsfUIPlugin.PLUGIN_ID + ".MAX_DETAILS_LENGTH_DIALOG"; //$NON-NLS-1$ + + private Text fTextWidget; + private Text fErrorTextWidget; + private String fErrorMessage; + private String fValue; + private IInputValidator fValidator; + + /** + * Constructs a new dialog on the given shell. + * + * @param parent shell + */ + public DetailPaneMaxLengthDialog(Shell parent) { + super(parent); + setShellStyle(getShellStyle() | SWT.RESIZE); + fValue = Integer.toString(DsfUIPlugin.getDefault().getPreferenceStore().getInt(IDsfDebugUIConstants.PREF_MAX_DETAIL_LENGTH)); + fValidator = new IInputValidator() { + public String isValid(String newText) { + try { + int num = Integer.parseInt(newText); + if (num < 0) { + return MessagesForDetailPane.PaneMaxLengthDialog_IntegerCannotBeNegative; + } + } catch (NumberFormatException e) { + return MessagesForDetailPane.PaneMaxLengthDialog_EnterAnInteger; + } + return null; + } + + }; + } + + /* (non-Javadoc) + * @see org.eclipse.ui.dialogs.SelectionDialog#getDialogBoundsSettings() + */ + @Override + protected IDialogSettings getDialogBoundsSettings() { + IDialogSettings settings = DsfUIPlugin.getDefault().getDialogSettings(); + IDialogSettings section = settings.getSection(SETTINGS_ID); + if (section == null) { + section = settings.addNewSection(SETTINGS_ID); + } + return section; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.dialogs.Dialog#createContents(org.eclipse.swt.widgets.Composite) + */ + @Override + protected Control createContents(Composite parent) { + getShell().setText(MessagesForDetailPane.PaneMaxLengthDialog_ConfigureDetails); + Control contents = super.createContents(parent); + PlatformUI.getWorkbench().getHelpSystem().setHelp(getDialogArea(), IDsfDebugUIConstants.DETAIL_PANE_MAX_LENGTH_ACTION); + return contents; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite) + */ + @Override + protected Control createDialogArea(Composite parent) { + Composite composite = (Composite) super.createDialogArea(parent); + Label label = new Label(composite, SWT.WRAP); + label.setText(MessagesForDetailPane.PaneMaxLengthDialog_MaxCharactersToDisplay); + GridData data = new GridData(GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL | GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_CENTER); + data.widthHint = convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH); + label.setLayoutData(data); + label.setFont(parent.getFont()); + fTextWidget = new Text(composite, SWT.SINGLE | SWT.BORDER); + fTextWidget.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL)); + fTextWidget.setText(fValue); + fTextWidget.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + validateInput(); + fValue = fTextWidget.getText(); + } + }); + fErrorTextWidget = new Text(composite, SWT.READ_ONLY); + fErrorTextWidget.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL + | GridData.HORIZONTAL_ALIGN_FILL)); + fErrorTextWidget.setBackground(fErrorTextWidget.getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND)); + setErrorMessage(fErrorMessage); + applyDialogFont(composite); + return composite; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.dialogs.Dialog#okPressed() + */ + @Override + protected void okPressed() { + String text = getValue(); + try { + DsfUIPlugin.getDefault().getPreferenceStore().setValue(IDsfDebugUIConstants.PREF_MAX_DETAIL_LENGTH, Integer.parseInt(text)); + } + catch (NumberFormatException e) { + DsfUIPlugin.log(e); + } + super.okPressed(); + } + + /** + * Returns the string typed into this input dialog. + * + * @return the input string + * @since 3.3 + */ + public String getValue() { + return fValue; + } + + /** + * Validates the current input + * @since 3.3 + */ + private void validateInput() { + String errorMessage = null; + if (fValidator != null) { + errorMessage = fValidator.isValid(fTextWidget.getText()); + } + setErrorMessage(errorMessage); + } + + /** + * Sets the current error message or none if null + * @param errorMessage + * @since 3.3 + */ + public void setErrorMessage(String errorMessage) { + fErrorMessage = errorMessage; + if (fErrorTextWidget != null && !fErrorTextWidget.isDisposed()) { + fErrorTextWidget.setText(errorMessage == null ? "" : errorMessage); //$NON-NLS-1$ + fErrorTextWidget.getParent().update(); + // Access the ok button by id, in case clients have overridden button creation. + // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=113643 + Control button = getButton(IDialogConstants.OK_ID); + if (button != null) { + button.setEnabled(errorMessage == null); + } + } + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/detailsupport/DetailPaneWordWrapAction.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/detailsupport/DetailPaneWordWrapAction.java new file mode 100644 index 00000000000..1458aeb22fc --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/detailsupport/DetailPaneWordWrapAction.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * 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, Inc. - extended implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.detailsupport; + +import org.eclipse.cdt.dsf.debug.ui.IDsfDebugUIConstants; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.ui.PlatformUI; + +/** + * An check box action that allows the word wrap property to be set, determining if the detail pane + * should wrap text. + */ +public class DetailPaneWordWrapAction extends Action { + + ITextViewer fTextViewer; + + public DetailPaneWordWrapAction(ITextViewer textViewer) { + super(MessagesForDetailPane.PaneWordWrapAction_WrapText,IAction.AS_CHECK_BOX); + + PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IDsfDebugUIConstants.DETAIL_PANE_WORD_WRAP_ACTION); + + fTextViewer = textViewer; + setEnabled(true); + + boolean prefSetting = DsfUIPlugin.getDefault().getPreferenceStore().getBoolean(IDsfDebugUIConstants.PREF_DETAIL_PANE_WORD_WRAP); + fTextViewer.getTextWidget().setWordWrap(prefSetting); + setChecked(prefSetting); + + + } + + /* (non-Javadoc) + * @see org.eclipse.jface.action.IAction#run() + */ + @Override + public void run() { + fTextViewer.getTextWidget().setWordWrap(isChecked()); + DsfUIPlugin.getDefault().getPreferenceStore().setValue(IDsfDebugUIConstants.PREF_DETAIL_PANE_WORD_WRAP,isChecked()); + DsfUIPlugin.getDefault().savePluginPreferences(); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/detailsupport/MessagesForDetailPane.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/detailsupport/MessagesForDetailPane.java new file mode 100644 index 00000000000..a5a76e64684 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/detailsupport/MessagesForDetailPane.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * 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, Inc. - extended implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.detailsupport; + +import org.eclipse.osgi.util.NLS; + +public class MessagesForDetailPane extends NLS { + + private static final String BUNDLE_NAME = "org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.detailsupport.messages"; //$NON-NLS-1$ + + public static String NumberFormatDetailPane_Name; + public static String NumberFormatDetailPane_Description; + + public static String DetailPane_Copy; + public static String DetailPane_LabelPattern; + public static String DetailPane_Select_All; + public static String PaneWordWrapAction_WrapText; + public static String PaneMaxLengthAction_MaxLength; + public static String PaneMaxLengthDialog_ConfigureDetails; + public static String PaneMaxLengthDialog_MaxCharactersToDisplay; + public static String PaneMaxLengthDialog_IntegerCannotBeNegative; + public static String PaneMaxLengthDialog_EnterAnInteger; + + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, MessagesForDetailPane.class); + } + + private MessagesForDetailPane() {} +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/detailsupport/TextViewerAction.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/detailsupport/TextViewerAction.java new file mode 100644 index 00000000000..77d74a60db1 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/detailsupport/TextViewerAction.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * 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, Inc. - extended implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.detailsupport; + +import org.eclipse.jface.action.Action; +import org.eclipse.jface.text.ITextOperationTarget; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.ui.texteditor.IUpdate; + +/** + * Common function for actions that operate on a text viewer. + * <p> + * Clients may subclass this class. + * </p> + * @since 3.0 + */ +public class TextViewerAction extends Action implements IUpdate { + + private int fOperationCode= -1; + private ITextOperationTarget fOperationTarget; + + /** + * Constructs a new action in the given text viewer with + * the specified operation code. + * + * @param viewer + * @param operationCode + */ + public TextViewerAction(ITextViewer viewer, int operationCode) { + fOperationCode= operationCode; + fOperationTarget= viewer.getTextOperationTarget(); + update(); + } + + /* (non-Javadoc) + * @see org.eclipse.ui.texteditor.IUpdate#update() + * + * Updates the enabled state of the action. + * Fires a property change if the enabled state changes. + * + * @see org.eclipse.jface.action.Action#firePropertyChange(String, Object, Object) + */ + public void update() { + + boolean wasEnabled= isEnabled(); + boolean isEnabled= (fOperationTarget != null && fOperationTarget.canDoOperation(fOperationCode)); + setEnabled(isEnabled); + + if (wasEnabled != isEnabled) { + firePropertyChange(ENABLED, wasEnabled ? Boolean.TRUE : Boolean.FALSE, isEnabled ? Boolean.TRUE : Boolean.FALSE); + } + } + + /* (non-Javadoc) + * @see org.eclipse.jface.action.IAction#run() + */ + @Override + public void run() { + if (fOperationCode != -1 && fOperationTarget != null) { + fOperationTarget.doOperation(fOperationCode); + } + } + + /** + * Configures this action with a label, tool tip, and description. + * + * @param text action label + * @param toolTipText action tool tip + * @param description action description + */ + public void configureAction(String text, String toolTipText, String description) { + setText(text); + setToolTipText(toolTipText); + setDescription(description); + } +} + diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/detailsupport/messages.properties b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/detailsupport/messages.properties new file mode 100644 index 00000000000..1995fe0865d --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/detailsupport/messages.properties @@ -0,0 +1,25 @@ +############################################################################### +# 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 Inc - copied for non-restricted version for DSDP/DD/DSF +############################################################################### + +NumberFormatDetailPane_Name=Number Formats Viewer +NumberFormatDetailPane_Description=Detail viewer showing selected variable in all available formats. + +DetailPane_LabelPattern={0} : {1} +DetailPane_Select_All=Select &All +DetailPane_Copy=&Copy + +PaneWordWrapAction_WrapText=&Wrap Text +PaneMaxLengthAction_MaxLength=&Max Length... +PaneMaxLengthDialog_ConfigureDetails=Configure Details Pane +PaneMaxLengthDialog_MaxCharactersToDisplay=&Maximum characters to display in details pane (0 = unlimited): +PaneMaxLengthDialog_IntegerCannotBeNegative=Integer must be non-negative +PaneMaxLengthDialog_EnterAnInteger=Enter an integer diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/numberformat/detail/NumberFormatDetailPane.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/numberformat/detail/NumberFormatDetailPane.java new file mode 100644 index 00000000000..8c355996444 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/numberformat/detail/NumberFormatDetailPane.java @@ -0,0 +1,1218 @@ +/******************************************************************************* + * 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, Inc. - extended implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.numberformat.detail; + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.detailsupport.DetailPaneMaxLengthAction; +import org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.detailsupport.DetailPaneWordWrapAction; +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.IExpressions; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues; +import org.eclipse.cdt.dsf.debug.service.IRegisters; +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; +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.IRegisterDMContext; +import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMData; +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.ui.viewmodel.IVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; +import org.eclipse.cdt.dsf.ui.viewmodel.update.AbstractCachingVMProvider; +import org.eclipse.core.commands.operations.IUndoContext; +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.core.model.IDebugModelProvider; +import org.eclipse.debug.core.model.IValue; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer; +import org.eclipse.debug.ui.IDebugModelPresentation; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.debug.ui.IDebugView; +import org.eclipse.debug.ui.IDetailPane; +import org.eclipse.debug.ui.IValueDetailListener; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.action.IMenuListener; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.action.IStatusLineManager; +import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.action.Separator; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.text.BadLocationException; +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.IFindReplaceTarget; +import org.eclipse.jface.text.ITextOperationTarget; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.IUndoManager; +import org.eclipse.jface.text.IUndoManagerExtension; +import org.eclipse.jface.text.source.SourceViewer; +import org.eclipse.jface.text.source.SourceViewerConfiguration; +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.ITreeSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.TreePath; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.events.FocusAdapter; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.KeyListener; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +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.IViewSite; +import org.eclipse.ui.IWorkbenchActionConstants; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchPartSite; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.actions.ActionFactory; +import org.eclipse.ui.operations.OperationHistoryActionHandler; +import org.eclipse.ui.operations.RedoActionHandler; +import org.eclipse.ui.operations.UndoActionHandler; +import org.eclipse.ui.progress.WorkbenchJob; +import org.eclipse.ui.texteditor.IAbstractTextEditorHelpContextIds; +import org.eclipse.ui.texteditor.ITextEditorActionConstants; +import org.eclipse.ui.texteditor.IUpdate; +import org.eclipse.ui.texteditor.IWorkbenchActionDefinitionIds; +import org.eclipse.ui.texteditor.StatusLineContributionItem; + +@SuppressWarnings("restriction") +public class NumberFormatDetailPane implements IDetailPane, IAdaptable, IPropertyChangeListener { + + /** + * 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; + + } + + /** + * 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; + } + + /** + * 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$ + protected static final String DETAIL_WORD_WRAP_ACTION = DsfUIPlugin.PLUGIN_ID + ".detail_pane_word_wrap"; //$NON-NLS-1$ + protected static final String DETAIL_MAX_LENGTH_ACTION = "MaxLength"; //$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 = "NumberFormatPane"; //$NON-NLS-1$ + + /** + * Data structure for the position label value. + */ + private static class PositionLabelValue { + + public int fValue; + + @Override + public String toString() { + return String.valueOf(fValue); + } + } + + /** + * Internal interface for a cursor listener. I.e. aggregation + * of mouse and key listener. + * @since 3.0 + */ + interface ICursorListener extends MouseListener, KeyListener { + } + + /** + * Job to compute the details for a selection + */ + class DetailJob extends Job implements IValueDetailListener { + private IPresentationContext fPresentationContext; + private Object fViewerInput; + private ITreeSelection fElements; + private boolean fFirst = true; + private IProgressMonitor fMonitor; + private boolean fComputed = false; + + public DetailJob(IPresentationContext context, Object viewerInput, ITreeSelection elements, + IDebugModelPresentation model) + { + super("compute variable details"); //$NON-NLS-1$ + setSystem(true); + fPresentationContext = context; + fViewerInput = viewerInput; + fElements = elements; + } + + /* + * This is the routine which will actually process the various format requests + * for a given element. It is expected and required that this routine will be + * called from within a DSF executor. + */ + private void putInformationIntoDetailPane( final AbstractCachingVMProvider provider, + final IVMNode node, + final TreePath path, + final IFormattedDataDMContext finalDmc , + final IFormattedValues service, + final IProgressMonitor monitor, + final String name ) { + + /* + * Now that we can process this one. Find out how many formats we can + * show this in. We will choose to show all of the supported formats. + * Since we are doing this in the background and the debug engines do + * typically cache the results so producing multiple formats will not + * typically be a burden. We should probably consider perhaps doing a + * preference where they can select what formats they want to show. + */ + + final DataRequestMonitor<String[]> getAvailableFormatsDone = + new DataRequestMonitor<String[]>(service.getExecutor(), null) { + @Override + protected void handleSuccess() { + if (monitor.isCanceled()) { + notifyAll(); + return; + } + + /* + * Now we have a set of formats for each one fire up an independent + * asynchronous request to get the data in that format. We do not + * go through the cache manager here because when the values are + * edited and written the cache is bypassed. + */ + String[] formats = getData(); + + final List<String> completedFormatStrings = new ArrayList<String>(); + + final CountingRequestMonitor countingRm = new CountingRequestMonitor(service.getExecutor(), null) { + @Override + protected void handleCompleted() { + + if (monitor.isCanceled()) { + notifyAll(); + return; + } + + /* + * We sort the array to make the strings appear always in the same order. + */ + + java.util.Collections.sort( completedFormatStrings ); + + int len = completedFormatStrings.size() ; + + if ( len == 0 ) { + detailComputed(null,""); //$NON-NLS-1$ + } + else { + /* + * Add the HEADER which identifies what is being represented. When there + * are multiple selections in the view the detail pane contains multiple + * entries. They would be all compressed together and even though the + * order of the entries is the order of the selections in the view and + * it is very hard to know what goes with what. This makes it easy. + */ + String finalResult = "Name : " + name + "\n"; //$NON-NLS-1$ //$NON-NLS-2$ + int cnt = 0 ; + + for (String str : completedFormatStrings) { + + finalResult += " " + str ; //$NON-NLS-1$ + + if ( ( ++ cnt ) < len ) { + finalResult += "\n" ; //$NON-NLS-1$ + } + } + + detailComputed(null,finalResult); + } + } + }; + + countingRm.setDoneCount(formats.length); + + for ( final String str : formats ) { + /* + * Format has been validated. Get the formatted value. + */ + final FormattedValueDMContext valueDmc = service.getFormattedValueContext(finalDmc, str); + + provider.getModelData( + node, + new IViewerUpdate() { + public void cancel() {} + public void done() {} + public Object getViewerInput() { return fViewerInput; } + public TreePath getElementPath() { return path; } + public Object getElement() { return path.getLastSegment(); } + public IPresentationContext getPresentationContext() { return fPresentationContext; } + public boolean isCanceled() { return monitor.isCanceled(); } + public void setStatus(IStatus status) {} + public IStatus getStatus() { return null; } + }, + service, valueDmc, + new DataRequestMonitor<FormattedValueDMData>(service.getExecutor(), null) { + @Override + public void handleCompleted() { + if (getStatus().isOK()) { + /* + * Show the information indicating the format. + */ + if ( str == IFormattedValues.HEX_FORMAT) { + completedFormatStrings.add("Hex.... : " + getData().getFormattedValue()); //$NON-NLS-1$ + } + else if ( str == IFormattedValues.OCTAL_FORMAT) { + completedFormatStrings.add("Octal.. : " + getData().getFormattedValue()); //$NON-NLS-1$ + } + else if ( str == IFormattedValues.NATURAL_FORMAT) { + completedFormatStrings.add("Natural : " + getData().getFormattedValue()); //$NON-NLS-1$ + } + else if ( str == IFormattedValues.BINARY_FORMAT) { + completedFormatStrings.add("Binary. : " + getData().getFormattedValue()); //$NON-NLS-1$ + } + else if ( str == IFormattedValues.DECIMAL_FORMAT) { + completedFormatStrings.add("Decimal : " + getData().getFormattedValue()); //$NON-NLS-1$ + } + else if ( str == IFormattedValues.STRING_FORMAT) { + completedFormatStrings.add("String : " + getData().getFormattedValue()); //$NON-NLS-1$ + } + else { + completedFormatStrings.add("Other.. : (" + str + ") " + getData().getFormattedValue()); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + countingRm.done(); + } + }, + provider.getExecutor()); + } + } + }; + + /* + * Get the supported formats. + */ + service.getAvailableFormats(finalDmc, getAvailableFormatsDone); + } + + /* (non-Javadoc) + * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) + */ + @Override + protected IStatus run(final IProgressMonitor monitor) { + + String message = null; + if ( fMonitor != null && ! fMonitor.isCanceled() ) { + fMonitor.setCanceled(true); + } + fMonitor = monitor; + TreePath[] paths = fElements.getPaths(); + for (int i = 0; i < paths.length; i++) { + if (monitor.isCanceled()) { + break; + } + final TreePath path = paths[i]; + Object element = paths[i].getLastSegment(); + + /* + * Make sure this is an element we want to deal with. + */ + if ( element instanceof IDMVMContext) { + IDMVMContext vmc = (IDMVMContext)element; + /* + * We are specifically looking to support the following Data Model Contexts + * + * IRegisterDMContext + * IBitFieldDMContext + * IExpressionDMContext + * + * At first you might think that we should just use the service which is + * associated with the dmc. But there are implementations where the data + * model contexts are extended but the services do not extend each other + * ( this is the case with the WindRiver OCD extensions for example ). + * + * So here we specifically look for the service which knows how to deal + * with the formatted data. + * + * Please note that the order or searching for the ancestor is important. + * A BitField Data Model Context will have a Register Data Model Context + * as its parent so if we search for a Register DMC first when we actually + * have a BitField DMC we will get the register and show the value of the + * register not the bit field. + */ + + DsfServicesTracker tracker = new DsfServicesTracker(DsfUIPlugin.getBundleContext(), vmc.getDMContext().getSessionId()); + + final AbstractCachingVMProvider provider = (AbstractCachingVMProvider)vmc.getAdapter(AbstractCachingVMProvider.class); + final IVMNode node = (IVMNode)vmc.getAdapter(IVMNode.class); + + final IBitFieldDMContext bitfieldDmc = DMContexts.getAncestorOfType(((IDMVMContext) element).getDMContext(), IBitFieldDMContext.class); + if ( bitfieldDmc != null ) { + /* + * Get the name so we can construct the header which identifies the detail + * set of values. + */ + final IRegisters regService = tracker.getService(IRegisters.class); + + regService.getExecutor().submit( + new Runnable() { + public void run() { + regService.getBitFieldData( + bitfieldDmc, + new DataRequestMonitor<IBitFieldDMData>(regService.getExecutor(), null) { + @Override + public void handleCompleted() { + if (getStatus().isOK()) { + putInformationIntoDetailPane(provider, node, path, bitfieldDmc , regService, monitor, getData().getName()); + } + } + } + ); + } + } + ); + } + else { + final IRegisterDMContext regDmc = DMContexts.getAncestorOfType(((IDMVMContext) element).getDMContext(), IRegisterDMContext.class); + + if ( regDmc != null ) { + /* + * Get the name so we can construct the header which identifies the detail + * set of values. + */ + final IRegisters regService = tracker.getService(IRegisters.class); + + regService.getExecutor().submit( + new Runnable() { + public void run() { + regService.getRegisterData( + regDmc, + new DataRequestMonitor<IRegisterDMData>(regService.getExecutor(), null) { + @Override + public void handleCompleted() { + if (getStatus().isOK()) { + putInformationIntoDetailPane(provider, node, path, regDmc , regService, monitor, getData().getName()); + } + } + } + ); + } + } + ); + } + else { + final IExpressionDMContext exprDmc = DMContexts.getAncestorOfType(((IDMVMContext) element).getDMContext(), IExpressionDMContext.class); + + if ( exprDmc != null ) { + final IExpressions exprService = tracker.getService(IExpressions.class);; + + exprService.getExecutor().submit( + new Runnable() { + public void run() { + putInformationIntoDetailPane(provider, node, path, exprDmc , exprService, monitor, exprDmc.getExpression()); + } + } + ); + } + else { + /* + * For whatever reason we are seeing some form of context we do not handle. So we + * will skip this one and try and process any remaining ones. At least this way + * we can fill the detail pane with those we do understand. + */ + continue; + } + } + } + tracker.dispose(); + + /* + * We need to wait until all the values are in. This causes the work + * to in effect be synchronous, but if we do not wait then when we + * are stepping fast if we exit before the job is finished then we + * will enter and start to update the pane before the previous work + * is actually done. This causes the data to be screwed up in an + * overlapped way. It should be the case that most of the jobs that + * occur in the middle will be cancelled, so there is not a lot of + * waste actually going on. + */ + + synchronized (this) { + try { + // wait for a max of 30 seconds for result, then cancel + wait(30000); + if (!fComputed) { + fMonitor.setCanceled(true); + } + } catch (InterruptedException e) { + break; + } + } + } + else { +// IValue val = null; +// if (element instanceof IVariable) { +// try { +// val = ((IVariable)element).getValue(); +// } catch (DebugException e) { +// detailComputed(null, e.getStatus().getMessage()); +// } +// } else if (element instanceof IExpression) { +// val = ((IExpression)element).getValue(); +// } + if (element instanceof String) { + message = (String) element; + } + else { + message = element.toString(); + } + fComputed = true; + } + // If no details were computed for the selected variable, clear the pane + // or use the message, if the variable was a java.lang.String + if (!fComputed){ + if (message == null) { + detailComputed(null,""); //$NON-NLS-1$ + } else { + detailComputed(null, message); + } + } + } + + return Status.OK_STATUS; + } + + /* (non-Javadoc) + * @see org.eclipse.core.runtime.jobs.Job#canceling() + */ + @Override + protected void canceling() { + super.canceling(); + synchronized (this) { + notifyAll(); + } + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.IValueDetailListener#detailComputed(org.eclipse.debug.core.model.IValue, java.lang.String) + */ + public void detailComputed(IValue value, final String result) { + synchronized (this) { + fComputed = true; + } + if (!fMonitor.isCanceled()) { + WorkbenchJob append = new WorkbenchJob("append details") { //$NON-NLS-1$ + @Override + public IStatus runInUIThread(IProgressMonitor monitor) { + if (!fMonitor.isCanceled()) { + String insert = result; + int length = 0; + if (!fFirst) { + length = getDetailDocument().getLength(); + } + if (length > 0) { + insert = "\n" + result; //$NON-NLS-1$ + } + try { + int max = DsfUIPlugin.getDefault().getPreferenceStore().getInt(IDebugUIConstants.PREF_MAX_DETAIL_LENGTH); + if (max > 0 && insert.length() > max) { + insert = insert.substring(0, max) + "..."; //$NON-NLS-1$ + } + if (fFirst) { + getDetailDocument().set(insert); + fFirst = false; + } else { + getDetailDocument().replace(length, 0,insert); + } + } catch (BadLocationException e) { + DsfUIPlugin.log(e); + } + } + return Status.OK_STATUS; + } + }; + append.setSystem(true); + append.schedule(); + } + synchronized (this) { + notifyAll(); + } + } + } + + /** + * The model presentation used to produce the string details for a + * selected variable. + */ + private String fDebugModelIdentifier; + + /** + * Controls the status line while the details area has focus. + * Displays the current cursor position in the text (line:character). + */ + private StatusLineContributionItem fStatusLineItem; + + /** + * The source viewer in which the computed string detail + * of selected variables will be displayed. + */ + private SourceViewer fSourceViewer; + + /** + * The last selection displayed in the source viewer. + */ + private IStructuredSelection fLastDisplayed = null; + + /** + * Variables used to create the detailed information for a selection + */ + private IDocument fDetailDocument; + private DetailJob fDetailJob = null; + private final String fPositionLabelPattern = MessagesForDetailPane.DetailPane_LabelPattern; + private final PositionLabelValue fLineLabel = new PositionLabelValue(); + private final PositionLabelValue fColumnLabel = new PositionLabelValue(); + private final Object[] fPositionLabelPatternArguments = new Object[] {fLineLabel, fColumnLabel }; + private ICursorListener fCursorListener; + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.IDetailPane#createControl(org.eclipse.swt.widgets.Composite) + */ + public Control createControl(Composite parent) { + + createSourceViewer(parent); + + if (isInView()){ + createViewSpecificComponents(); + createActions(); + DsfUIPlugin.getDefault().getPreferenceStore().addPropertyChangeListener(this); + JFaceResources.getFontRegistry().addListener(this); + } + + return fSourceViewer.getControl(); + } + + /** + * 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); + } + + /** + * 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(); + } + }); + + // Create a status line item displaying the current cursor location + fStatusLineItem = new StatusLineContributionItem("ModeContributionItem"); //$NON-NLS-1$ + IStatusLineManager manager= getViewSite().getActionBars().getStatusLineManager(); + manager.add(fStatusLineItem); + fSourceViewer.getTextWidget().addMouseListener(getCursorListener()); + fSourceViewer.getTextWidget().addKeyListener(getCursorListener()); + + // Add a context menu to the detail area + createDetailContextMenu(fSourceViewer.getTextWidget()); + } + + /** + * 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(); + + IAction action = new DetailPaneWordWrapAction(fSourceViewer); + setAction(DETAIL_WORD_WRAP_ACTION, action); + + action = new DetailPaneMaxLengthAction(fSourceViewer.getControl().getShell()); + setAction(DETAIL_MAX_LENGTH_ACTION,action); + } + + /** + * Create the context menu particular to the detail pane. Note that anyone + * wishing to contribute an action to this menu must use + * <code>IDebugUIConstants.VARIABLE_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(IDebugUIConstants.VARIABLE_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(IDebugUIConstants.VARIABLE_GROUP)); + menu.add(new Separator()); + menu.add(getAction(DETAIL_COPY_ACTION)); + menu.add(getAction(DETAIL_SELECT_ALL_ACTION)); + menu.add(new Separator()); + menu.add(getAction(DETAIL_WORD_WRAP_ACTION)); + menu.add(getAction(DETAIL_MAX_LENGTH_ACTION)); + menu.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.IDetailPane#display(org.eclipse.jface.viewers.IStructuredSelection) + */ + public void display(IStructuredSelection selection) { + + if (selection == null){ + clearSourceViewer(); + return; + } + + fLastDisplayed = selection; + + if ( selection.isEmpty() || !(selection instanceof ITreeSelection) ) { + clearSourceViewer(); + return; + } + + Object firstElement = selection.getFirstElement(); + if (firstElement instanceof IAdaptable) { + IDebugModelProvider debugModelProvider = + (IDebugModelProvider)((IAdaptable)firstElement).getAdapter(IDebugModelProvider.class); + if (debugModelProvider != null) { + String[] ids = debugModelProvider.getModelIdentifiers(); + if (ids != null && ids.length > 0) { + setDebugModel(ids[0]); + } + } + } + + synchronized (this) { + if (fDetailJob != null) { + fDetailJob.cancel(); + } + IWorkbenchPart part = fWorkbenchPartSite.getPart(); + if (part instanceof IDebugView) { + Viewer viewer = ((IDebugView)part).getViewer(); + Object input = viewer.getInput(); + if (input != null && viewer instanceof TreeModelViewer) { + TreeModelViewer treeModelViewer = (TreeModelViewer)viewer; + fDetailJob = new DetailJob(treeModelViewer.getPresentationContext(), input, + (ITreeSelection)selection, null); + fDetailJob.schedule(); + } + } + } + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.IDetailPane#setFocus() + */ + public boolean setFocus(){ + if (fSourceViewer != null){ + fSourceViewer.getTextWidget().setFocus(); + return true; + } + return false; + } + + /* + * + */ + public void dispose(){ + fActionMap.clear(); + fSelectionActions.clear(); + + if (fDetailJob != null) fDetailJob.cancel(); + fDebugModelIdentifier = null; // Setting this to null makes sure the source viewer is reconfigured with the model presentation after disposal + if (fSourceViewer != null && fSourceViewer.getControl() != null) fSourceViewer.getControl().dispose(); + + if (isInView()){ + disposeUndoRedoAction(ITextEditorActionConstants.UNDO); + disposeUndoRedoAction(ITextEditorActionConstants.REDO); + + getViewSite().getActionBars().getStatusLineManager().remove(fStatusLineItem); + + DsfUIPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(this); + JFaceResources.getFontRegistry().removeListener(this); + } + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.IDetailPane#getDescription() + */ + public String getDescription() { + return MessagesForDetailPane.NumberFormatDetailPane_Description; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.IDetailPane#getID() + */ + public String getID() { + return ID; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.IDetailPane#getName() + */ + public String getName() { + return MessagesForDetailPane.NumberFormatDetailPane_Name; + } + + /* (non-Javadoc) + * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class) + */ + @SuppressWarnings("unchecked") + public Object getAdapter(Class required) { + if (IFindReplaceTarget.class.equals(required)) { + return fSourceViewer.getFindReplaceTarget(); + } + if (ITextViewer.class.equals(required)) { + return fSourceViewer; + } + return null; + } + + /** + * Lazily instantiate and return a Document for the detail pane text viewer. + */ + protected IDocument getDetailDocument() { + if (fDetailDocument == null) { + fDetailDocument = new Document(); + } + return fDetailDocument; + } + + /** + * Clears the source viewer, removes all text. + */ + protected void clearSourceViewer(){ + if (fDetailJob != null) { + fDetailJob.cancel(); + } + fLastDisplayed = null; + fDetailDocument.set(""); //$NON-NLS-1$ + fSourceViewer.setEditable(false); + } + + /** + * Configures the details viewer for the debug model + * currently being displayed + */ + protected void configureDetailsViewer() { + SourceViewerConfiguration svc = new SourceViewerConfiguration(); + + fSourceViewer.setEditable(false); + fSourceViewer.unconfigure(); + fSourceViewer.configure(svc); + + if (isInView()){ + createUndoRedoActions(); + } + } + + /** + * @return The formatted string describing cursor position + */ + protected String getCursorPosition() { + + if (fSourceViewer == null) { + return ""; //$NON-NLS-1$ + } + + StyledText styledText= fSourceViewer.getTextWidget(); + int caret= styledText.getCaretOffset(); + IDocument document= fSourceViewer.getDocument(); + + if (document == null) { + return ""; //$NON-NLS-1$ + } + + try { + + int line= document.getLineOfOffset(caret); + + int lineOffset= document.getLineOffset(line); + int tabWidth= styledText.getTabs(); + int column= 0; + for (int i= lineOffset; i < caret; i++) + if ('\t' == document.getChar(i)) { + column += tabWidth - (tabWidth == 0 ? 0 : column % tabWidth); + } else { + column++; + } + + fLineLabel.fValue= line + 1; + fColumnLabel.fValue= column + 1; + return MessageFormat.format(fPositionLabelPattern, fPositionLabelPatternArguments); + + } catch (BadLocationException x) { + return ""; //$NON-NLS-1$ + } + } + + /** + * Returns this view's "cursor" listener to be installed on the view's + * associated details viewer. This listener is listening to key and mouse button events. + * It triggers the updating of the status line. + * + * @return the listener + */ + private ICursorListener getCursorListener() { + if (fCursorListener == null) { + fCursorListener= new ICursorListener() { + + public void keyPressed(KeyEvent e) { + fStatusLineItem.setText(getCursorPosition()); + } + + public void keyReleased(KeyEvent e) { + } + + public void mouseDoubleClick(MouseEvent e) { + } + + public void mouseDown(MouseEvent e) { + } + + public void mouseUp(MouseEvent e) { + fStatusLineItem.setText(getCursorPosition()); + } + }; + } + return fCursorListener; + } + + /** + * Returns the identifier of the debug model being displayed + * in this view, or <code>null</code> if none. + * + * @return debug model identifier + */ + protected String getDebugModel() { + return fDebugModelIdentifier; + } + + /** + * Sets the identifier of the debug model being displayed + * in this view, or <code>null</code> if none. + * + * @param id debug model identifier of the type of debug + * elements being displayed in this view + */ + protected void setDebugModel(String id) { + if (id != fDebugModelIdentifier) { + fDebugModelIdentifier = id; + configureDetailsViewer(); + } + } + + /** + * Creates this editor's undo/re-do actions. + * <p> + * Subclasses may override or extend.</p> + * + * @since 3.2 + */ + protected void createUndoRedoActions() { + disposeUndoRedoAction(ITextEditorActionConstants.UNDO); + disposeUndoRedoAction(ITextEditorActionConstants.REDO); + IUndoContext undoContext= getUndoContext(); + if (undoContext != null) { + // Use actions provided by global undo/re-do + + // Create the undo action + OperationHistoryActionHandler undoAction= new UndoActionHandler(getViewSite(), undoContext); + PlatformUI.getWorkbench().getHelpSystem().setHelp(undoAction, IAbstractTextEditorHelpContextIds.UNDO_ACTION); + undoAction.setActionDefinitionId(IWorkbenchActionDefinitionIds.UNDO); + setAction(ITextEditorActionConstants.UNDO, undoAction); + setGlobalAction(ITextEditorActionConstants.UNDO, undoAction); + + // Create the re-do action. + OperationHistoryActionHandler redoAction= new RedoActionHandler(getViewSite(), undoContext); + PlatformUI.getWorkbench().getHelpSystem().setHelp(redoAction, IAbstractTextEditorHelpContextIds.REDO_ACTION); + redoAction.setActionDefinitionId(IWorkbenchActionDefinitionIds.REDO); + setAction(ITextEditorActionConstants.REDO, redoAction); + setGlobalAction(ITextEditorActionConstants.REDO, redoAction); + + getViewSite().getActionBars().updateActionBars(); + } + } + + /** + * Disposes of the action with the specified ID + * + * @param actionId the ID of the action to disposed + */ + protected void disposeUndoRedoAction(String actionId) { + OperationHistoryActionHandler action = (OperationHistoryActionHandler) getAction(actionId); + if (action != null) { + action.dispose(); + setAction(actionId, null); + } + } + + /** + * Returns this editor's viewer's undo manager undo context. + * + * @return the undo context or <code>null</code> if not available + * @since 3.2 + */ + private IUndoContext getUndoContext() { + IUndoManager undoManager= fSourceViewer.getUndoManager(); + if (undoManager instanceof IUndoManagerExtension) + return ((IUndoManagerExtension)undoManager).getUndoContext(); + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent) + */ + 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)); + } else if (propertyName.equals(IDsfDebugUIConstants.PREF_MAX_DETAIL_LENGTH)) { + display(fLastDisplayed); + } else if (propertyName.equals(IDsfDebugUIConstants.PREF_DETAIL_PANE_WORD_WRAP)) { + fSourceViewer.getTextWidget().setWordWrap(DsfUIPlugin.getDefault().getPreferenceStore().getBoolean(IDsfDebugUIConstants.PREF_DETAIL_PANE_WORD_WRAP)); + getAction(DETAIL_WORD_WRAP_ACTION).setChecked(DsfUIPlugin.getDefault().getPreferenceStore().getBoolean(IDsfDebugUIConstants.PREF_DETAIL_PANE_WORD_WRAP)); + } + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/numberformat/detail/NumberFormatDetailPaneFactory.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/numberformat/detail/NumberFormatDetailPaneFactory.java new file mode 100644 index 00000000000..fc62cdc6ebe --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/numberformat/detail/NumberFormatDetailPaneFactory.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * 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, Inc. - initial implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.numberformat.detail; + +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.detailsupport.MessagesForDetailPane; +import org.eclipse.debug.ui.IDetailPane; +import org.eclipse.debug.ui.IDetailPaneFactory; +import org.eclipse.jface.viewers.IStructuredSelection; + +/** + * This provides a simple Detail Pane Factory for the core debug views for DSF. + */ + +public class NumberFormatDetailPaneFactory implements IDetailPaneFactory { + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.views.variables.IDetailsFactory#createDetailsArea(java.lang.String) + */ + public IDetailPane createDetailPane(String id) { + return new NumberFormatDetailPane(); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.views.variables.IDetailsFactory#getDetailsTypes(org.eclipse.jface.viewers.IStructuredSelection) + */ + @SuppressWarnings("unchecked") + public Set getDetailPaneTypes(IStructuredSelection selection) { + Set<String> possibleIDs = new HashSet<String>(1); + possibleIDs.add(NumberFormatDetailPane.ID); + return possibleIDs; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.IDetailPaneFactory#getDefaultDetailPane(java.util.Set, org.eclipse.jface.viewers.IStructuredSelection) + */ + public String getDefaultDetailPane(IStructuredSelection selection) { + return NumberFormatDetailPane.ID; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.views.variables.IDetailsFactory#getName(java.lang.String) + */ + public String getDetailPaneName(String id) { + if (id.equals(NumberFormatDetailPane.ID)){ + return MessagesForDetailPane.NumberFormatDetailPane_Name; + } + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.views.variables.IDetailsFactory#getDescription(java.lang.String) + */ + public String getDetailPaneDescription(String id) { + if (id.equals(NumberFormatDetailPane.ID)){ + return MessagesForDetailPane.NumberFormatDetailPane_Description; + } + return null; + } + +} 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 diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/internal/ui/DsfUIPlugin.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/internal/ui/DsfUIPlugin.java new file mode 100644 index 00000000000..5a2b1e24de3 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/internal/ui/DsfUIPlugin.java @@ -0,0 +1,151 @@ +/******************************************************************************* + * 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.internal.ui; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.SourceDocumentProvider; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +/** + * The activator class controls the plug-in life cycle + */ +public class DsfUIPlugin extends AbstractUIPlugin { + + // The plug-in ID + public static final String PLUGIN_ID = "org.eclipse.cdt.dsf.ui"; //$NON-NLS-1$ + + // The shared instance + private static DsfUIPlugin fgPlugin; + + private static BundleContext fgBundleContext; + + // The document provider for source documents in the disassembly. + private SourceDocumentProvider fSourceDocumentProvider; + + public static boolean DEBUG = false; + + /** + * The constructor + */ + public DsfUIPlugin() { + fgPlugin = this; + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) + */ + @Override + public void start(BundleContext context) throws Exception { + fgBundleContext = context; + super.start(context); + DEBUG = "true".equals(Platform.getDebugOption("org.eclipse.cdt.dsf.ui/debug")); //$NON-NLS-1$//$NON-NLS-2$ + + fSourceDocumentProvider = new SourceDocumentProvider(); + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) + */ + @Override + public void stop(BundleContext context) throws Exception { + fSourceDocumentProvider.dispose(); + fSourceDocumentProvider = null; + fgPlugin = null; + fgBundleContext = null; + super.stop(context); + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static DsfUIPlugin getDefault() { + return fgPlugin; + } + + public static BundleContext getBundleContext() { + return fgBundleContext; + } + + /** + * Returns an image descriptor for the image file at the given + * plug-in relative path + * + * @param path the path + * @return the image descriptor + */ + public static ImageDescriptor getImageDescriptor(String path) { + return imageDescriptorFromPlugin(PLUGIN_ID, path); + } + + public static SourceDocumentProvider getSourceDocumentProvider() { + return getDefault().fSourceDocumentProvider; + } + + + /** + * If the debug flag is set the specified message is printed to the console + * @param message + */ + public static void debug(String message) { + if (DEBUG) { + System.out.println(message); + } + } + + /** + * Logs the specified status with this plug-in's log. + * + * @param status status to log + */ + public static void log(IStatus status) { + getDefault().getLog().log(status); + } + + /** + * Logs the specified throwable with this plug-in's log. + * + * @param t throwable to log + */ + public static void log(Throwable t) { + log(newErrorStatus("Error logged from Debug UI: ", t)); //$NON-NLS-1$ + } + + /** + * Logs an internal error with the specified message. + * + * @param message the error message to log + */ + public static void logErrorMessage(String message) { + // this message is intentionally not internationalized, as an exception may + // be due to the resource bundle itself + log(newErrorStatus("Internal message logged from Debug UI: " + message, null)); //$NON-NLS-1$ + } + + /** + * Returns a new error status for this plug-in with the given message + * @param message the message to be included in the status + * @param exception the exception to be included in the status or <code>null</code> if none + * @return a new error status + */ + public static IStatus newErrorStatus(String message, Throwable exception) { + return new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDebugUIConstants.INTERNAL_ERROR, message, exception); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/concurrent/DisplayDsfExecutor.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/concurrent/DisplayDsfExecutor.java new file mode 100644 index 00000000000..46fbbed0c50 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/concurrent/DisplayDsfExecutor.java @@ -0,0 +1,258 @@ +/******************************************************************************* + * 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.ui.concurrent; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.Future; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.cdt.dsf.concurrent.DefaultDsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutable; +import org.eclipse.swt.SWT; +import org.eclipse.swt.SWTException; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; + +/** + * DSF executor which uses the display thread to run the submitted runnables + * and callables. The implementation is based on the default DSF executor + * which still creates its own thread. However this thread blocks when running + * each executable in the display thread. + */ +public class DisplayDsfExecutor extends DefaultDsfExecutor +{ + /** + * Internal mapping of display objects to executors. + */ + private static Map<Display, DisplayDsfExecutor> fExecutors = Collections.synchronizedMap( new HashMap<Display, DisplayDsfExecutor>() ); + + /** + * Factory method for display executors. + * @param display Display to create an executor for. + * @return The new (or re-used) executor. + */ + public static DisplayDsfExecutor getDisplayDsfExecutor(Display display) { + synchronized (fExecutors) { + DisplayDsfExecutor executor = fExecutors.get(display); + if (executor == null) { + executor = new DisplayDsfExecutor(display); + fExecutors.put(display, executor); + } + return executor; + } + } + + /** + * The display class used by this executor to execute the submitted runnables. + */ + private final Display fDisplay; + + private DisplayDsfExecutor(Display display) { + super("Display DSF Executor"); //$NON-NLS-1$ + fDisplay = display; + fDisplay.addListener(SWT.Dispose, new Listener() { + public void handleEvent(Event event) { + if (event.type == SWT.Dispose) { + DisplayDsfExecutor.super.shutdownNow(); + } + } + }); + } + + /** + * Override to check if we're in the display thread rather than the helper + * thread of the super-class. + */ + @Override + public boolean isInExecutorThread() { + return Thread.currentThread().equals(fDisplay.getThread()); + } + + /** + * Creates a callable wrapper, which delegates to the display to perform the + * operation. The callable blocks the executor thread while each call + * is executed in the display thred. + * @param <V> Type used in the callable. + * @param callable Callable to wrap. + * @return Wrapper callable. + */ + private <V> Callable<V> createSWTDispatchCallable(final Callable<V> callable) { + // Check if executable wasn't executed already. + if (DEBUG_EXECUTOR && callable instanceof DsfExecutable) { + assert !((DsfExecutable)callable).getSubmitted() : "Executable was previously executed."; //$NON-NLS-1$ + ((DsfExecutable)callable).setSubmitted(); + } + + return new Callable<V>() { + @SuppressWarnings("unchecked") + public V call() throws Exception { + final Object[] v = new Object[1]; + final Throwable[] e = new Throwable[1]; + + try { + fDisplay.syncExec(new Runnable() { + public void run() { + try { + v[0] = callable.call(); + } catch(Throwable exception) { + e[0] = exception; + } + } + }); + } catch (SWTException swtException) { + if (swtException.code == SWT.ERROR_DEVICE_DISPOSED) { + DisplayDsfExecutor.super.shutdown(); + } + } + + if(e[0] instanceof RuntimeException) { + throw (RuntimeException) e[0]; + } else if (e[0] instanceof Error) { + throw (Error) e[0]; + } else if(e[0] instanceof Exception) { + throw (Exception) e[0]; + } + + return (V) v[0]; + } + }; + } + + /** + * Creates a runnable wrapper, which delegates to the display to perform the + * operation. The runnable blocks the executor thread while each call + * is executed in the display thred. + * @param runnable Runnable to wrap. + * @return Wrapper runnable. + */ + private Runnable createSWTDispatchRunnable(final Runnable runnable) { + + // Check if executable wasn't executed already. + if (DEBUG_EXECUTOR && runnable instanceof DsfExecutable) { + assert !((DsfExecutable)runnable).getSubmitted() : "Executable was previously executed."; //$NON-NLS-1$ + ((DsfExecutable)runnable).setSubmitted(); + } + + return new Runnable() { + public void run() { + try { + fDisplay.syncExec(new Runnable() { + public void run() { + runnable.run(); + } + }); + } catch (SWTException swtException) { + if (swtException.code == SWT.ERROR_DEVICE_DISPOSED) { + DisplayDsfExecutor.super.shutdownNow(); + } + } + } + }; + } + + @Override + public <V> ScheduledFuture<V> schedule(final Callable<V> callable, long delay, TimeUnit unit) { + if (fDisplay.isDisposed()) { + if (!super.isShutdown()) super.shutdown(); + throw new RejectedExecutionException("Display " + fDisplay + " is disposed."); //$NON-NLS-1$ //$NON-NLS-2$ + } + return super.schedule(createSWTDispatchCallable(callable), delay, unit); + } + + @Override + public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) { + if (fDisplay.isDisposed()) { + if (!super.isShutdown()) super.shutdown(); + throw new RejectedExecutionException("Display " + fDisplay + " is disposed."); //$NON-NLS-1$ //$NON-NLS-2$ + } + return super.schedule(createSWTDispatchRunnable(command), delay, unit); + } + + @Override + public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { + if (fDisplay.isDisposed()) { + if (!super.isShutdown()) super.shutdown(); + throw new RejectedExecutionException("Display " + fDisplay + " is disposed."); //$NON-NLS-1$ //$NON-NLS-2$ + } + return super.scheduleAtFixedRate(createSWTDispatchRunnable(command), initialDelay, period, unit); + } + + @Override + public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { + if (fDisplay.isDisposed()) { + if (!super.isShutdown()) super.shutdown(); + throw new RejectedExecutionException("Display " + fDisplay + " is disposed."); //$NON-NLS-1$ //$NON-NLS-2$ + } + return super.scheduleWithFixedDelay(createSWTDispatchRunnable(command), initialDelay, delay, unit); + } + + @Override + public void execute(Runnable command) { + if (fDisplay.isDisposed()) { + if (!super.isShutdown()) super.shutdown(); + throw new RejectedExecutionException("Display " + fDisplay + " is disposed."); //$NON-NLS-1$ //$NON-NLS-2$ + } + super.execute(createSWTDispatchRunnable(command)); + } + + @Override + public <T> Future<T> submit(Callable<T> callable) { + if (fDisplay.isDisposed()) { + if (!super.isShutdown()) super.shutdown(); + throw new RejectedExecutionException("Display " + fDisplay + " is disposed."); //$NON-NLS-1$ //$NON-NLS-2$ + } + return super.submit(createSWTDispatchCallable(callable)); + } + + @Override + public <T> Future<T> submit(Runnable command, T result) { + if (fDisplay.isDisposed()) { + if (!super.isShutdown()) super.shutdown(); + throw new RejectedExecutionException("Display " + fDisplay + " is disposed."); //$NON-NLS-1$ //$NON-NLS-2$ + } + return super.submit(createSWTDispatchRunnable(command), result); + } + + @Override + public Future<?> submit(Runnable command) { + if (fDisplay.isDisposed()) { + if (!super.isShutdown()) super.shutdown(); + throw new RejectedExecutionException("Display " + fDisplay + " is disposed."); //$NON-NLS-1$ //$NON-NLS-2$ + } + return super.submit(createSWTDispatchRunnable(command)); + } + + /** + * Override to prevent clients from shutting down. The executor will be + * shut down when the underlying display is discovered to be shut down. + */ + @Override + public void shutdown() { + } + + /** + * Override to prevent clients from shutting down. The executor will be + * shut down when the underlying display is discovered to be shut down. + */ + @SuppressWarnings({ "cast", "unchecked" }) + @Override + public List<Runnable> shutdownNow() { + return (List<Runnable>)Collections.EMPTY_LIST; + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/concurrent/SimpleDisplayExecutor.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/concurrent/SimpleDisplayExecutor.java new file mode 100644 index 00000000000..dae9a513272 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/concurrent/SimpleDisplayExecutor.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * 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.ui.concurrent; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Executor; +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.swt.SWT; +import org.eclipse.swt.SWTException; +import org.eclipse.swt.widgets.Display; + +/** + * A simple executor which uses the display thread to run the submitted + * runnables. It only implements the {@link Executor}, and NOT the more + * sophisticated {@link DsfExecutor} (which extends + * {@link java.util.concurrent.ScheduledExecutorService}). However, this + * implementation is much more efficient than DisplayDsfExecutor as it does + * not use a separate thread or maintain its own queue. + */ +public class SimpleDisplayExecutor implements Executor{ + /** + * Internal mapping of display objects to executors. + */ + private static Map<Display, SimpleDisplayExecutor> fExecutors = Collections.synchronizedMap( new HashMap<Display, SimpleDisplayExecutor>() ); + + /** + * Factory method for display executors. + * @param display Display to create an executor for. + * @return The new (or re-used) executor. + */ + public static SimpleDisplayExecutor getSimpleDisplayExecutor(Display display) { + synchronized (fExecutors) { + SimpleDisplayExecutor executor = fExecutors.get(display); + if (executor == null) { + executor = new SimpleDisplayExecutor(display); + fExecutors.put(display, executor); + } + return executor; + } + } + + /** + * The display class used by this executor to execute the submitted runnables. + */ + private final Display fDisplay; + + private SimpleDisplayExecutor(Display display) { + fDisplay = display; + } + + public void execute(Runnable command) { + try { + fDisplay.asyncExec(command); + } catch (SWTException e) { + if (e.code == SWT.ERROR_DEVICE_DISPOSED) { + throw new RejectedExecutionException("Display " + fDisplay + " is disposed", e); //$NON-NLS-1$ //$NON-NLS-2$ + } else { + throw e; + } + } + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/concurrent/ViewerCountingRequestMonitor.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/concurrent/ViewerCountingRequestMonitor.java new file mode 100644 index 00000000000..90634ad4dfb --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/concurrent/ViewerCountingRequestMonitor.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * 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.ui.concurrent; + +import java.util.concurrent.Executor; + +import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; + +/** + * Counting multi data request monitor that takes a <code>IViewerUpdate</code> + * as a parent. If the IViewerUpdate is canceled, this request monitor becomes + * canceled as well. + * + * @see IViewerUpdate. + */ +@SuppressWarnings("restriction") +public class ViewerCountingRequestMonitor extends CountingRequestMonitor { + + private final IViewerUpdate fUpdate; + public ViewerCountingRequestMonitor(Executor executor, IViewerUpdate update) { + super(executor, null); + fUpdate = update; + } + + @Override + public synchronized boolean isCanceled() { + // isCanceled() is called implicitly by the super-constructor before fUpdate + // is initialized. The fUpdate != null is here to protect against an NPE + // from that. + return (fUpdate != null && fUpdate.isCanceled()) || super.isCanceled(); + } + + @Override + protected void handleSuccess() { + fUpdate.done(); + } + + @Override + protected void handleErrorOrWarning() { + fUpdate.setStatus(getStatus()); + fUpdate.done(); + } + + @Override + protected void handleCancel() { + fUpdate.setStatus(getStatus()); + fUpdate.done(); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/concurrent/ViewerDataRequestMonitor.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/concurrent/ViewerDataRequestMonitor.java new file mode 100644 index 00000000000..6db69286cdf --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/concurrent/ViewerDataRequestMonitor.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * 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.ui.concurrent; + +import java.util.concurrent.Executor; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; + +/** + * Data Request monitor that takean <code>IViewerUpdate</code> as a parent. + * If the IViewerUpdate is canceled, this request monitor becomes canceled as well. + * @see IViewerUpdate + */ +@SuppressWarnings("restriction") +public class ViewerDataRequestMonitor<V> extends DataRequestMonitor<V> { + + private final IViewerUpdate fUpdate; + public ViewerDataRequestMonitor(Executor executor, IViewerUpdate update) { + super(executor, null); + fUpdate = update; + } + + @Override + public synchronized boolean isCanceled() { + return fUpdate.isCanceled() || super.isCanceled(); + } + + @Override + protected void handleSuccess() { + fUpdate.done(); + } + + @Override + protected void handleErrorOrWarning() { + fUpdate.setStatus(getStatus()); + fUpdate.done(); + } + + @Override + protected void handleCancel() { + fUpdate.setStatus(getStatus()); + fUpdate.done(); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/AbstractVMAdapter.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/AbstractVMAdapter.java new file mode 100644 index 00000000000..c98b3b4653f --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/AbstractVMAdapter.java @@ -0,0 +1,275 @@ +/******************************************************************************* + * 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.ui.viewmodel; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor; +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.concurrent.ThreadSafe; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +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.IColumnPresentation; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; + +/** + * Base implementation for View Model Adapters. The implementation uses + * its own single-thread executor for communicating with providers and + * layout nodes. + */ +@ThreadSafe +@SuppressWarnings("restriction") +abstract public class AbstractVMAdapter implements IVMAdapterExtension +{ + + private boolean fDisposed; + + private final Map<IPresentationContext, IVMProvider> fViewModelProviders = + Collections.synchronizedMap( new HashMap<IPresentationContext, IVMProvider>() ); + + /** + * Constructor for the View Model session. It is tempting to have the + * adapter register itself here with the session as the model adapter, but + * that would mean that the adapter might get accessed on another thread + * even before the deriving class is fully constructed. So it it better + * to have the owner of this object register it with the session. + * @param session + */ + public AbstractVMAdapter() { + } + + @ThreadSafe + public IVMProvider getVMProvider(IPresentationContext context) { + synchronized(fViewModelProviders) { + if (fDisposed) return null; + + IVMProvider provider = fViewModelProviders.get(context); + if (provider == null) { + provider = createViewModelProvider(context); + if (provider != null) { + fViewModelProviders.put(context, provider); + } + } + return provider; + } + } + + /** + * {@inheritDoc} + * + * @since 1.1 + */ + public IVMProvider[] getActiveProviders() { + synchronized(fViewModelProviders) { + return fViewModelProviders.values().toArray(new IVMProvider[fViewModelProviders.size()]); + } + } + + public void dispose() { + IVMProvider[] providers = new IVMProvider[0]; + synchronized(fViewModelProviders) { + providers = fViewModelProviders.values().toArray(new IVMProvider[fViewModelProviders.size()]); + fViewModelProviders.clear(); + fDisposed = true; + } + + for (final IVMProvider provider : providers) { + try { + provider.getExecutor().execute(new Runnable() { + public void run() { + provider.dispose(); + } + }); + } catch (RejectedExecutionException e) { + // Not much we can do at this point. + } + } + } + + /** + * @return whether this VM adapter is disposed. + * + * @since 1.1 + */ + public boolean isDisposed() { + return fDisposed; + } + + public void update(IHasChildrenUpdate[] updates) { + handleUpdate(updates); + } + + public void update(IChildrenCountUpdate[] updates) { + handleUpdate(updates); + } + + public void update(final IChildrenUpdate[] updates) { + handleUpdate(updates); + } + + private void handleUpdate(IViewerUpdate[] updates) { + IVMProvider provider = getVMProvider(updates[0].getPresentationContext()); + if (provider != null) { + updateProvider(provider, updates); + } else { + for (IViewerUpdate update : updates) { + update.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, + "No model provider for update " + update, null)); //$NON-NLS-1$ + update.done(); + } + } + } + + private void updateProvider(final IVMProvider provider, final IViewerUpdate[] updates) { + try { + provider.getExecutor().execute(new Runnable() { + public void run() { + if (updates instanceof IHasChildrenUpdate[]) { + provider.update((IHasChildrenUpdate[])updates); + } else if (updates instanceof IChildrenCountUpdate[]) { + provider.update((IChildrenCountUpdate[])updates); + } else if (updates instanceof IChildrenUpdate[]) { + provider.update((IChildrenUpdate[])updates); + } + } + }); + } catch (RejectedExecutionException e) { + for (IViewerUpdate update : updates) { + update.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, + "Display is disposed, cannot complete update " + update, null)); //$NON-NLS-1$ + update.done(); + } + } + } + + public IModelProxy createModelProxy(Object element, IPresentationContext context) { + IVMProvider provider = getVMProvider(context); + if (provider != null) { + return provider.createModelProxy(element, context); + } + return null; + } + + public IColumnPresentation createColumnPresentation(IPresentationContext context, Object element) { + final IVMProvider provider = getVMProvider(context); + if (provider != null) { + return provider.createColumnPresentation(context, element); + } + return null; + } + + public String getColumnPresentationId(IPresentationContext context, Object element) { + final IVMProvider provider = getVMProvider(context); + if (provider != null) { + return provider.getColumnPresentationId(context, element); + } + return null; + } + + + public void update(IViewerInputUpdate update) { + final IVMProvider provider = getVMProvider(update.getPresentationContext()); + if (provider != null) { + provider.update(update); + } + } + + /** + * Creates a new View Model Provider for given presentation context. Returns null + * if the presentation context is not supported. + */ + @ThreadSafe + abstract protected IVMProvider createViewModelProvider(IPresentationContext context); + + /** + * Dispatch given event to VM providers interested in events. + * + * @since 1.1 + */ + protected final void handleEvent(final Object event) { + final List<IVMEventListener> eventListeners = new ArrayList<IVMEventListener>(); + + aboutToHandleEvent(event); + + for (IVMProvider vmProvider : getActiveProviders()) { + if (vmProvider instanceof IVMEventListener) { + eventListeners.add((IVMEventListener)vmProvider); + } + } + + if (!eventListeners.isEmpty()) { + final CountingRequestMonitor crm = new CountingRequestMonitor(ImmediateExecutor.getInstance(), null) { + @Override + protected void handleCompleted() { + if (isDisposed()) { + return; + } + doneHandleEvent(event); + } + }; + + int count = 0; + for (final IVMEventListener vmEventListener : eventListeners) { + RequestMonitor listenerRm = null; + if (vmEventListener.shouldWaitHandleEventToComplete()) { + listenerRm = crm; + count++; + } else { + // Create a dummy executor for the handling of this event. + listenerRm = new RequestMonitor(ImmediateExecutor.getInstance(), null); + } + final RequestMonitor finalListenerRm = listenerRm; + vmEventListener.getExecutor().execute(new DsfRunnable() { + public void run() { + vmEventListener.handleEvent(event, finalListenerRm); + }}); + } + crm.setDoneCount(count); + } else { + doneHandleEvent(event); + } + } + + /** + * Given event is about to be handled. + * + * @param event + * + * @since 1.1 + */ + protected void aboutToHandleEvent(final Object event) { + } + + /** + * Given event has been processed by all VM event listeners. + * + * @param event + * + * @since 1.1 + */ + protected void doneHandleEvent(final Object event) { + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/AbstractVMContext.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/AbstractVMContext.java new file mode 100644 index 00000000000..aa538f61412 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/AbstractVMContext.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * 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.ui.viewmodel; + +import org.eclipse.core.runtime.Platform; + +/** + * Implementation of basic view model context interface. + * <p> The main purpose of the VMC wrapper is to re-direct adapter + * queries. The redirecting of adapter queries follows this order: + * <ol> + * <li>If context implements the adapter itself, it is returned.</li> + * <li>If the VM Adapter implements the adapter, the VM Adapter is returned.</li> + * <li>If the VM Provider implements the adapter, the VM Provider is returned.</li> + * <li>If the VM Node implements the adapter, the VM Node is returned.</li> + * </ol> + * </p> + * <p> + * Note: Deriving classes must override the Object.equals/hashCode methods. + * This is because the view model context objects are just wrappers that are + * created by the view model on demand, so the equals methods must use the + * object being wrapped to perform a meaningful comparison. + */ +abstract public class AbstractVMContext implements IVMContext { + protected final IVMNode fNode; + + public AbstractVMContext(IVMNode node) { + fNode = node; + } + + public IVMNode getVMNode() { return fNode; } + + @SuppressWarnings("unchecked") + public Object getAdapter(Class adapter) { + // If the context implements the given adapter directly, it always takes + // precedence. + if (adapter.isInstance(this)) { + return this; + } + + IVMProvider vmProvider = getVMNode().getVMProvider(); + IVMAdapter vmAdapter = vmProvider.getVMAdapter(); + if (adapter.isInstance(vmAdapter)) { + return vmAdapter; + } else if (adapter.isInstance(vmProvider)) { + return vmProvider; + } else if (adapter.isInstance(getVMNode())) { + return getVMNode(); + } + return Platform.getAdapterManager().getAdapter(this, adapter); + } + + /** Deriving classes must override. */ + @Override + abstract public boolean equals(Object obj); + + /** Deriving classes must override. */ + @Override + abstract public int hashCode(); +}
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/AbstractVMNode.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/AbstractVMNode.java new file mode 100644 index 00000000000..455383a6c38 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/AbstractVMNode.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * 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.ui.viewmodel; + +import java.util.concurrent.Executor; + +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.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; +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.IViewerUpdate; + +/** + * Base implementation of the view model node. + * The main functionality implemented here is for building the view model + * deltas (IModelDelta), based on the flags returned by child nodes. + */ +@SuppressWarnings("restriction") +abstract public class AbstractVMNode implements IVMNode { + + private final AbstractVMProvider fProvider; + private boolean fDisposed = false; + + public AbstractVMNode(AbstractVMProvider provider) { + fProvider = provider; + } + + /** + * Accessor method for sub-classes. + */ + protected Executor getExecutor() { + return fProvider.getExecutor(); + } + + public IVMProvider getVMProvider() { + return fProvider; + } + + public void dispose() { + fDisposed = true; + } + + public void getContextsForEvent(VMDelta parentDelta, Object event, DataRequestMonitor<IVMContext[]> rm) { + rm.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED, "", null)); //$NON-NLS-1$ + rm.done(); + } + + protected boolean isDisposed() { + return fDisposed; + } + + /** + * Convenience method that returns a token value in case when the services + * that the layout node depends on, are not available. + */ + protected boolean checkUpdate(IViewerUpdate update) { + if (update.isCanceled()) { + update.done(); + return false; + } + if (fDisposed) { + handleFailedUpdate(update); + return false; + } + return true; + } + + /** + * A convenience method that completes update object in case of an error. + * Different types of update need to have some data configured to exhibit + * desired behavior in the viewer. + * @param update Update to handle. + */ + protected void handleFailedUpdate(IViewerUpdate update) { + if (update instanceof IHasChildrenUpdate) { + ((IHasChildrenUpdate)update).setHasChilren(false); + } else if (update instanceof IChildrenCountUpdate) { + ((IChildrenCountUpdate)update).setChildCount(0); + } else if (update instanceof ILabelUpdate) { + ILabelUpdate labelUpdate = (ILabelUpdate)update; + String[] columns = labelUpdate.getColumnIds(); + for (int i = 0; i < (columns != null ? columns.length : 1); i++) { + labelUpdate.setLabel("...", i); //$NON-NLS-1$ + } + } + update.done(); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/AbstractVMProvider.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/AbstractVMProvider.java new file mode 100644 index 00000000000..de4388e247e --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/AbstractVMProvider.java @@ -0,0 +1,722 @@ +/******************************************************************************* + * 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.ui.viewmodel; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executor; +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.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.cdt.dsf.ui.concurrent.SimpleDisplayExecutor; +import org.eclipse.cdt.dsf.ui.concurrent.ViewerDataRequestMonitor; +import org.eclipse.cdt.dsf.ui.viewmodel.update.UserEditEvent; +import org.eclipse.core.runtime.Platform; +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.IColumnPresentation; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentationFactory; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider; +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.IModelProxy; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputUpdate; +import org.eclipse.debug.internal.ui.viewers.provisional.IAsynchronousContentAdapter; +import org.eclipse.debug.internal.ui.viewers.provisional.IAsynchronousLabelAdapter; +import org.eclipse.swt.widgets.Display; + +/** + * View model provider implements the asynchronous view model functionality for + * a single view. This provider is just a holder which further delegates the + * model provider functionality to the view model nodes that need + * to be configured with each provider. + * + * <p/> + * The view model provider, often does not provide the model for the entire + * view. Rather, it needs to be able to plug in at any level in the viewer's + * content model and provide data for a sub-tree. + * + * <p/> + * Clients are intended to extend this class. + * + * @see IAsynchronousContentAdapter + * @see IAsynchronousLabelAdapter + * @see IModelProxy + * @see IVMNode + */ +@SuppressWarnings("restriction") +abstract public class AbstractVMProvider implements IVMProvider, IVMEventListener +{ + // debug flags + /** @since 1.1 */ + public static String DEBUG_PRESENTATION_ID = null; + + /** @since 1.1 */ + public static boolean DEBUG_CONTENT_PROVIDER = false; + + /** @since 1.1 */ + public static boolean DEBUG_DELTA = false; + + static { + DEBUG_PRESENTATION_ID = Platform.getDebugOption("org.eclipse.cdt.dsf.ui/debug/vm/presentationId"); //$NON-NLS-1$ + if (!DsfUIPlugin.DEBUG || "".equals(DEBUG_PRESENTATION_ID)) { //$NON-NLS-1$ + DEBUG_PRESENTATION_ID = null; + } + DEBUG_CONTENT_PROVIDER = DsfUIPlugin.DEBUG && "true".equals( //$NON-NLS-1$ + Platform.getDebugOption("org.eclipse.cdt.dsf.ui/debug/vm/contentProvider")); //$NON-NLS-1$ + + DEBUG_DELTA = DsfUIPlugin.DEBUG && "true".equals( //$NON-NLS-1$ + Platform.getDebugOption("org.eclipse.cdt.dsf.ui/debug/vm/delta")); //$NON-NLS-1$ + } + + /** Reference to the VM adapter that owns this provider */ + private final AbstractVMAdapter fVMAdapter; + + /** The presentation context that this provider is associated with */ + private final IPresentationContext fPresentationContext; + + /** + * The executor that this VM provider operates in. This executor will be + * initialized properly when we can access the display from the + * IPresentationContext object (bug 213629). For now utilize the + * assumption that there is only one display. + */ + private final Executor fExecutor = SimpleDisplayExecutor.getSimpleDisplayExecutor(Display.getDefault()); + + /** + * The element content provider implementation that this provider delegates to. + * Sub-classes may override the content strategy used for custom functionality. + */ + private final IElementContentProvider fContentStrategy; + + /** + * The list of active model proxies in this provider. A new model + * proxy is created when a viewer has a new input element + * (see {@link #createModelProxy(Object, IPresentationContext)}). + * Typically there will be only one active model proxy in a given + * provider. However, if a view model provider fills only a sub-tree + * in a viewer, and there are several sub-trees active in the same viewer + * at the same time, each of these sub-trees will have it's own model + * proxy. + */ + private List<IVMModelProxy> fActiveModelProxies = new LinkedList<IVMModelProxy>(); + + /** + * Convencience constant. + */ + private static final IVMNode[] EMPTY_NODES_ARRAY = new IVMNode[0]; + + + /** + * The mapping of parent to child nodes. + */ + private Map<IVMNode,IVMNode[]> fChildNodesMap = + new HashMap<IVMNode,IVMNode[]>(); + + /** + * Cached array of all the configued view model nodes. It is generated + * based on the child nodes map. + */ + private IVMNode[] fNodesListCache = null; + + /** + * Flag indicating that the provider is disposed. + */ + private boolean fDisposed = false; + + /** + * The root node for this model provider. The root layout node could be + * null when first created, to allow sub-classes to prorperly configure the + * root node in the sub-class constructor. + */ + private IRootVMNode fRootNode; + + private class EventInfo { + EventInfo(Object event, RequestMonitor rm) { + fEvent = event; + fClientRm = rm; + } + Object fEvent; + RequestMonitor fClientRm; + } + + private class ModelProxyEventQueue { + EventInfo fCurrentEvent = null; + RequestMonitor fCurrentRm = null; + List<EventInfo> fEventQueue = new LinkedList<EventInfo>(); + } + + private Map<IVMModelProxy, ModelProxyEventQueue> fProxyEventQueues = new HashMap<IVMModelProxy, ModelProxyEventQueue>(); + + /** + * Constructs the view model provider for given DSF session. The + * constructor is thread-safe to allow VM provider to be constructed + * synchronously when a call to getAdapter() is made on an element + * in a view. + */ + public AbstractVMProvider(AbstractVMAdapter adapter, IPresentationContext presentationContext) { + fVMAdapter = adapter; + fPresentationContext = presentationContext; + fContentStrategy = createContentStrategy(); + } + + public IPresentationContext getPresentationContext() { + return fPresentationContext; + } + + public AbstractVMAdapter getVMAdapter() { + return fVMAdapter; + } + + /** + * Creates the strategy class that will be used to implement the content + * provider interface of this view model provider. This method can be + * overridden by sub-classes to provider custom content provider strategy. + * <p/> + * Note this method can be called by the base class constructor, therefore + * it should not reference any fields initialized in the sub-class. + * + * @return New content provider implementation. + */ + protected IElementContentProvider createContentStrategy() { + return new DefaultVMContentProviderStrategy(this); + } + + /** + * Access method for the content provider strategy. + * + * @return Content provider implementation currently being used by this + * class. + */ + protected IElementContentProvider getContentStrategy() { + return fContentStrategy; + } + + /** + * Creates the strategy class that will be used to implement the content + * model proxy of this view model provider. It is normally called by + * {@link #createModelProxy(Object, IPresentationContext)} every time the + * input in the viewer is updated. This method can be overridden by + * sub-classes to provider custom model proxy strategy. + * + * @return New model proxy implementation. + */ + protected IVMModelProxy createModelProxyStrategy(Object rootElement) { + return new DefaultVMModelProxyStrategy(this, rootElement); + } + + /** + * Returns the list of active proxies in this provider. The returned + * list is not a copy and if a sub-class modifies this list, it will + * modify the current list of active proxies. This allows the + * sub-classes to change how the active proxies are managed and + * retained. + */ + protected List<IVMModelProxy> getActiveModelProxies() { + return fActiveModelProxies; + } + + /** + * Processes the given event in the given provider, sending model + * deltas if necessary. + */ + public void handleEvent(final Object event) { + handleEvent(event, null); + } + + /** + * {@inheritDoc} + * @since 1.1 + */ + public void handleEvent(final Object event, RequestMonitor rm) { + CountingRequestMonitor crm = new CountingRequestMonitor(getExecutor(), rm); + final List<IVMModelProxy> activeModelProxies= new ArrayList<IVMModelProxy>(getActiveModelProxies()); + crm.setDoneCount(activeModelProxies.size()); + + for (final IVMModelProxy proxyStrategy : activeModelProxies) { + if (proxyStrategy.isDeltaEvent(event) || event instanceof UserEditEvent) { + if (DEBUG_DELTA && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) { + DsfUIPlugin.debug("eventReceived(proxyRoot = " + proxyStrategy .getRootElement() + ", event = " + event + ")" ); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ + } + if (!fProxyEventQueues.containsKey(proxyStrategy)) { + fProxyEventQueues.put(proxyStrategy, new ModelProxyEventQueue()); + if (DEBUG_DELTA && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) { + DsfUIPlugin.debug("eventQueued(proxyRoot = " + proxyStrategy.getRootElement() + ", event = " + event + ")" ); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ + } + } + final ModelProxyEventQueue queue = fProxyEventQueues.get(proxyStrategy); + if (queue.fCurrentEvent != null) { + assert queue.fCurrentRm != null; + // Iterate through the events in the queue and check if + // they can be skipped. If they can be skipped, then just + // mark their RM as done. Stop iterating through the queue + // if an event that cannot be skipped is encountered. + while (!queue.fEventQueue.isEmpty()) { + EventInfo eventToSkipInfo = queue.fEventQueue.get(queue.fEventQueue.size() - 1); + + if (canSkipHandlingEvent(event, eventToSkipInfo.fEvent)) { + if (DEBUG_DELTA && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) { + DsfUIPlugin.debug("eventSkipped(proxyRoot = " + proxyStrategy.getRootElement() + ", event = " + eventToSkipInfo + ")" ); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ + } + queue.fEventQueue.remove(queue.fEventQueue.size() - 1); + eventToSkipInfo.fClientRm.done(); + } else { + break; + } + } + // If the queue is empty check if the current event + // being processed can be skipped. If so, cancel its + // processing + if (queue.fEventQueue.isEmpty() && canSkipHandlingEvent(event, queue.fCurrentEvent.fEvent)) { + if (DEBUG_DELTA && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) { + DsfUIPlugin.debug("eventCancelled(proxyRoot = " + proxyStrategy.getRootElement() + ", event = " + queue.fCurrentEvent + ")" ); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ + } + queue.fCurrentRm.cancel(); + } + queue.fEventQueue.add(new EventInfo(event, crm)); + } else { + doHandleEvent(queue, proxyStrategy, new EventInfo(event, crm)); + } + } else { + crm.done(); + } + } + + // Clean up model proxies that were removed. + List<IVMModelProxy> activeProxies = getActiveModelProxies(); + for (Iterator<IVMModelProxy> itr = fProxyEventQueues.keySet().iterator(); itr.hasNext();) { + if (!activeProxies.contains(itr.next())) { + itr.remove(); + } + } + } + + private void doHandleEvent(final ModelProxyEventQueue queue, final IVMModelProxy proxyStrategy, final EventInfo eventInfo) { + assert queue.fCurrentEvent == null && queue.fCurrentRm == null; + + queue.fCurrentEvent = eventInfo; + queue.fCurrentRm = new RequestMonitor(getExecutor(), null) { + @Override + protected void handleCompleted() { + queue.fCurrentEvent = null; + queue.fCurrentRm = null; + if (!queue.fEventQueue.isEmpty()) { + EventInfo eventInfo = queue.fEventQueue.remove(0); + doHandleEvent(queue, proxyStrategy, eventInfo); + } + eventInfo.fClientRm.done(); + } + }; + handleEvent(proxyStrategy, eventInfo.fEvent, queue.fCurrentRm); + } + + /** + * Handles the given event for the given proxy strategy. + * <p> + * This method is called by the base {@link #handleEvent(Object)} + * implementation to handle the given event using the given model proxy. + * The default implementation of this method checks whether the given + * proxy is active and if the proxy is active, it is called to generate the + * delta which is then sent to the viewer. + * </p> + * @param proxyStrategy Model proxy strategy to use to process this event. + * @param event Event to process. + * @param rm Request monitor to call when processing the event is + * completed. + */ + protected void handleEvent(final IVMModelProxy proxyStrategy, final Object event, RequestMonitor rm) { + if (!proxyStrategy.isDisposed()) { + if (DEBUG_DELTA && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) { + DsfUIPlugin.debug("eventProcessing(proxyRoot = " + proxyStrategy.getRootElement() + ", event = " + event + ")" ); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ + } + proxyStrategy.createDelta( + event, + new DataRequestMonitor<IModelDelta>(getExecutor(), rm) { + @Override + public void handleCompleted() { + if (isSuccess()) { + proxyStrategy.fireModelChanged(getData()); + if (DEBUG_DELTA && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) { + DsfUIPlugin.debug("eventDeltaFired(proxyRoot = " + proxyStrategy.getRootElement() + ", event = " + event + ")" ); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ + } + } + super.handleCompleted(); + } + @Override public String toString() { + return "Result of a delta for event: '" + event.toString() + "' in VMP: '" + this + "'" + "\n" + getData().toString(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + } + }); + } else { + rm.done(); + } + } + + /** + * Determines whether processing of a given event can be skipped. This + * method is called when there are multiple events waiting to be processed + * by the provider. As new events are received from the model, they are + * compared with the events in the queue using this method, events at the + * end of the queue are tested for removal. If this method returns that a + * given event can be skipped in favor of the new event, the skipped event + * is removed from the queue. This process is repeated with the new event + * until an event which cannot be stopped is found or the queue goes empty. + * <p> + * This method may be overriden by specific view model provider + * implementations extending this abstract class. + * </p> + * @param newEvent New event that was received from the model. + * @param eventToSkip Event which is currently at the end of the queue. + * @return True if the event at the end of the queue can be skipped in + * favor of the new event. + */ + protected boolean canSkipHandlingEvent(Object newEvent, Object eventToSkip) { + return false; + } + + /** @since 1.1 */ + public boolean shouldWaitHandleEventToComplete() { + return false; + } + + public IRootVMNode getRootVMNode() { + return fRootNode; + } + + public IVMNode[] getAllVMNodes() { + if (fNodesListCache != null) { + return fNodesListCache; + } + List<IVMNode> list = new ArrayList<IVMNode>(); + for (IVMNode node : fChildNodesMap.keySet()) { + if (node != null) { + list.add(node); + } + } + fNodesListCache = list.toArray(new IVMNode[list.size()]);; + return fNodesListCache; + } + + public IVMNode[] getChildVMNodes(IVMNode node) { + IVMNode[] retVal = fChildNodesMap.get(node); + if (retVal != null) { + return retVal; + } + return EMPTY_NODES_ARRAY; + } + + /** + * Configures the given array of nodes as children of the given parent node. + * Sub-classes should call this method to define the hierarchy of nodes. + */ + protected void addChildNodes(IVMNode parentNode, IVMNode[] childNodes) { + // Add to the child nodes array. + IVMNode[] existingChildNodes = fChildNodesMap.get(parentNode); + if (existingChildNodes == null) { + fChildNodesMap.put(parentNode, childNodes); + } else { + IVMNode[] newNodes = new IVMNode[existingChildNodes.length + childNodes.length]; + System.arraycopy(existingChildNodes, 0, newNodes, 0, existingChildNodes.length); + System.arraycopy(childNodes, 0, newNodes, existingChildNodes.length, childNodes.length); + fChildNodesMap.put(parentNode, newNodes); + } + + // Make sure that each new expression node has an entry of its own. + for (IVMNode childNode : childNodes) { + addNode(childNode); + } + + fNodesListCache = null; + } + + /** + * Adds the given node to configured nodes, without creating any + * parent-child relationship for it. It is useful for providers which do have + * a strict tree hierarchy of ndoes. + */ + protected void addNode(IVMNode node) { + if (!fChildNodesMap.containsKey(node)) { + fChildNodesMap.put(node, EMPTY_NODES_ARRAY); + } + } + + /** + * Clears all configured nodes. This allows a subclass to reset and + * reconfigure its nodes. + */ + protected void clearNodes() { + for (IVMNode node : fChildNodesMap.keySet()) { + node.dispose(); + } + fChildNodesMap.clear(); + fRootNode = null; + } + + /** + * Sets the root node for this provider. + */ + protected void setRootNode(IRootVMNode rootNode) { + fRootNode = rootNode; + } + + /** Called to dispose the provider. */ + public void dispose() { + clearNodes(); + fRootNode = null; + fDisposed = true; + } + + public void update(final IHasChildrenUpdate[] updates) { + fContentStrategy.update(updates); + } + + public void update(final IChildrenCountUpdate[] updates) { + fContentStrategy.update(updates); + } + + public void update(final IChildrenUpdate[] updates) { + fContentStrategy.update(updates); + } + + /** + * Calls the given view model node to perform the given updates. This + * method is called by view model provider and it's helper classes instead + * of calling the IVMNode method directly, in order to allow additional + * processing of the udpate. For example the AbstractCachingVMProvider + * overrides this method to optionally return the results for an update from + * a cache. + */ + public void updateNode(final IVMNode node, IHasChildrenUpdate[] updates) { + IHasChildrenUpdate[] updateProxies = new IHasChildrenUpdate[updates.length]; + for (int i = 0; i < updates.length; i++) { + final IHasChildrenUpdate update = updates[i]; + if (DEBUG_CONTENT_PROVIDER && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) { + DsfUIPlugin.debug("updateNodeHasChildren(node = " + node + ", update = " + update + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + updateProxies[i] = new VMHasChildrenUpdate( + update, + new ViewerDataRequestMonitor<Boolean>(getExecutor(), updates[i]) { + @Override + protected void handleSuccess() { + update.setHasChilren(getData()); + update.done(); + } + + @Override + protected void handleErrorOrWarning() { + if (getStatus().getCode() == IDsfStatusConstants.NOT_SUPPORTED) { + if (DEBUG_CONTENT_PROVIDER && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) { + DsfUIPlugin.debug("not-supported:updateNodeHasChildren(node = " + node + ", update = " + update + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + updateNode( + node, + new VMChildrenUpdate( + update, -1, -1, + new ViewerDataRequestMonitor<List<Object>>(getExecutor(), update) { + @Override + protected void handleSuccess() { + update.setHasChilren( !getData().isEmpty() ); + update.done(); + } + }) + ); + + } else { + update.setStatus(getStatus()); + update.done(); + } + } + + }); + } + node.update(updateProxies); + } + + /** + * Calls the given view model node to perform the given updates. This + * method is called by view model provider and it's helper classes instead + * of calling the IVMNode method directly, in order to allow additional + * processing of the udpate. For example the AbstractCachingVMProvider + * overrides this method to optionally return the results for an update from + * a cache. + */ + public void updateNode(final IVMNode node, final IChildrenCountUpdate update) { + if (DEBUG_CONTENT_PROVIDER && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) { + DsfUIPlugin.debug("updateNodeChildCount(node = " + node + ", update = " + update + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + node.update(new IChildrenCountUpdate[] { + new VMChildrenCountUpdate( + update, + new ViewerDataRequestMonitor<Integer>(getExecutor(), update) { + @Override + protected void handleSuccess() { + update.setChildCount(getData()); + update.done(); + } + + @Override + protected void handleErrorOrWarning() { + if (getStatus().getCode() == IDsfStatusConstants.NOT_SUPPORTED) { + if (DEBUG_CONTENT_PROVIDER && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) { + DsfUIPlugin.debug("not-supported:updateNodeChildCount(node = " + node + ", update = " + update + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + updateNode( + node, + new VMChildrenUpdate( + update, -1, -1, + new ViewerDataRequestMonitor<List<Object>>(getExecutor(), update) { + @Override + protected void handleSuccess() { + update.setChildCount( getData().size() ); + update.done(); + } + }) + ); + } else { + super.handleErrorOrWarning(); + } + } + + }) + }); + } + + /** + * Calls the given view model node to perform the given updates. This + * method is called by view model provider and it's helper classes instead + * of calling the IVMNode method directly, in order to allow additional + * processing of the udpate. For example the AbstractCachingVMProvider + * overrides this method to optionally return the results for an update from + * a cache. + */ + public void updateNode(IVMNode node, IChildrenUpdate update) { + if (DEBUG_CONTENT_PROVIDER && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) { + DsfUIPlugin.debug("updateNodeChildren(node = " + node + ", update = " + update + ")"); //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$ + } + node.update(new IChildrenUpdate[] { update }); + } + + + /** + * Returns whether this provider has been disposed. + */ + protected boolean isDisposed() { + return fDisposed; + } + + /** + * The abstract provider uses a the display-thread executor so that the + * provider will operate on the same thread as the viewer. This way no + * synchronization is necessary when the provider is called by the viewer. + * Also, the display thread is likely to be shut down long after any of the + * view models are disposed, so the users of this abstract provider do not + * need to worry about the executor throwing the {@link RejectedExecutionException} + * exception. + */ + public Executor getExecutor() { + return fExecutor; + } + + public IModelProxy createModelProxy(Object element, IPresentationContext context) { + + // Iterate through the current active proxies to try to find a proxy with the same + // element and re-use it if found. At the same time purge proxies that are no longer + IVMModelProxy proxy = null; + for (Iterator<IVMModelProxy> itr = getActiveModelProxies().iterator(); itr.hasNext();) { + IVMModelProxy next = itr.next(); + if (next != null) { + if (next.getRootElement().equals(element)) { + proxy = next; + } else if (next.isDisposed()) { + itr.remove(); + } + } + } + + if (proxy == null) { + proxy = createModelProxyStrategy(element); + getActiveModelProxies().add(proxy); + } else if (proxy.isDisposed()) { + // DSF is capable of re-using old proxies which were previously + // disposed. However, the viewer which installs a proxy using + // a background job to install the proxy calls + // IModelProxy.isDisposed(), to check whether the proxy was disposed + // before it could be installed. We need to clear the disposed flag + // of the re-used proxy here, otherwise the proxy will never get used. + // Calling init here will cause the init() method to be called twice + // so the IVMModelProxy needs to be prepared for that. + // See bug 241024. + proxy.init(context); + } + return proxy; + } + + /** + * Creates the column presentation for the given object. This method is meant + * to be overriden by deriving class to provide view-specific functionality. + * The default is to return null, meaning no columns. + * <p> + * The viewer only reads the column presentation for the root/input element of + * the tree/table, so the VMProvider must be configured to own the root element + * in the view in order for this setting to be effective. + * <p> + * Note: since the IColumnEditorFactory interface is synchronous, and since + * column info is fairly static, this method is thread-safe, and it will + * not be called on the executor thread. + * + * @see IColumnPresentationFactory#createColumnPresentation(IPresentationContext, Object) + */ + public IColumnPresentation createColumnPresentation(IPresentationContext context, Object element) { + return null; + } + + /** + * Returns the ID of the column presentation for the given object. This method + * is meant to be overriden by deriving class to provide view-specific + * functionality. The default is to return null, meaning no columns. + * <p> + * The viewer only reads the column presentation for the root/input element of + * the tree/table, so the VMProvider must be configured to own the root element + * in the view in order for this setting to be effective. + * <p> + * Note: since the IColumnEditorFactory interface is synchronous, and since + * column info is fairly static, this method is thread-safe, and it will + * not be called on the executor thread. + * + * @see IColumnEditorFactory#getColumnEditorId(IPresentationContext, Object) + */ + public String getColumnPresentationId(IPresentationContext context, Object element) { + return null; + } + + /** + * Calculates the proxy input object to be used for the given input in the given + * viewer. By default no proxy object is used an the given element is used + * as the input into the view. + * <p> + * Sub classes can override this method for view-specific behavior. + * + * @see IViewerInputProvider + */ + public void update(IViewerInputUpdate update) { + update.setInputElement(update.getElement()); + update.done(); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/DefaultVMContentProviderStrategy.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/DefaultVMContentProviderStrategy.java new file mode 100644 index 00000000000..02bce411ff1 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/DefaultVMContentProviderStrategy.java @@ -0,0 +1,379 @@ +/******************************************************************************* + * 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.ui.viewmodel; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.MultiRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.ui.concurrent.ViewerCountingRequestMonitor; +import org.eclipse.cdt.dsf.ui.concurrent.ViewerDataRequestMonitor; +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.IElementContentProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; + +/** + * The default strategy for implementing the IElementContentProvider + * functionality for an IVMProvider. It implements an algorithm to populate + * contents of the view in accordance with the tree structure of the + * view model nodes configured in the view model provider. + * <p/> + * This class may be used by an <code>IVMProvider</code> directly, or it + * may be be extended to customize for the provider's needs. + * <p/> + * This class is closely linked with a view model provider which is required + * for the constructor. The view model provider is used to access the correct + * executor and the node hierarchy. + */ +@ConfinedToDsfExecutor("#getExecutor()") +@SuppressWarnings("restriction") +public class DefaultVMContentProviderStrategy implements IElementContentProvider { + + private final AbstractVMProvider fVMProvider; + + public DefaultVMContentProviderStrategy(AbstractVMProvider provider) { + fVMProvider = provider; + } + + /** + * Returns the view model provider that this strategy is configured for. + * @return + */ + protected AbstractVMProvider getVMProvider() { return fVMProvider; } + + public void update(final IHasChildrenUpdate[] updates) { + if (updates.length == 0) return; + + // Optimization: if all the updates belong to the same node, avoid creating any new lists/arrays. + boolean allNodesTheSame = true; + IVMNode firstNode = getNodeForElement(updates[0].getElement()); + for (int i = 1; i < updates.length; i++) { + if (firstNode != getNodeForElement(updates[i].getElement())) { + allNodesTheSame = false; + break; + } + } + + if (allNodesTheSame) { + updateNode(firstNode, updates); + } else { + // Sort the updates by the node. + Map<IVMNode,List<IHasChildrenUpdate>> nodeUpdatesMap = new HashMap<IVMNode,List<IHasChildrenUpdate>>(); + for (IHasChildrenUpdate update : updates) { + // Get the VM Context for last element in path. + IVMNode node = getNodeForElement(update.getElement()); + if (node == null) { + // Stale update, most likely as a result of the nodes being + // changed. Just ignore it. + update.done(); + continue; + } + if (!nodeUpdatesMap.containsKey(node)) { + nodeUpdatesMap.put(node, new ArrayList<IHasChildrenUpdate>()); + } + nodeUpdatesMap.get(node).add(update); + } + + // Iterate through the nodes in the sorted map. + for (IVMNode node : nodeUpdatesMap.keySet()) { + updateNode(node, nodeUpdatesMap.get(node).toArray(new IHasChildrenUpdate[nodeUpdatesMap.get(node).size()])); + } + } + } + + private void updateNode(IVMNode node, final IHasChildrenUpdate[] updates) { + // If parent element's node has no children, just set the + // result and continue to next element. + final IVMNode[] childNodes = getVMProvider().getChildVMNodes(node); + if (childNodes.length == 0) { + for (IHasChildrenUpdate update : updates) { + update.setHasChilren(false); + update.done(); + } + return; + } + + // Create a matrix of element updates: + // The first dimension "i" is the list of children updates that came from the viewer. + // For each of these updates, there are "j" number of elment updates corresponding + // to the number of child nodes in this node. + // Each children update from the viewer is complete when all the child nodes + // fill in their elements update. + // Once the matrix is constructed, the child nodes are given the list of updates + // equal to the updates requested by the viewer. + VMHasChildrenUpdate[][] elementsUpdates = + new VMHasChildrenUpdate[childNodes.length][updates.length]; + for (int i = 0; i < updates.length; i ++) + { + final IHasChildrenUpdate update = updates[i]; + + final MultiRequestMonitor<DataRequestMonitor<Boolean>> hasChildrenMultiRequestMon = + new MultiRequestMonitor<DataRequestMonitor<Boolean>>(getVMProvider().getExecutor(), null) { + @Override + protected void handleCompleted() { + // Status is OK, only if all request monitors are OK. + if (isSuccess()) { + boolean isContainer = false; + for (DataRequestMonitor<Boolean> hasElementsDone : getRequestMonitors()) { + isContainer |= hasElementsDone.isSuccess() && + hasElementsDone.getData().booleanValue(); + } + update.setHasChilren(isContainer); + } else { + update.setStatus(getStatus()); + } + update.done(); + } + }; + + for (int j = 0; j < childNodes.length; j++) + { + elementsUpdates[j][i] = new VMHasChildrenUpdate( + update, + hasChildrenMultiRequestMon.add( + new ViewerDataRequestMonitor<Boolean>(getVMProvider().getExecutor(), update) { + @Override + protected void handleCompleted() { + hasChildrenMultiRequestMon.requestMonitorDone(this); + } + })); + } + } + + for (int j = 0; j < childNodes.length; j++) { + getVMProvider().updateNode(childNodes[j], elementsUpdates[j]); + } + } + + + public void update(final IChildrenCountUpdate[] updates) { + for (final IChildrenCountUpdate update : updates) { + IVMNode node = getNodeForElement(update.getElement()); + + if (node != null && !update.isCanceled()) { + IVMNode[] childNodes = getVMProvider().getChildVMNodes(node); + + if (childNodes.length == 0) { + update.setChildCount(0); + update.done(); + } else if (childNodes.length == 1) { + getVMProvider().updateNode(childNodes[0], update); + } else { + getChildrenCountsForNode( + update, + node, + new ViewerDataRequestMonitor<Integer[]>(getVMProvider().getExecutor(), update) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + int numChildren = 0; + for (Integer count : getData()) { + numChildren += count.intValue(); + } + update.setChildCount(numChildren); + } else { + update.setChildCount(0); + } + update.done(); + } + }); + } + } else { + update.done(); + } + + } + } + + public void update(final IChildrenUpdate[] updates) { + for (final IChildrenUpdate update : updates) { + // Get the VM Context for last element in path. + final IVMNode node = getNodeForElement(update.getElement()); + if (node != null && !update.isCanceled()) { + IVMNode[] childNodes = getVMProvider().getChildVMNodes(node); + if (childNodes.length == 0) { + // Invalid update, just mark done. + update.done(); + } else if (childNodes.length == 1) { + getVMProvider().updateNode(childNodes[0], update); + } else { + getChildrenCountsForNode( + update, + node, + new ViewerDataRequestMonitor<Integer[]>(getVMProvider().getExecutor(), update) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + update.done(); + return; + } + + updateChildrenWithCounts(update, node, getData()); + } + }); + } + } else { + // Stale update. Just ignore. + update.done(); + } + + } + } + + + /** + * Calculates the number of elements in each child node for the element in + * update. These counts are then used to delegate the children update to + * the correct nodes. + */ + private void getChildrenCountsForNode(IViewerUpdate update, IVMNode updateNode, final DataRequestMonitor<Integer[]> rm) { + + IVMNode[] childNodes = getVMProvider().getChildVMNodes(updateNode); + + // Check for an invalid call + assert childNodes.length != 0; + + // Get the mapping of all the counts. + final Integer[] counts = new Integer[childNodes.length]; + final MultiRequestMonitor<RequestMonitor> childrenCountMultiReqMon = + new MultiRequestMonitor<RequestMonitor>(getVMProvider().getExecutor(), rm) { + @Override + protected void handleSuccess() { + rm.setData(counts); + rm.done(); + } + }; + + for (int i = 0; i < childNodes.length; i++) { + final int nodeIndex = i; + getVMProvider().updateNode( + childNodes[i], + new VMChildrenCountUpdate( + update, + childrenCountMultiReqMon.add( + new ViewerDataRequestMonitor<Integer>(getVMProvider().getExecutor(), update) { + @Override + protected void handleSuccess() { + counts[nodeIndex] = getData(); + } + + @Override + protected void handleCompleted() { + super.handleCompleted(); + childrenCountMultiReqMon.requestMonitorDone(this); + } + })) + ); + } + } + + /** + * Splits the given children update among the configured child nodes. Then calls + * each child node to complete the update. + */ + private void updateChildrenWithCounts(final IChildrenUpdate update, IVMNode node, Integer[] nodeElementCounts) { + // Create the multi request monitor to mark update when querying all + // children nodes is finished. + CountingRequestMonitor multiRm = new ViewerCountingRequestMonitor(getVMProvider().getExecutor(), update) { + @Override + protected void handleCompleted() { + update.done(); + } + }; + int multiRmCount = 0; + + // Iterate through all child nodes and if requested range matches, call them to + // get their elements. + int updateStartIdx = update.getOffset(); + int updateEndIdx = update.getOffset() + update.getLength(); + int idx = 0; + IVMNode[] nodes = getVMProvider().getChildVMNodes(node); + for (int i = 0; i < nodes.length; i++) { + final int nodeStartIdx = idx; + final int nodeEndIdx = idx + nodeElementCounts[i]; + idx = nodeEndIdx; + + // Check if update range overlaps the node's range. + if (updateStartIdx <= nodeEndIdx && updateEndIdx > nodeStartIdx) { + final int elementsStartIdx = Math.max(updateStartIdx - nodeStartIdx, 0); + final int elementsEndIdx = Math.min(updateEndIdx - nodeStartIdx, nodeElementCounts[i]); + final int elementsLength = elementsEndIdx - elementsStartIdx; + if (elementsLength > 0) { + getVMProvider().updateNode( + nodes[i], + new VMChildrenUpdate( + update, elementsStartIdx, elementsLength, + new DataRequestMonitor<List<Object>>(getVMProvider().getExecutor(), multiRm) { + @Override + protected void handleCompleted() { + // Workaround for a bug caused by an optimization in the viewer: + // The viewer may request more children then there are at a given level. + // This causes the update to return with an error. + // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=202109 + // Instead of checking isSuccess(), check getData() != null. + if (getData() != null) { + for (int i = 0; i < elementsLength && i < getData().size(); i++) { + Object child = getData().get(i); + if (child != null) { + update.setChild(getData().get(i), elementsStartIdx + nodeStartIdx + i); + } + } + } + super.handleCompleted(); + } + }) + ); + multiRmCount++; + } + } + } + + // Guard against invalid queries. + multiRm.setDoneCount(multiRmCount); + } + + /** + * Convenience method that finds the VMC corresponding to given parent + * argument given to isContainer() or retrieveChildren(). + * @param object Object to find the VMC for. + * @return parent VMC, if null it indicates that the object did not originate + * from this view or is stale. + */ + protected IVMNode getNodeForElement(Object element) { + if (element instanceof IVMContext) { + IVMNode node = ((IVMContext)element).getVMNode(); + if (isOurNode(((IVMContext)element).getVMNode())) { + return node; + } + } + return getVMProvider().getRootVMNode(); + } + + /** + * Convenience method which checks whether given layout node is a node + * that is configured in this ViewModelProvider. + */ + private boolean isOurNode(IVMNode node) { + for (IVMNode nodeToSearch : getVMProvider().getAllVMNodes()) { + if (nodeToSearch.equals(node)) return true; + } + return false; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/DefaultVMModelProxyStrategy.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/DefaultVMModelProxyStrategy.java new file mode 100644 index 00000000000..ffc53ee40f6 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/DefaultVMModelProxyStrategy.java @@ -0,0 +1,673 @@ +/******************************************************************************* + * Copyright (c) 2005, 2007 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Wind River Systems - adapted to use with DSF + *******************************************************************************/ +package org.eclipse.cdt.dsf.ui.viewmodel; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor; +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.MultiRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.core.runtime.ISafeRunnable; +import org.eclipse.core.runtime.ListenerList; +import org.eclipse.core.runtime.SafeRunner; +import org.eclipse.debug.internal.ui.DebugUIPlugin; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelChangedListener; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; +import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.IDoubleClickListener; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ITreeSelection; +import org.eclipse.jface.viewers.StructuredViewer; +import org.eclipse.jface.viewers.TreePath; +import org.eclipse.jface.viewers.Viewer; + + +/** + * This is the default implementation of {@link IModelProxy} interface for + * use by a view model provider. It implements an algorithm to walk the + * tree hierarchy of nodes configured with a provider in order to compose + * an {@link IModelDelta} for a given data model event. + * <p/> + * This class is closely linked with a view model provider which is required + * for the constructor. The view model provider is used to access the correct + * executor and the node hierarchy. + */ +@ConfinedToDsfExecutor("#getProvider()#getExecutor()") +@SuppressWarnings("restriction") +public class DefaultVMModelProxyStrategy implements IVMModelProxy, IVMModelProxyExtension { + + private final AbstractVMProvider fProvider; + private final Object fRootElement; + private IPresentationContext fContext; + private Viewer fViewer; + private boolean fDisposed = false; + private ListenerList fListeners = new ListenerList(); + private IDoubleClickListener fDoubleClickListener; + + /** + * Creates this model proxy strategy for the given provider. + */ + public DefaultVMModelProxyStrategy(AbstractVMProvider provider, Object rootElement) { + fProvider = provider; + fRootElement = rootElement; + } + + public boolean isDeltaEvent(Object event) { + IRootVMNode rootNode = getVMProvider().getRootVMNode(); + return rootNode != null && + rootNode.isDeltaEvent(getRootElement(), event) && + getDeltaFlags(rootNode, null, event) != 0; + } + + + /** + * Returns the view model provider that this strategy is configured for. + * @return + */ + protected AbstractVMProvider getVMProvider() { + return fProvider; + } + + + private Object[] getListeners() { + return fListeners.getListeners(); + } + + public void addModelChangedListener(IModelChangedListener listener) { + fListeners.add(listener); + } + + public void removeModelChangedListener(IModelChangedListener listener) { + fListeners.remove(listener); + } + + public Object getRootElement() { + return fRootElement; + } + + /** @since 1.1 */ + public Object getViewerInput() { + return fRootElement; + } + + /** @since 1.1 */ + public TreePath getRootPath() { + return TreePath.EMPTY; + } + + /** + * Notifies registered listeners of the given delta. + * + * @param delta model delta to broadcast + */ + public void fireModelChanged(IModelDelta delta) { + final IModelDelta root = getRootDelta(delta); + Object[] listeners = getListeners(); + for (int i = 0; i < listeners.length; i++) { + final IModelChangedListener listener = (IModelChangedListener) listeners[i]; + ISafeRunnable safeRunnable = new ISafeRunnable() { + public void handleException(Throwable exception) { + DebugUIPlugin.log(exception); + } + + public void run() throws Exception { + listener.modelChanged(root, DefaultVMModelProxyStrategy.this); + } + + }; + SafeRunner.run(safeRunnable); + } + } + + /** + * Convenience method that returns the root node of the given delta. + * + * @param delta delta node + * @return returns the root of the given delta + */ + protected IModelDelta getRootDelta(IModelDelta delta) { + IModelDelta parent = delta.getParentDelta(); + while (parent != null) { + delta = parent; + parent = delta.getParentDelta(); + } + return delta; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.IModelProxy#dispose() + */ + public void dispose() { + fDisposed = true; + if (fViewer instanceof StructuredViewer && fDoubleClickListener != null) { + ((StructuredViewer) fViewer).removeDoubleClickListener(fDoubleClickListener); + fDoubleClickListener= null; + } + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.IModelProxy#init(org.eclipse.debug.internal.ui.viewers.IPresentationContext) + */ + public void init(IPresentationContext context) { + fDisposed = false; + fContext = context; + } + + /** + * Returns the context this model proxy is installed in. + * + * @return presentation context, or <code>null</code> if this + * model proxy has been disposed + */ + public IPresentationContext getPresentationContext() { + return fContext; + } + + /* (non-Javadoc) + * + * Subclasses should override as required. + * + * @see org.eclipse.debug.internal.ui.viewers.provisional.IModelProxy#installed(org.eclipse.jface.viewers.Viewer) + */ + public void installed(final Viewer viewer) { + fViewer = viewer; + getVMProvider().getExecutor().execute( new DsfRunnable() { + public void run() { + fProvider.handleEvent(new ModelProxyInstalledEvent(DefaultVMModelProxyStrategy.this, viewer, fRootElement)); + } + }); + if (fViewer instanceof StructuredViewer && fDoubleClickListener == null) { + ((StructuredViewer) fViewer).addDoubleClickListener(fDoubleClickListener= new IDoubleClickListener() { + public void doubleClick(DoubleClickEvent e) { + handleDoubleClick(e); + } + }); + } + } + + /** + * Handle viewer double click. + * + * @param e the event + * + * @since 1.1 + */ + protected void handleDoubleClick(final DoubleClickEvent e) { + final AbstractVMProvider vmProvider= getVMProvider(); + if (!vmProvider.isDisposed()) { + ISelection selection = e.getSelection(); + if (!selection.isEmpty() && selection instanceof ITreeSelection) { + final TreePath path = ((ITreeSelection)selection).getPaths()[0]; + final Object input = e.getViewer().getInput(); + + vmProvider.getExecutor().execute( new DsfRunnable() { + public void run() { + Object rootElement = getRootElement(); + boolean eventContainsRootElement = rootElement.equals(input); + for (int i = 0; !eventContainsRootElement && i < path.getSegmentCount(); i++) { + eventContainsRootElement = rootElement.equals(path.getSegment(i)); + } + + if (eventContainsRootElement) { + vmProvider.handleEvent(e); + } + } + }); + } + } + } + + /** + * Returns the viewer this proxy is installed in. + * + * @return viewer or <code>null</code> if not installed + * + * @since 1.1 + */ + public Viewer getViewer() { + return fViewer; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy#isDisposed() + */ + public boolean isDisposed() { + return fDisposed; + } + + /** + * Recursively calls the VM nodes in the hierarchy of the given node + * to calculate the delta flags that are + * <p/> + * Note: If a child node has a <code>IModelDelta.CONTENT</code> delta + * flag, it means that this flag will be added to this node's element. + * To allow for optimization change the child's <code>IModelDelta.CONTENT</code> + * flag into a <code>IModelDelta.STATE</code> flag. + * + * @param node + * @param event + * @return + */ + protected int getDeltaFlags(IVMNode node, ModelDelta parentDelta, Object event) { + int flags = node.getDeltaFlags(event); + for (IVMNode childNode : getVMProvider().getChildVMNodes(node)) { + if (!childNode.equals(node)) { + int childNodeDeltaFlags = getDeltaFlags(childNode, parentDelta, event); + if ((childNodeDeltaFlags & IModelDelta.CONTENT) != 0) { + childNodeDeltaFlags &= ~IModelDelta.CONTENT; + childNodeDeltaFlags |= IModelDelta.STATE; + } + flags |= childNodeDeltaFlags; + } + } + // Optimization: If the parent delta contains the "content" flag, we do + // not need to add it to the child. This can shorten delta processing + // considerably so check for it. + while (parentDelta != null) { + if ( (parentDelta.getFlags() & IModelDelta.CONTENT) != 0 ) { + flags = flags & ~IModelDelta.CONTENT & ~IModelDelta.STATE; + break; + } + parentDelta = (ModelDelta)parentDelta.getParentDelta(); + } + return flags; + } + + /** + * Default implementation creates a delta assuming that the root node + * is the input object into the view. + */ + public void createDelta(final Object event, final DataRequestMonitor<IModelDelta> rm) { + final IRootVMNode rootNode = getVMProvider().getRootVMNode(); + + // Always create the rootDelta, no matter what delta flags the child nodes have. + rootNode.createRootDelta( + getRootElement(), event, + new DataRequestMonitor<VMDelta>(getVMProvider().getExecutor(), rm) { + @Override + protected void handleSuccess() { + // Find the root delta for the whole view to use when firing the delta. + // Note: the view root is going to be different than the model root + // in case when the view model provider is registered to populate only + // a sub-tree of a view. + final IModelDelta viewRootDelta = getRootDelta(getData()); + + // Find the child nodes that have deltas for the given event. + final Map<IVMNode,Integer> childNodesWithDeltaFlags = getChildNodesWithDeltaFlags(rootNode, getData(), event); + + // If no child nodes have deltas we can stop here. + if (childNodesWithDeltaFlags.size() == 0) { + rm.setData(viewRootDelta); + rm.done(); + return; + } + + callChildNodesToBuildDelta( + rootNode, + childNodesWithDeltaFlags, getData(), event, + new RequestMonitor(getVMProvider().getExecutor(), rm) { + @Override + protected void handleSuccess() { + rm.setData(viewRootDelta); + rm.done(); + } + }); + } + }); + } + + protected void buildChildDeltas(final IVMNode node, final Object event, final VMDelta parentDelta, + final int nodeOffset, final RequestMonitor rm) + { + node.getContextsForEvent( + parentDelta, + event, + new DataRequestMonitor<IVMContext[]>(getVMProvider().getExecutor(), rm) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + assert getData() != null; + buildChildDeltasForEventContext(getData(), node, event, parentDelta, nodeOffset, rm); + } else if (getStatus().getCode() == IDsfStatusConstants.NOT_SUPPORTED) { + // The DMC for this node was not found in the event. Call the + // super-class to resort to the default behavior which may add a + // delta for every element in this node. + buildChildDeltasForAllContexts(node, event, parentDelta, nodeOffset, rm); + } else { + super.handleCompleted(); + } + } + }); + } + + protected void buildChildDeltasForEventContext(final IVMContext[] vmcs, final IVMNode node, final Object event, + final VMDelta parentDelta, final int nodeOffset, final RequestMonitor requestMonitor) + { + final Map<IVMNode,Integer> childNodeDeltas = getChildNodesWithDeltaFlags(node, parentDelta, event); + if (childNodeDeltas.size() == 0) { + // There are no child nodes with deltas, just return to parent. + requestMonitor.done(); + return; + } + + // Check if any of the child nodes are will generate IModelDelta.SELECT or + // IModelDelta.EXPAND flags. If so, we must calculate the index for this + // VMC. + boolean calculateIndex = false; + if (nodeOffset >= 0) { + for (int childDelta : childNodeDeltas.values()) { + if ( (childDelta & (IModelDelta.SELECT | IModelDelta.EXPAND)) != 0 ) { + calculateIndex = true; + break; + } + } + } + + if (calculateIndex) { + // Calculate the index of this node by retrieving all the + // elements and then finding the DMC that the event is for. + getVMProvider().updateNode( + node, + new VMChildrenUpdate( + parentDelta, getVMProvider().getPresentationContext(), -1, -1, + new DataRequestMonitor<List<Object>>(getVMProvider().getExecutor(), requestMonitor) { + @Override + protected void handleSuccess() { + // Check for an empty list of elements. If it's empty then we + // don't have to call the children nodes, so return here. + // No need to propagate error, there's no means or need to display it. + if (getData().isEmpty()) { + requestMonitor.done(); + return; + } + + CountingRequestMonitor countingRm = + new CountingRequestMonitor(getVMProvider().getExecutor(), requestMonitor); + + int count = 0; + for (IVMContext vmc : vmcs) { + // Find the index of the vmc in the full list of elements. + int i; + for (i = 0; i < getData().size(); i++) { + if (vmc.equals(getData().get(i))) break; + } + if (i == getData().size()) { + // Element not found, no need to generate the delta. + continue; + } + + // Optimization: Try to find a delta with a matching element, if found use it. + // Otherwise create a new delta for the event element. + int elementIndex = nodeOffset + i; + VMDelta delta = parentDelta.getChildDelta(vmc); + if (delta == null || delta.getIndex() != elementIndex) { + delta = parentDelta.addNode(vmc, elementIndex, IModelDelta.NO_CHANGE); + } + + callChildNodesToBuildDelta(node, childNodeDeltas, delta, event, countingRm); + count++; + } + countingRm.setDoneCount(count); + } + })); + } else { + CountingRequestMonitor countingRm = + new CountingRequestMonitor(getVMProvider().getExecutor(), requestMonitor); + int count = 0; + for (IVMContext vmc : vmcs) { + // Optimization: Try to find a delta with a matching element, if found use it. + // Otherwise create a new delta for the event element. + VMDelta delta = parentDelta.getChildDelta(vmc); + if (delta == null) { + delta = parentDelta.addNode(vmc, IModelDelta.NO_CHANGE); + } + callChildNodesToBuildDelta(node, childNodeDeltas, delta, event, requestMonitor); + count++; + } + countingRm.setDoneCount(count); + } + } + + /** + * Base implementation that handles calling child nodes to build + * the model delta. The child nodes are called with all the elements + * in this node, which could be very inefficient. In order to build delta + * only for specific elements in this node, the class extending + * <code>AbstractVMNode</code> should override this method. + * @see IVMNode#buildDelta(Object, ModelDelta, int, RequestMonitor) + */ + protected void buildChildDeltasForAllContexts(final IVMNode node, final Object event, final VMDelta parentDelta, + final int nodeOffset, final RequestMonitor requestMonitor) + { + // Find the child nodes that have deltas for the given event. + final Map<IVMNode,Integer> childNodesWithDeltaFlags = getChildNodesWithDeltaFlags(node, parentDelta, event); + + // If no child nodes have deltas we can stop here. + if (childNodesWithDeltaFlags.size() == 0) { + requestMonitor.done(); + return; + } + + // Check if the child delta only has an IModelDelta.STATE flag. + // If that's the case, we can skip creating a delta for this node, + // because the TreeUpdatePolicy does not use the full path from the + // delta to handle these flags. Similarly, the index argument is + // not necessary either. + boolean mustGetElements = false; + for (int childDelta : childNodesWithDeltaFlags.values()) { + if ((childDelta & ~IModelDelta.STATE) != 0) { + mustGetElements = true; + } + } + + if (!mustGetElements) { + callChildNodesToBuildDelta(node, childNodesWithDeltaFlags, parentDelta, event, requestMonitor); + } else { + // The given child nodes have deltas potentially for all elements + // from this node. Retrieve all elements and call the child nodes with + // each element as the parent of their delta. + getVMProvider().updateNode( + node, + new VMChildrenUpdate( + parentDelta, getVMProvider().getPresentationContext(), -1, -1, + new DataRequestMonitor<List<Object>>(getVMProvider().getExecutor(), requestMonitor) { + @Override + protected void handleSuccess() { + if (fDisposed) return; + + // Check for an empty list of elements. If it's empty then we + // don't have to call the children nodes, so return here. + if (getData().size() == 0) { + requestMonitor.done(); + return; + } + + final MultiRequestMonitor<RequestMonitor> elementsDeltasMultiRequestMon = + new MultiRequestMonitor<RequestMonitor>(getVMProvider().getExecutor(), requestMonitor); + + // For each element from this node, create a new delta, + // and then call all the child nodes to build their delta. + for (int i = 0; i < getData().size(); i++) { + int elementIndex = nodeOffset >= 0 ? nodeOffset + i : -1; + VMDelta delta= parentDelta.getChildDelta(getData().get(i)); + if (delta == null) { + delta= parentDelta.addNode(getData().get(i), elementIndex, IModelDelta.NO_CHANGE); + } + callChildNodesToBuildDelta( + node, childNodesWithDeltaFlags, delta, event, + elementsDeltasMultiRequestMon.add(new RequestMonitor(getVMProvider().getExecutor(), null) { + @Override + protected void handleCompleted() { + elementsDeltasMultiRequestMon.requestMonitorDone(this); + } + })); + } + } + }) + ); + } + } + + /** + * Calls the specified child nodes to build the delta for the given event. + * @param childNodes Map of nodes to be invoked, and the corresponding delta + * flags that they will generate. This map is generated with a call to + * {@link #getChildNodesWithDeltaFlags(Object)}. + * @param delta The delta object to build on. This delta should have been + * generated by this node, unless the full delta path is not being calculated + * due to an optimization. + * @param event The event object that the delta is being built for. + * @param requestMonitor The result token to invoke when the delta is completed. + */ + protected void callChildNodesToBuildDelta(final IVMNode node, final Map<IVMNode,Integer> childNodes, final VMDelta delta, final Object event, final RequestMonitor requestMonitor) { + assert childNodes.size() != 0; + + // Check if any of the child nodes are will generate IModelDelta.SELECT or + // IModelDelta.EXPAND flags. If so, we must calculate the index for this + // VMC. + boolean calculateOffsets = false; + for (int childDelta : childNodes.values()) { + if ( (childDelta & (IModelDelta.SELECT | IModelDelta.EXPAND | IModelDelta.INSERTED | IModelDelta.REMOVED)) != 0 ) { + calculateOffsets = true; + break; + } + } + + getChildNodesElementOffsets( + node, delta, calculateOffsets, + new DataRequestMonitor<Map<IVMNode, Integer>>(getVMProvider().getExecutor(), requestMonitor) { + @Override + protected void handleSuccess() { + final CountingRequestMonitor multiRm = new CountingRequestMonitor(getVMProvider().getExecutor(), requestMonitor); + int multiRmCount = 0; + + // Set the total count of number of children in the parent delta. + delta.setChildCount(getData().get(null)); + + for (final IVMNode childNode : childNodes.keySet()) { + // Avoid descending into recursive node hierarchy's when calculating the delta. + if (node.equals(childNode)) continue; + + final int nodeOffset = getData().get(childNode); + childNode.buildDelta( + event, delta, nodeOffset, + new RequestMonitor(getVMProvider().getExecutor(), multiRm) { + @Override + protected void handleSuccess() { + buildChildDeltas( + childNode, event, delta, nodeOffset, new RequestMonitor(getVMProvider().getExecutor(), multiRm)); + } + }); + multiRmCount++; + } + multiRm.setDoneCount(multiRmCount); + } + }); + } + + /** + * Calculates the indexes at which the elements of each of the child + * nodes begin. These indexes are necessary to correctly + * calculate the deltas for elements in the child nodes. + * @param delta The delta object to build on. This delta should have been + * generated by this node, unless the full delta path is not being calculated + * due to an optimization. + * @param doCalculdateOffsets If true, the method calls each node to get its + * element count. If false, it causes this method to fill the return data + * structure with dummy values. The dummy values indicate that the indexes + * are not known and are acceptable in the delta if the delta flags being + * generated do not require full index information. + * @param rm Return token containing the results. The result data is a + * mapping between the child nodes and the indexes at which the child nodes' + * elements begin. There is a special value in the map with a <code>null</code> + * key, which contains the full element count for all the nodes. + */ + private void getChildNodesElementOffsets(IVMNode node, IModelDelta delta, boolean calculdateOffsets, final DataRequestMonitor<Map<IVMNode, Integer>> rm) { + final IVMNode[] childNodes = getVMProvider().getChildVMNodes(node); + assert childNodes.length != 0; + + if (calculdateOffsets) { + final Integer[] counts = new Integer[childNodes.length]; + final MultiRequestMonitor<RequestMonitor> childrenCountMultiRequestMon = + new MultiRequestMonitor<RequestMonitor>(getVMProvider().getExecutor(), rm) { + @Override + protected void handleSuccess() { + Map<IVMNode, Integer> data = new HashMap<IVMNode, Integer>(); + int offset = 0; + for (int i = 0; i < childNodes.length; i++) { + data.put(childNodes[i], offset); + offset += counts[i]; + } + // As the final value, put the total count in the return map, with null key. + data.put(null, offset); + rm.setData(data); + rm.done(); + } + }; + + for (int i = 0; i < childNodes.length; i++) { + final int nodeIndex = i; + getVMProvider().updateNode( + childNodes[i], + new VMChildrenCountUpdate( + delta, getVMProvider().getPresentationContext(), + childrenCountMultiRequestMon.add( + new DataRequestMonitor<Integer>(getVMProvider().getExecutor(), rm) { + @Override + protected void handleCompleted() { + counts[nodeIndex] = getData(); + childrenCountMultiRequestMon.requestMonitorDone(this); + } + }) + ) + ); + } + } else { + Map<IVMNode, Integer> data = new HashMap<IVMNode, Integer>(); + for (int i = 0; i < childNodes.length; i++) { + data.put(childNodes[i], -1); + } + data.put(null, -1); + rm.setData(data); + rm.done(); + } + } + + /** + * Convenience method that returns the child nodes which return + * <code>true</code> to the <code>hasDeltaFlags()</code> test for the given + * event. + */ + protected Map<IVMNode, Integer> getChildNodesWithDeltaFlags(IVMNode node, ModelDelta parentDelta, Object e) { + Map<IVMNode, Integer> nodes = new HashMap<IVMNode, Integer>(); + for (final IVMNode childNode : getVMProvider().getChildVMNodes(node)) { + if (!childNode.equals(node)) { + int delta = getDeltaFlags(childNode, parentDelta, e); + if (delta != IModelDelta.NO_CHANGE) { + nodes.put(childNode, delta); + } + } + } + return nodes; + } + + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/IRootVMNode.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/IRootVMNode.java new file mode 100644 index 00000000000..ef305c3c81f --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/IRootVMNode.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * 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.ui.viewmodel; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; + +/** + * Special type of the view model node, which can be used as a root node + * for a hierarchy. The root node of a layout hierarchy has to implement this + * interface. + */ +public interface IRootVMNode extends IVMNode{ + + /** + * Returns whether the given event should be processed for delta generation. + * Root node is different than other nodes in that there is only one root + * element in the view model provider hierarchy. This method allows the root + * node to match up the root object of the provider with the given event. If + * the root node can determine that the given event does not apply to the root + * object, it should return false so that the event is ignored. + * + * @param rootObject The root object of the VM provider + * @param event + * @return + */ + public boolean isDeltaEvent(Object rootObject, Object event); + + /** + * Version of the {@link IVMNode#buildDelta(Object, ViewModelDelta, org.eclipse.cdt.dsf.concurrent.RequestMonitor)} + * method, which creates and returns the root node of the delta. It does + * not require a parent object for the delta, as this is the root node. + * @param event Event to process. + * @param rm Result notification, contains the root of the delta. + */ + public void createRootDelta(Object rootObject, Object event, DataRequestMonitor<VMDelta> rm); +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/IVMAdapter.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/IVMAdapter.java new file mode 100644 index 00000000000..31db516f116 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/IVMAdapter.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2006, 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.ui.viewmodel; + +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentationFactory; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxyFactory; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputProvider; + +/** + * The View Model adapter handles the layout of a given data model within a + * set of viewers. This adapter should be returned by an adapter factory for + * the input object of the viewer, and this adapter implementation will then + * populate the view contents. + */ +@ThreadSafe +@SuppressWarnings("restriction") +public interface IVMAdapter + extends IElementContentProvider, IModelProxyFactory, IColumnPresentationFactory, IViewerInputProvider +{ + /** + * Returns the View Model Provider that is registered for the given presentation + * context. Returns <code>null</code> if there is none. + */ + public IVMProvider getVMProvider(IPresentationContext presentationContext); +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/IVMAdapterExtension.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/IVMAdapterExtension.java new file mode 100644 index 00000000000..87804008999 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/IVMAdapterExtension.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.ui.viewmodel; + +/** + * Extension to the IVMAdapter interface which allows access to the array of active + * providers. + * + * @since 1.1 + */ +public interface IVMAdapterExtension extends IVMAdapter { + + /** + * Retrieves the currently active VM providers in this adapter. + * + * @return array of VM providers + */ + public IVMProvider[] getActiveProviders(); +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/IVMContext.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/IVMContext.java new file mode 100644 index 00000000000..d43c1842faf --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/IVMContext.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * 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.ui.viewmodel; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.core.runtime.IAdaptable; + +/** + * View model element which is stored as the data object of nodes in the viewer. + * The implementation of this interface is usually a wrapper object for an object + * from some data model, which is then used to correctly implement the + * {@link #equals(Object)} and {@link #hashCode()} methods of this wrapper. + */ +@Immutable +public interface IVMContext extends IAdaptable { + + /** + * Returns the view model node that originated this element. + */ + public IVMNode getVMNode(); +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/IVMEventListener.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/IVMEventListener.java new file mode 100644 index 00000000000..1c9800834b2 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/IVMEventListener.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * 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.ui.viewmodel; + +import java.util.concurrent.Executor; + +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; + +/** + * A listener participating in event notifications sent out from VM adapter. + * + * @since 1.1 + */ +public interface IVMEventListener { + + /** + * Returns the executor that needs to be used to access this event listener. + */ + public Executor getExecutor(); + + /** + * Process the given event and indicate completion with request monitor. + */ + public void handleEvent(final Object event, RequestMonitor rm); + + /** + * Returns whether the event handling manager should wait for this listener + * to complete handling this event, or whether the event listener can process + * the event asynchronously. + */ + public boolean shouldWaitHandleEventToComplete(); +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/IVMModelProxy.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/IVMModelProxy.java new file mode 100644 index 00000000000..b688ea52529 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/IVMModelProxy.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * 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.ui.viewmodel; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy; + +/** + * View Model extension to the platform IModelProxy interface. This extension + * allows the IVMProvider implementation to delegate the model proxy implementation + * into a separate object. + * <br/> + * Note: The IVMModelProxy.init() may be called twice when installed, as a + * workaround for bug 241024. + */ +@SuppressWarnings("restriction") +public interface IVMModelProxy extends IModelProxy { + + /** + * Returns the root element that this model proxy was created for. + */ + public Object getRootElement(); + + /** + * Returns whether the given event applies to the root element and the + * nodes in this model proxy. + */ + public boolean isDeltaEvent(Object event); + + /** + * Creates a model delta for the given event. + */ + public void createDelta(final Object event, final DataRequestMonitor<IModelDelta> rm); + + /** + * Sends the given delta to this model proxy's listeners. + */ + public void fireModelChanged(IModelDelta delta); + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/IVMModelProxyExtension.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/IVMModelProxyExtension.java new file mode 100644 index 00000000000..812f4b39e3e --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/IVMModelProxyExtension.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * 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.ui.viewmodel; + +import org.eclipse.jface.viewers.TreePath; +import org.eclipse.jface.viewers.Viewer; + +/** + * View Model extension to IVMModelProxy interface. This extension + * allows access to the viewer. + * + * @since 1.1 + */ +public interface IVMModelProxyExtension extends IVMModelProxy { + + /** + * Returns the viewer. + */ + public Viewer getViewer(); + + /** + * Returns the viewer input that was set to the viewer when this proxy + * was created. + */ + public Object getViewerInput(); + + /** + * Returns the full path for the root element. If the path is empty, it + * means that the root element is the viewer input. + * @return + */ + public TreePath getRootPath(); +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/IVMNode.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/IVMNode.java new file mode 100644 index 00000000000..36279034b92 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/IVMNode.java @@ -0,0 +1,116 @@ +/******************************************************************************* + * 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.ui.viewmodel; + +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.service.IDsfService; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider; +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.IElementContentProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; + +/** + * View model nodes are configured with a view model provider to collectively + * define the layout of a view. Each layout node generates elements of type + * {@link IVMContext} which are then stored in the viewer. + * + * <p/> + * NOTE: This interface extends <code>IElementContentProvider</code> but it has + * slightly different parameter requirements. For the + * {@link IElementContentProvider#update(IChildrenUpdate[])} method, this class + * can accept an update where {@link IChildrenUpdate#getOffset()} and + * {@link IChildrenUpdate#getLength()} may return -1. In this case the + * implementation should return all available elements for the given parent.<br> + * Also the for the {@link IElementContentProvider#update(IHasChildrenUpdate[])} and + * {@link IElementContentProvider#update(IChildrenCountUpdate[])} methods, the + * implementation may return an error with an error code of {@link IDsfService#NOT_SUPPORTED}. + * In this case the caller of this update should call + * {@link IElementContentProvider#update(IChildrenUpdate[])} + * instead. + * + * @see AbstractDMVMProvider + * @see IElementContentProvider + */ +@ConfinedToDsfExecutor("") +@SuppressWarnings("restriction") +public interface IVMNode extends IElementContentProvider +{ + /** + * Retrieves the view model provider that this node is configured with. + */ + public IVMProvider getVMProvider(); + + /** + * Returns the potential delta flags that would be generated by this node + * for the given event. + * @param event Event to process. + * @return IModelDelta flags + * @see #buildDelta(Object, VMDelta, int, RequestMonitor) + * @see IModelDelta + */ + public int getDeltaFlags(Object event); + + /** + * Builds model delta information based on the given event. + * <p> + * Model deltas, which are used to control the state of elements in the viewer, are + * generated by the layout nodes by recursively calling this method on all the nodes + * in the layout tree. Each node implements two methods: {@link #getDeltaFlags(Object)}, + * and <code>buildDelta()</code>. A parent node which is processing a + * <code>buildDelta</code> operation needs to determine which of its elements are + * affected by a given event, set appropriate flags on these elements, and then + * it needs to call its child nodes with those elements to give the child nodes a + * chance to add onto the delta. + * </p> + * <p> + * The <code>getDeltaFlags()</code> is a synchronous + * call which tells the parent node whether on not to call the child node's + * <code>buildDelta</code> with the given event. If a child node return + * <code>true</code>, it only indicates that the node may add delta flags, but it + * does not require it to do so. + * </p> + * + * @param event Event to process. + * @param parent Parent model delta node that this object should add delta + * data to. + * @param nodeOffset The offset of the first element in this node. This offset + * depends on the elements returned by the siblings of this layout node. + * @param requestMonitor Return token, which notifies the caller that the calculation is + * complete. + */ + public void buildDelta(Object event, VMDelta parent, int nodeOffset, RequestMonitor requestMonitor); + + /** + * Retireves the view model elements for the given data model event. This method + * is optional and it allows the view model provider to optimize event processing + * by avoiding the need to retrieve all possible elements for the given node. + * </p> + * For example: If a threads node implementation is given a thread stopped event in + * this method, and the stopped event included a reference to the thread. Then + * the implementation should create a view model context for that thread and return it + * here. + * + * @param parentDelta The parent delta in the processing of this event. + * @param event The event to check for the data model object. + * @param Request monitor for the array of elements corresponding to the + * given event. + */ + public void getContextsForEvent(VMDelta parentDelta, Object event, DataRequestMonitor<IVMContext[]> rm); + + /** + * Releases the resources held by this node. + */ + public void dispose(); +}
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/IVMProvider.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/IVMProvider.java new file mode 100644 index 00000000000..5aa50485756 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/IVMProvider.java @@ -0,0 +1,114 @@ +/******************************************************************************* + * 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.ui.viewmodel; + +import java.util.concurrent.Executor; + +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; +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.IColumnPresentationFactory; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxyFactory; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputProvider; + +/** + * The view model provider handles the layout of a given model within a + * single viewer. The View Model Adapter delegates calls for view content to + * this object for a view that this provider handles. + * + * <p/> + * A given view model provider is typically configured with a number of + * {@link IVMNode} objects which are organized in a parent-child hierarchy. + * The node hierarchy has a root node which is retrieved using {@link #getRootVMNode()}. + * + * <p/> + * Note on concurency: The view model provider is single-threaded and it has to be + * accessed only using the <code>Executor</code> returned by {@link #getExecutor()}. + * The thread of this executor should be the display thread used by the viewer + * corresponding to the view model provider. Currently the flexible hierarchy + * interfaces that this interface extends do not guarantee that their methods + * will be called on the display thread, although from their use we are making + * this assumption (bug 213629). {@link IElementContentProvider} is an + * exception to this, it is called by the TreeModelViewer on a background + * thread, however it is not expected that the viewer will be calling the + * IVMProvider directly. Rather, it is expected that the viewer will call + * {@link IVMAdapter} which implements <code>IElementContentProvider</code>, + * and <code>IVMAdapter</code> implementation is expected to switch to + * provider's thread before delegating the call to it. + */ +@ConfinedToDsfExecutor("#getExecutor()") +@SuppressWarnings("restriction") +public interface IVMProvider + extends IElementContentProvider, IModelProxyFactory, IColumnPresentationFactory, IViewerInputProvider +{ + /** + * Returns the presentation context of the viewer that this provider + * is configured for. + */ + public IPresentationContext getPresentationContext(); + + /** + * Returns the VM Adapter associated with the provider. + */ + public IVMAdapter getVMAdapter(); + + /** + * Returns the executor that needs to be used to access this provider. + */ + public Executor getExecutor(); + + /** + * Returns the root node that is configured in this provider. + * It may return null, if a root node is not yet configured. + */ + public IRootVMNode getRootVMNode(); + + /** + * Returns an array of nodes which are configured as child nodes of the given node. + */ + public IVMNode[] getChildVMNodes(IVMNode node); + + /** + * Retrieves the list of all nodes configured for this provider. + */ + public IVMNode[] getAllVMNodes(); + + /** + * Calls the given view model node to perform the given updates. This + * method is different than calling the IVMNode update method directly in that + * it allows the provider to do additional processing on the update such as caching. + */ + public void updateNode(final IVMNode node, IHasChildrenUpdate[] updates); + + /** + * Calls the given view model node to perform the given updates. This + * method is different than calling the IVMNode update method directly in that + * it allows the provider to do additional processing on the update such as caching. + */ + public void updateNode(final IVMNode node, IChildrenCountUpdate updates); + + /** + * Calls the given view model node to perform the given updates. This + * method is different than calling the IVMNode update method directly in that + * it allows the provider to do additional processing on the update such as caching. + */ + public void updateNode(IVMNode node, IChildrenUpdate updates); + + + /** + * Cleans up the resources associated with this provider. + */ + public void dispose(); + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/ModelProxyInstalledEvent.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/ModelProxyInstalledEvent.java new file mode 100644 index 00000000000..741f7588780 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/ModelProxyInstalledEvent.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * 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.ui.viewmodel; + +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy; +import org.eclipse.jface.viewers.Viewer; + +/** + * Event generated by an IModelProxy implementation when it is installed + * into a viewer. + */ +@SuppressWarnings("restriction") +public class ModelProxyInstalledEvent { + private final IModelProxy fProxy; + private final Viewer fViewer; + private final Object fRootElement; + + public ModelProxyInstalledEvent(IModelProxy proxy, Viewer viewer, Object rootElement) { + fProxy = proxy; + fViewer = viewer; + fRootElement = rootElement; + } + + /** + * Returns the IModelProxy that generated this event. + */ + public IModelProxy getModelProxy() { + return fProxy; + } + + /** + * Returns the element that this model proxy was registered for. + */ + public Object getRootElement() { + return fRootElement; + } + + /** + * Returns the viewer that installed this model proxy. + */ + public Viewer getViewer() { + return fViewer; + } +}
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/RootVMNode.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/RootVMNode.java new file mode 100644 index 00000000000..41bbef29e45 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/RootVMNode.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.ui.viewmodel; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +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; + +/** + * Default implementation of a root view model node. This class may be sub-classed + * to implement model-specific event handling. + */ +@SuppressWarnings("restriction") +public class RootVMNode extends AbstractVMNode implements IRootVMNode { + + public RootVMNode(AbstractVMProvider provider) { + super(provider); + } + + public void update(IChildrenUpdate[] updates) { + throw new UnsupportedOperationException("Root view model node should never be queried for list of elements."); //$NON-NLS-1$ + } + + public void update(IChildrenCountUpdate[] updates) { + throw new UnsupportedOperationException("Root view model node should never be queried for list of elements."); //$NON-NLS-1$ + } + + public void update(IHasChildrenUpdate[] updates) { + throw new UnsupportedOperationException("Root view model node should never be queried for list of elements."); //$NON-NLS-1$ + } + + /** + * Default implementation does not examine the event and assumes that every + * event should be processed to generate a delta. + */ + public boolean isDeltaEvent(Object rootObject, Object event) { + if (event instanceof ModelProxyInstalledEvent) { + return rootObject.equals( ((ModelProxyInstalledEvent)event).getRootElement() ); + } + return true; + } + + /** + * Default implementation creates a delta assuming that the root layout node + * is the input object into the view. + */ + public void createRootDelta(Object rootObject, Object event, final DataRequestMonitor<VMDelta> rm) { + rm.setData(new VMDelta(rootObject, 0, IModelDelta.NO_CHANGE)); + rm.done(); + } + + + public int getDeltaFlags(Object event) { + return IModelDelta.NO_CHANGE; + } + + public void buildDelta(Object event, VMDelta parent, int nodeOffset, RequestMonitor requestMonitor) { + requestMonitor.done(); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/VMChildrenCountUpdate.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/VMChildrenCountUpdate.java new file mode 100644 index 00000000000..a4929a91361 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/VMChildrenCountUpdate.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * 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.ui.viewmodel; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; +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; + +/** + * Helper class implementation of the {@link IChildrenCountUpdate} update object. + * + * @see VMViewerUpdate + */ +@SuppressWarnings("restriction") +public class VMChildrenCountUpdate extends VMViewerUpdate implements IChildrenCountUpdate { + final private DataRequestMonitor<Integer> fCountRequestMonitor; + + public VMChildrenCountUpdate(IViewerUpdate clientUpdate, DataRequestMonitor<Integer> rm) { + super(clientUpdate, rm); + fCountRequestMonitor = rm; + } + + public VMChildrenCountUpdate(IModelDelta delta, IPresentationContext presentationContext, DataRequestMonitor<Integer> rm) { + super(delta, presentationContext, rm); + fCountRequestMonitor = rm; + } + + public VMChildrenCountUpdate(TreePath elementPath, Object viewerInput, IPresentationContext presentationContext, DataRequestMonitor<Integer> rm) { + super(elementPath, viewerInput, presentationContext, rm); + fCountRequestMonitor = rm; + } + + public void setChildCount(int count) { + fCountRequestMonitor.setData(count); + } + + @Override + public String toString() { + return "VMChildrenCountUpdate: " + getElement(); //$NON-NLS-1$ + } + + @Override + public void done() { + assert isCanceled() || fCountRequestMonitor.getData() != null || !fCountRequestMonitor.isSuccess(); + super.done(); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/VMChildrenUpdate.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/VMChildrenUpdate.java new file mode 100644 index 00000000000..73bcc1f9ea4 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/VMChildrenUpdate.java @@ -0,0 +1,122 @@ +/******************************************************************************* + * 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.ui.viewmodel; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +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; + +/** + * Helper class implementation of the {@link IChildrenUpdate} update object. + * + * @see VMViewerUpdate + */ +@SuppressWarnings("restriction") +public class VMChildrenUpdate extends VMViewerUpdate implements IChildrenUpdate { + private final int fOffset; + private final int fLength; + protected final List<Object> fElements; + + public VMChildrenUpdate(IViewerUpdate clientUpdate, int offset, int length, + DataRequestMonitor<List<Object>> requestMonitor) + { + super(clientUpdate, requestMonitor); + fOffset = offset; + fLength = length; + fElements = length > 0 ? new ArrayList<Object>(length) : new ArrayList<Object>(); + } + + public VMChildrenUpdate(IModelDelta delta, IPresentationContext presentationContext, int offset, int length, + DataRequestMonitor<List<Object>> rm) + { + super(delta, presentationContext, rm); + fOffset = offset; + fLength = length; + fElements = length > 0 ? new ArrayList<Object>(length) : new ArrayList<Object>(); + } + + public VMChildrenUpdate(TreePath elementPath, Object viewerInput, IPresentationContext presentationContext, + int offset, int length, DataRequestMonitor<List<Object>> rm) + { + super(elementPath, viewerInput, presentationContext, rm); + fOffset = offset; + fLength = length; + fElements = length > 0 ? new ArrayList<Object>(length) : new ArrayList<Object>(); + } + + public int getOffset() { + return fOffset; + } + + public int getLength() { + return fLength; + } + + public void setChild(Object element, int offset) { + // Calculate the index in array based on configured offset. + int idx = offset - (fOffset > 0 ? fOffset : 0); + + // To make sure that index is in valid range. + if (idx < 0 || (fLength > 0 && idx >= fLength)) return; + + // Increase the list size if needed. + ensureElementsSize(idx + 1); + + // Finally set the element in elements list. + fElements.set(idx, element); + } + + private void ensureElementsSize(int size) { + while (fElements.size() < size) { + fElements.add(null); + } + } + + @Override + public String toString() { + return "VMChildrenUpdate:" + getElement() + " {"+ getOffset() + "->" + (getOffset() + getLength()) + "}"; //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$ //$NON-NLS-4$ + } + + @Override + public void done() { + @SuppressWarnings("unchecked") + DataRequestMonitor<List<Object>> rm = (DataRequestMonitor<List<Object>>)getRequestMonitor(); + + /* See https://bugs.eclipse.org/bugs/show_bug.cgi?id=202109 + * + * A flexible hierarchy bug/optimization causes query with incorrect + * IChildrenUpdate[] array length. + * + * The problem manifests itself while deleting a register node. + * For example, if the register view displays: + * PC + * EAX + * EBX + * ECX + * EDX + * And EBX is deleted, forcing a refresh, the viewer will query + * for IChildrenUpdate[5] and IChildrenCountUpdate at the same time. + * + * To avoid this problem do not generate an error if the list of + * children is smaller than the list of requested indexes. Also, + * do not check if any of the elements are null. + */ + rm.setData(fElements); + super.done(); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/VMDelta.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/VMDelta.java new file mode 100644 index 00000000000..2e20cc6e2e2 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/VMDelta.java @@ -0,0 +1,358 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 - adapted to use in DSF + *******************************************************************************/ +package org.eclipse.cdt.dsf.ui.viewmodel; + +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDeltaVisitor; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; + +/** + * This delta class mostly just duplicates the ModelDelta implemention, but + * it allows clients to modify the flags after the original object is + * constructed. + * + * @see IModelDelta#getNodes() + */ +@SuppressWarnings("restriction") +public class VMDelta extends ModelDelta { + + private VMDelta fParent; + private Object fElement; + private int fFlags; + private VMDelta[] fNodes = EMPTY_NODES; + private Object fReplacement; + private int fIndex; + private static final VMDelta[] EMPTY_NODES = new VMDelta[0]; + private int fChildCount = -1; + + /** + * Constructs a new delta for the given element. + * + * @param vmcElement model element + * @param flags change flags + */ + public VMDelta(Object element, int flags) { + super(element, flags); + fElement = element; + fFlags = flags; + } + + /** + * Constructs a new delta for the given element to be replaced + * with the specified replacement element. + * + * @param vmcElement model element + * @param replacement replacement element + * @param flags change flags + */ + public VMDelta(Object element, Object replacement, int flags) { + super(element, replacement, flags); + fElement = element; + fReplacement = replacement; + fFlags = flags; + } + + /** + * Constructs a new delta for the given element to be inserted at + * the specified index. + * + * @param vmcElement model element + * @param index insertion position + * @param flags change flags + */ + public VMDelta(Object element, int index, int flags) { + super(element, index, flags); + fElement = element; + fIndex = index; + fFlags = flags; + } + + /** + * Constructs a new delta for the given element at the specified index + * relative to its parent with the given number of children. + * + * @param element model element + * @param index insertion position + * @param flags change flags + * @param childCount number of children this node has + */ + public VMDelta(Object element, int index, int flags, int childCount) { + super(element, index, flags, childCount); + fElement = element; + fIndex = index; + fFlags = flags; + fChildCount = childCount; + } + + /** + * Returns the non-VMC element if one is set, otherwise returns the VMC + * element of this delta node. + * @see org.eclipse.debug.internal.ui.viewers.IModelDelta#getElement() + */ + @Override + public Object getElement() { + return fElement; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.IModelDelta#getFlags() + */ + @Override + public int getFlags() { + return fFlags; + } + + /** + * Sets this delta's flags. + * + * @param flags + */ + @Override + public void setFlags(int flags) { + fFlags = flags; + } + + @Override + public void setChildCount(int count) { + fChildCount = count; + } + + /** + * Adds a child node to this delta with the given element and change flags, + * and returns the child delta. + * + * @param element child element to add + * @param flags change flags for child + * @return newly created child delta + */ + @Override + public VMDelta addNode(Object element, int flags) { + VMDelta node = new VMDelta(element, flags); + node.setParent(this); + addDelta(node); + return node; + } + + /** + * Adds a child node to this delta to replace the given element with the + * specified replacement element and change flags, and returns the + * newly created child delta. + * + * @param element child element to add to this delta + * @param replacement replacement element for the child element + * @param flags change flags + * @return newly created child delta + */ + @Override + public VMDelta addNode(Object element, Object replacement, int flags) { + VMDelta node = new VMDelta(element, replacement, flags); + node.setParent(this); + addDelta(node); + return node; + } + + /** + * Adds a child delta to this delta to insert the specified element at + * the given index, and returns the newly created child delta. + * + * @param element child element in insert + * @param index index of insertion + * @param flags change flags + * @return newly created child delta + */ + @Override + public VMDelta addNode(Object element, int index, int flags) { + VMDelta node = new VMDelta(element, index, flags); + node.setParent(this); + addDelta(node); + return node; + } + + /** + * Adds a child delta to this delta at the specified index with the + * given number of children, and returns the newly created child delta. + * + * @param element child element in insert + * @param index index of the element relative to parent + * @param flags change flags + * @param numChildren the number of children the element has + * @return newly created child delta + */ + @Override + public VMDelta addNode(Object element, int index, int flags, int numChildren) { + VMDelta node = new VMDelta(element, index, flags, numChildren); + node.setParent(this); + addDelta(node); + return node; + } + + /** + * Returns the child delta for the given element, or <code>null</code> if none. + * + * @param element child element + * @return corresponding delta node, or <code>null</code> + * + * @since 1.1 + */ + @Override + public VMDelta getChildDelta(Object element) { + if (fNodes != null) { + for (int i = 0; i < fNodes.length; i++) { + VMDelta delta = fNodes[i]; + if (element.equals(delta.getElement())) { + return delta; + } + } + } + return null; + } + + /** + * Sets the parent delta of this delta + * + * @param node parent delta + */ + void setParent(VMDelta node) { + fParent = node; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.IModelDelta#getParent() + */ + @Override + public VMDelta getParentDelta() { + return fParent; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.IModelDelta#getReplacementElement() + */ + @Override + public Object getReplacementElement() { + return fReplacement; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.IModelDelta#getIndex() + */ + @Override + public int getIndex() { + return fIndex; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.IModelDelta#getNodes() + */ + @Override + public VMDelta[] getChildDeltas() { + return fNodes; + } + + private void addDelta(VMDelta delta) { + if (fNodes.length == 0) { + fNodes = new VMDelta[]{delta}; + } else { + VMDelta[] nodes = new VMDelta[fNodes.length + 1]; + System.arraycopy(fNodes, 0, nodes, 0, fNodes.length); + nodes[fNodes.length] = delta; + fNodes = nodes; + } + } + + @Override + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("Model Delta Start\n"); //$NON-NLS-1$ + appendDetail(buf, this); + buf.append("Model Delta End\n"); //$NON-NLS-1$ + return buf.toString(); + } + + private void appendDetail(StringBuffer buf, VMDelta delta) { + buf.append("\tElement: "); //$NON-NLS-1$ + buf.append(delta.getElement()); + buf.append('\n'); + buf.append("\t\tFlags: "); //$NON-NLS-1$ + int flags = delta.getFlags(); + if (flags == 0) { + buf.append("NO_CHANGE"); //$NON-NLS-1$ + } else { + if ((flags & IModelDelta.ADDED) > 0) { + buf.append("ADDED | "); //$NON-NLS-1$ + } + if ((flags & IModelDelta.CONTENT) > 0) { + buf.append("CONTENT | "); //$NON-NLS-1$ + } + if ((flags & IModelDelta.EXPAND) > 0) { + buf.append("EXPAND | "); //$NON-NLS-1$ + } + if ((flags & IModelDelta.INSERTED) > 0) { + buf.append("INSERTED | "); //$NON-NLS-1$ + } + if ((flags & IModelDelta.REMOVED) > 0) { + buf.append("REMOVED | "); //$NON-NLS-1$ + } + if ((flags & IModelDelta.REPLACED) > 0) { + buf.append("REPLACED | "); //$NON-NLS-1$ + } + if ((flags & IModelDelta.SELECT) > 0) { + buf.append("SELECT | "); //$NON-NLS-1$ + } + if ((flags & IModelDelta.STATE) > 0) { + buf.append("STATE | "); //$NON-NLS-1$ + } + if ((flags & IModelDelta.INSTALL) > 0) { + buf.append("INSTALL | "); //$NON-NLS-1$ + } + if ((flags & IModelDelta.UNINSTALL) > 0) { + buf.append("UNINSTALL | "); //$NON-NLS-1$ + } + } + buf.append('\n'); + buf.append("\t\tIndex: "); //$NON-NLS-1$ + buf.append(delta.fIndex); + buf.append(" Child Count: "); //$NON-NLS-1$ + buf.append(delta.fChildCount); + buf.append('\n'); + IModelDelta[] nodes = delta.getChildDeltas(); + for (int i = 0; i < nodes.length; i++) { + appendDetail(buf, (VMDelta)nodes[i]); + } + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta#getChildCount() + */ + @Override + public int getChildCount() { + return fChildCount; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta#accept(org.eclipse.debug.internal.ui.viewers.provisional.IModelDeltaVisitor) + */ + @Override + public void accept(IModelDeltaVisitor visitor) { + doAccept(visitor, 0); + } + + @Override + protected void doAccept(IModelDeltaVisitor visitor, int depth) { + if (visitor.visit(this, depth)) { + ModelDelta[] childDeltas = getChildDeltas(); + for (int i = 0; i < childDeltas.length; i++) { + ((VMDelta)childDeltas[i]).doAccept(visitor, depth+1); + } + } + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/VMHasChildrenUpdate.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/VMHasChildrenUpdate.java new file mode 100644 index 00000000000..3c20145dc2b --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/VMHasChildrenUpdate.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * 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.ui.viewmodel; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +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.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; +import org.eclipse.jface.viewers.TreePath; + +/** + * Helper class implementation of the {@link IHasChildrenUpdate} update object. + * + * @see VMViewerUpdate + */ +@SuppressWarnings("restriction") +public class VMHasChildrenUpdate extends VMViewerUpdate implements IHasChildrenUpdate { + + final private DataRequestMonitor<Boolean> fHasElemsRequestMonitor; + + public VMHasChildrenUpdate(IViewerUpdate clientUpdate, DataRequestMonitor<Boolean> rm) { + super(clientUpdate, rm); + fHasElemsRequestMonitor = rm; + } + + public VMHasChildrenUpdate(IModelDelta delta, IPresentationContext presentationContext, DataRequestMonitor<Boolean> rm) { + super(delta, presentationContext, rm); + fHasElemsRequestMonitor = rm; + } + + public VMHasChildrenUpdate(TreePath elementPath, Object viewerInput, IPresentationContext presentationContext, DataRequestMonitor<Boolean> rm) { + super(elementPath, viewerInput, presentationContext, rm); + fHasElemsRequestMonitor = rm; + } + + public void setHasChilren(boolean hasChildren) { + fHasElemsRequestMonitor.setData(hasChildren); + } + + @Override + public String toString() { + return "VMHasChildrenUpdate: " + getElement(); //$NON-NLS-1$ + } + + @Override + public void done() { + assert isCanceled() || fHasElemsRequestMonitor.getData() != null || !fHasElemsRequestMonitor.isSuccess(); + super.done(); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/VMViewerUpdate.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/VMViewerUpdate.java new file mode 100644 index 00000000000..004bf39ec2d --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/VMViewerUpdate.java @@ -0,0 +1,186 @@ +/******************************************************************************* + * 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.ui.viewmodel; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.IRequest; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider; +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.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; + +/** + * Helper class implementation of the update objects used with + * {@link IElementContentProvider}, {@link IElementLabelProvider}, + * and {@link IElementMementoProvider}. The viewer update can be constructed + * using a higher level update object or a set of parameters to fulfill the + * <code>IViewerUpdate</code> interface. + */ +@SuppressWarnings("restriction") +public class VMViewerUpdate implements IViewerUpdate { + + /** + * The request monitor to be called when this update is completed. + */ + final private RequestMonitor fRequestMonitor; + + /** + * A higher-level update that this update is based on. If specified, the given + * update is used to delegate calls to {@link #cancel()} and {@link #isCanceled()}. + */ + final private IViewerUpdate fClientUpdate; + + /** + * The flag indicating whether this update was cancelled. This flag is not used + * if the {@link #fClientUpdate} is initialized. + */ + final private AtomicBoolean fCanceled; + + /** + * The viewer input object for this update. + */ + final private Object fViewerInput; + + /** + * The element object of this update. + */ + final private Object fElement; + + /** + * The element path of this update. + */ + final private TreePath fElementPath; + + /** + * The presentation context of this update. + */ + final private IPresentationContext fPresentationContext; + + /** + * Creates a viewer update based on a higher-level update. The update element + * information as well as cancel requests are delegated to the given client + * update. + * <p/> + * Note: this update will not automatically call the client update's + * {@link IRequest#done()} method. The user of this update should supply + * a request monitor which properly completes the client update. + * + * @param clientUpdate Client update that this update is based on. + * @param requestMonitor Call-back invoked when this update completes. + */ + public VMViewerUpdate(IViewerUpdate clientUpdate, RequestMonitor requestMonitor) { + fViewerInput = clientUpdate.getViewerInput(); + fElement = clientUpdate.getElement(); + fElementPath = clientUpdate.getElementPath(); + fPresentationContext = clientUpdate.getPresentationContext(); + fRequestMonitor = requestMonitor; + fClientUpdate = clientUpdate; + fCanceled = null; + } + + /** + * Request monitor which uses a model delta to calculate the element information. + * This update is useful when calculating a model delta for a given view model node. + * + * @param delta Model delta of a parent element. + * @param presentationContext Presentation context for this update. + * @param requestMonitor Call-back invoked when this update completes. + */ + public VMViewerUpdate(IModelDelta delta, IPresentationContext presentationContext, RequestMonitor requestMonitor) { + List<Object> elementList = new LinkedList<Object>(); + IModelDelta listDelta = delta; + elementList.add(0, listDelta.getElement()); + while (listDelta.getParentDelta() != null) { + listDelta = listDelta.getParentDelta(); + elementList.add(0, listDelta.getElement()); + } + fViewerInput = elementList.get(0); + fElement = elementList.get(elementList.size() - 1); + elementList.remove(0); + fElementPath = new TreePath(elementList.toArray()); + fPresentationContext = presentationContext; + fRequestMonitor = requestMonitor; + fClientUpdate = null; + fCanceled = new AtomicBoolean(false); + } + + /** + * Creates a viewer update with the given parameters. + * + * @param elementPath The path to the element for which the update is generated. + * @param viewerInput Input into the viewer of the update. + * @param presentationContext Presentation context for this update. + * @param requestMonitor Call-back invoked when this update completes. + */ + public VMViewerUpdate(TreePath elementPath, Object viewerInput, IPresentationContext presentationContext, RequestMonitor requestMonitor) { + fViewerInput = viewerInput; + fElement = elementPath.getSegmentCount() != 0 ? elementPath.getLastSegment() : viewerInput; + fElementPath = elementPath; + fPresentationContext = presentationContext; + fRequestMonitor = requestMonitor; + fClientUpdate = null; + fCanceled = new AtomicBoolean(false); + } + + protected RequestMonitor getRequestMonitor() { + return fRequestMonitor; + } + + public Object getViewerInput() { return fViewerInput; } + public Object getElement() { return fElement; } + public TreePath getElementPath() { return fElementPath; } + public IPresentationContext getPresentationContext() { return fPresentationContext; } + public IStatus getStatus() { return fRequestMonitor.getStatus(); } + public void setStatus(IStatus status) { fRequestMonitor.setStatus(status); } + + public boolean isCanceled() { + if (fClientUpdate != null) { + return fClientUpdate.isCanceled(); + } else { + return fCanceled.get(); + } + } + public void cancel() { + if (fClientUpdate != null) { + fClientUpdate.cancel(); + } else { + fCanceled.set(true); + } + } + + public void done() { + try { + if ( isCanceled() ) { + fRequestMonitor.cancel(); + fRequestMonitor.setStatus(new Status( IStatus.CANCEL, DsfUIPlugin.PLUGIN_ID," Update was cancelled") ); //$NON-NLS-1$ + } + fRequestMonitor.done(); + } catch (RejectedExecutionException e) { + // If the request monitor cannot be invoked still, try to complete the update to avoid + // leaving the viewer in an inconsistent state. + if (fClientUpdate != null) { + fClientUpdate.done(); + } + } + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/datamodel/AbstractDMVMAdapter.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/datamodel/AbstractDMVMAdapter.java new file mode 100644 index 00000000000..18465e69f95 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/datamodel/AbstractDMVMAdapter.java @@ -0,0 +1,102 @@ +/******************************************************************************* + * 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.ui.viewmodel.datamodel; + +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +import org.eclipse.cdt.dsf.datamodel.IDMEvent; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMAdapter; + +/** + * Base implementation for DSF-based view model adapters. + */ +@ThreadSafe +abstract public class AbstractDMVMAdapter extends AbstractVMAdapter +{ + private final DsfSession fSession; + + /** + * It is theoretically possible for a VM adapter to be disposed before it + * has a chance to register itself as event listener. This flag is used + * to avoid removing itself as listener in such situation. + */ + private boolean fRegisteredAsEventListener = false; + + /** + * Constructor for the View Model session. It is tempting to have the + * adapter register itself here with the session as the model adapter, but + * that would mean that the adapter might get accessed on another thread + * even before the deriving class is fully constructed. So it it better + * to have the owner of this object register it with the session. + * @param session + */ + public AbstractDMVMAdapter(DsfSession session) { + super(); + fSession = session; + // Add ourselves as listener for DM events events. + try { + session.getExecutor().execute(new Runnable() { + public void run() { + if (DsfSession.isSessionActive(getSession().getId())) { + getSession().addServiceEventListener(AbstractDMVMAdapter.this, null); + fRegisteredAsEventListener = true; + } + } + }); + } catch (RejectedExecutionException e) { + // Session shut down, not much we can do but wait to be disposed. + } + } + + @Override + public void dispose() { + try { + getSession().getExecutor().execute(new Runnable() { + public void run() { + if (fRegisteredAsEventListener && getSession().isActive()) { + fSession.removeServiceEventListener(AbstractDMVMAdapter.this); + } + } + }); + } catch (RejectedExecutionException e) { + // Session shut down. + } + super.dispose(); + } + + /** + * Returns the DSF session that this adapter is associated with. + * @return + */ + protected DsfSession getSession() { return fSession; } + + /** + * Handle "data model changed" event by generating a delta object for each + * view and passing it to the corresponding view model provider. The view + * model provider is then responsible for filling-in and sending the delta + * to the viewer. + * + * @param event + * + * @since 1.1 + */ + @DsfServiceEventHandler + public final void eventDispatched(final IDMEvent<?> event) { + // We're in session's executor thread (session in which the event originated). + if (isDisposed()) return; + + handleEvent(event); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/datamodel/AbstractDMVMNode.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/datamodel/AbstractDMVMNode.java new file mode 100644 index 00000000000..1dac1af30da --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/datamodel/AbstractDMVMNode.java @@ -0,0 +1,411 @@ +/******************************************************************************* + * 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.ui.viewmodel.datamodel; + +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.Immutable; +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.internal.ui.DsfUIPlugin; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.service.IDsfService; +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMContext; +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMNode; +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.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +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.IViewerUpdate; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.ITreeSelection; +import org.eclipse.jface.viewers.TreePath; + + +/** + * View model node based on a single Data Model Context type. + * The assumption in this implementation is that elements of this node have + * a single IDMContext associated with them, and all of these contexts + * are of the same class type. + */ +@SuppressWarnings("restriction") +abstract public class AbstractDMVMNode extends AbstractVMNode implements IVMNode { + + /** + * IVMContext implementation used for this schema node. + */ + @Immutable + protected class DMVMContext extends AbstractVMContext implements IDMVMContext { + private final IDMContext fDmc; + + public DMVMContext(IDMContext dmc) { + super(AbstractDMVMNode.this); + fDmc = dmc; + } + + public IDMContext getDMContext() { return fDmc; } + + /** + * 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(fDmc)) { + return fDmc; + } else { + return fDmc.getAdapter(adapter); + } + } + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof AbstractDMVMNode.DMVMContext)) return false; + DMVMContext otherVmc = (DMVMContext)other; + return AbstractDMVMNode.this.equals(otherVmc.getVMNode()) && + fDmc.equals(otherVmc.fDmc); + } + + @Override + public int hashCode() { + return AbstractDMVMNode.this.hashCode() + fDmc.hashCode(); + } + + @Override + public String toString() { + return fDmc.toString(); + } + } + + private DsfSession fSession; + + private DsfServicesTracker fServicesTracker; + + /** + * Concrete class type that the elements of this schema node are based on. + * Even though the data model type is a parameter the DMContextVMLayoutNode, + * this type is erased at runtime, so a concrete class typs of the DMC + * is needed for instanceof chacks. + */ + private Class<? extends IDMContext> fDMCClassType; + + /** + * Constructor initializes instance data, except for the child nodes. + * Child nodes must be initialized by calling setChildNodes() + * @param session + * @param dmcClassType + * @see #setChildNodes(IVMNode[]) + */ + public AbstractDMVMNode(AbstractDMVMProvider provider, DsfSession session, Class<? extends IDMContext> dmcClassType) { + super(provider); + fSession = session; + fServicesTracker = new DsfServicesTracker(DsfUIPlugin.getBundleContext(), session.getId()); + fDMCClassType = dmcClassType; + } + + @Override + public void dispose() { + fServicesTracker.dispose(); + super.dispose(); + } + + @Override + public void getContextsForEvent(VMDelta parentDelta, Object event, DataRequestMonitor<IVMContext[]> rm) { + if (event instanceof IDMEvent<?>) { + IDMEvent<?> dmEvent = (IDMEvent<?>)event; + IDMContext dmc = DMContexts.getAncestorOfType(dmEvent.getDMContext(), fDMCClassType); + if (dmc != null) { + rm.setData(new IVMContext[] { createVMContext(dmc) }); + rm.done(); + return; + } + } + super.getContextsForEvent(parentDelta, event, rm); + } + + protected AbstractDMVMProvider getDMVMProvider() { + return (AbstractDMVMProvider)getVMProvider(); + } + + protected DsfSession getSession() { + return fSession; + } + + protected DsfServicesTracker getServicesTracker() { + return fServicesTracker; + } + + @Override + protected boolean checkUpdate(IViewerUpdate update) { + if (!super.checkUpdate(update)) return false; + + // Extract the VMC from the update (whatever the update sub-class. + Object element = update.getElement(); + if (element instanceof IDMVMContext) { + // If update element is a DMC, check if session is still alive. + IDMContext dmc = ((IDMVMContext)element).getDMContext(); + if (dmc.getSessionId() != getSession().getId() || !DsfSession.isSessionActive(dmc.getSessionId())) { + handleFailedUpdate(update); + return false; + } + } + return true; + } + + /** + * Convenience method that checks whether the given dmc context is null. If it is null, an + * appropriate error message is set in the update. + * @param dmc Data Model Context (DMC) to check. + * @param update Update to handle in case the DMC is null. + * @return true if the DMC is NOT null, indicating that it's OK to proceed. + * + * This method has been deprecated. Users should simply perform this functionality in-line + * + * Example : + * + * IExampleDmc dmc = final TimerDMContext dmc = findDmcInPath(...) + * if ( dmc == null ) { + * handleFailedUpdate(update); + * // + * // Perform whatever cleanup or completion is needed because of a lack of + * // a valid data model context. + * // + * ........ + * return; + * } + */ + + @Deprecated + protected boolean checkDmc(IDMContext dmc, IViewerUpdate update) { + if (dmc == null) { + update.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, + "No valid context found.", null)); //$NON-NLS-1$ + handleFailedUpdate(update); + return false; + } + return true; + } + + /** + * A convenience method that checks whether a given service exists. If the service does not + * exist, the update is filled in with the appropriate error message. + * @param serviceClass Service class to find. + * @param filter Service filter to use in addition to the service class name. + * @param update Update object to fill in. + * @return true if service IS found, indicating that it's OK to proceed. + * + * This method has been deprecated. Users should simply perform this functionality in-line + * + * Example : + * + * IExampleService service = getServicesTracker().getService(IExampleService.class,null); + * if ( service == null ) { + * handleFailedUpdate(update); + * // + * // Perform whatever cleanup or completion is needed because of a lack of the service. + * // + * ........ + * return; + * } + */ + @Deprecated + protected boolean checkService(Class<? extends IDsfService> serviceClass, String filter, IViewerUpdate update) { + if (getServicesTracker().getService(serviceClass, filter) == null) { + update.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, + "Service " + serviceClass.getName() + " not available.", null)); //$NON-NLS-1$ //$NON-NLS-2$ + handleFailedUpdate(update); + return false; + } + return true; + } + + public void update(final IHasChildrenUpdate[] updates) { + try { + getSession().getExecutor().execute(new DsfRunnable() { + public void run() { + for (IHasChildrenUpdate update : updates) { + if (!checkUpdate(update)) continue; + updateHasElementsInSessionThread(update); + } + }}); + } catch (RejectedExecutionException e) { + for (IViewerUpdate update : updates) { + handleFailedUpdate(update); + } + } + } + + @ConfinedToDsfExecutor("getSession().getExecutor()") + protected void updateHasElementsInSessionThread(final IHasChildrenUpdate update) { + update.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED, "Not implemented, clients should call to update all children instead.", null)); //$NON-NLS-1$ + update.done(); + } + + public void update(final IChildrenCountUpdate[] updates) { + try { + getSession().getExecutor().execute(new DsfRunnable() { + public void run() { + for (IChildrenCountUpdate update : updates) { + if (!checkUpdate(update)) continue; + updateElementCountInSessionThread(update); + } + }}); + } catch (RejectedExecutionException e) { + for (IViewerUpdate update : updates) { + handleFailedUpdate(update); + } + } + } + + @ConfinedToDsfExecutor("getSession().getExecutor()") + protected void updateElementCountInSessionThread(final IChildrenCountUpdate update) { + update.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED, "Not implemented, clients should call to update all children instead.", null)); //$NON-NLS-1$ + update.done(); + } + + public void update(final IChildrenUpdate[] updates) { + try { + getSession().getExecutor().execute(new DsfRunnable() { + public void run() { + // After every dispatch, must check if update still valid. + for (IChildrenUpdate update : updates) { + if (!checkUpdate(update)) continue; + updateElementsInSessionThread(update); + } + }}); + } catch (RejectedExecutionException e) { + for (IViewerUpdate update : updates) { + handleFailedUpdate(update); + } + } + } + + @ConfinedToDsfExecutor("getSession().getExecutor()") + abstract protected void updateElementsInSessionThread(IChildrenUpdate update); + + /** + * Utility method that takes an array of DMC object and creates a + * corresponding array of IVMContext elements base on that. + * @param parent The parent for generated IVMContext elements. + * @param dmcs Array of DMC objects to build return array on. + * @return Array of IVMContext objects. + */ + protected IVMContext[] dmcs2vmcs(IDMContext[] dmcs) { + IVMContext[] vmContexts = new IVMContext[dmcs.length]; + for (int i = 0; i < dmcs.length; i++) { + vmContexts[i] = createVMContext(dmcs[i]); + } + return vmContexts; + } + + /** + * Fill update request with view model contexts based on given data model contexts. + * Assumes that data model context elements start at index 0. + * + * @param update the viewer update request + * @param dmcs the data model contexts + */ + protected void fillUpdateWithVMCs(IChildrenUpdate update, IDMContext[] dmcs) { + fillUpdateWithVMCs(update, dmcs, 0); + } + + /** + * Fill update request with view model contexts based on given data model contexts. + * + * @param update the viewer update request + * @param dmcs the data model contexts + * @param firstIndex the index of the first data model context + * + * @since 1.1 + */ + protected void fillUpdateWithVMCs(IChildrenUpdate update, IDMContext[] dmcs, int firstIndex) { + int updateIdx = update.getOffset() != -1 ? update.getOffset() : 0; + final int endIdx = updateIdx + (update.getLength() != -1 ? update.getLength() : dmcs.length); + int dmcIdx = updateIdx - firstIndex; + if (dmcIdx < 0) { + updateIdx -= dmcIdx; + dmcIdx = 0; + } + while (updateIdx < endIdx && dmcIdx < dmcs.length) { + update.setChild(createVMContext(dmcs[dmcIdx++]), updateIdx++); + } + } + + protected IDMVMContext createVMContext(IDMContext dmc) { + return new DMVMContext(dmc); + } + + /** + * Creates a default CompositeDMVMContext which represents the selection. + * This can be overridden by view model providers which for their own purposes. + * @param update defines the selection to be updated to + * @return DM Context which represent the current selection + */ + protected IDMContext createCompositeDMVMContext(IViewerUpdate update) { + return new CompositeDMVMContext(update); + } + + /** + * Searches for a DMC of given type in the tree patch contained in given + * VMC. Only a DMC in the same session will be returned. + * @param <V> Type of the DMC that will be returned. + * @param vmc VMC element to search. + * @param dmcType Class object for matching the type. + * @return DMC, or null if not found. + */ + protected <T extends IDMContext> T findDmcInPath(Object inputObject, TreePath path, Class<T> dmcType) { + T retVal = null; + for (int i = path.getSegmentCount() - 1; i >= 0; i--) { + if (path.getSegment(i) instanceof IDMVMContext) { + IDMContext dmc = ((IDMVMContext)path.getSegment(i)).getDMContext(); + if ( dmc.getSessionId().equals(getSession().getId()) ) { + retVal = DMContexts.getAncestorOfType(dmc, dmcType); + if (retVal != null) break; + } + } + } + // Search the root object of the layout hierarchy. + if (retVal == null) { + if (inputObject instanceof ITreeSelection) { + ITreeSelection inputSelection = (ITreeSelection)inputObject; + if (inputSelection.getPaths().length == 1) { + retVal = findDmcInPath(null, inputSelection.getPaths()[0], dmcType); + } + } else if (inputObject instanceof IStructuredSelection) { + Object rootElement = ((IStructuredSelection)inputObject).getFirstElement(); + if (rootElement instanceof IDMVMContext) { + retVal = DMContexts.getAncestorOfType(((IDMVMContext)rootElement).getDMContext(), dmcType); + } + } else if (inputObject instanceof IDMVMContext) { + retVal = DMContexts.getAncestorOfType(((IDMVMContext)inputObject).getDMContext(), dmcType); + } + } + + return retVal; + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/datamodel/AbstractDMVMProvider.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/datamodel/AbstractDMVMProvider.java new file mode 100644 index 00000000000..1b7d36109a2 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/datamodel/AbstractDMVMProvider.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * 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.ui.viewmodel.datamodel; + +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.cdt.dsf.datamodel.IDMEvent; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMAdapter; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.update.AbstractCachingVMProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.provisional.IAsynchronousContentAdapter; +import org.eclipse.debug.internal.ui.viewers.provisional.IAsynchronousLabelAdapter; + +/** + * View model provider implements the asynchronous view model functionality for + * a single view. This provider is just a holder which further delegates the + * model provider functionality to the view model nodes that need + * to be configured with each provider. + * <p> + * The view model provider, often does not provide the model for the entire + * view. Rather, it needs to be able to plug in at any level in the viewer's + * content model and provide data for a sub-tree. + * + * @see IAsynchronousContentAdapter + * @see IAsynchronousLabelAdapter + * @see IModelProxy + * @see IVMNode + */ +@ConfinedToDsfExecutor("fSession#getExecutor") +@SuppressWarnings("restriction") +abstract public class AbstractDMVMProvider extends AbstractCachingVMProvider +{ + private final DsfSession fSession; + /** + * Constructs the view model provider for given DSF session. The + * constructor is thread-safe to allow VM provider to be constructed + * synchronously when a call to getAdapter() is made on an element + * in a view. + */ + public AbstractDMVMProvider(AbstractVMAdapter adapter, IPresentationContext presentationContext, DsfSession session) { + super(adapter, presentationContext); + fSession = session; + } + + public DsfSession getSession() { return fSession; } + + /** + * @deprecated Kept for API compatibility reasons. + * Events are now received and dispatched by the VM adapter. + */ + @Deprecated + @DsfServiceEventHandler + public void eventDispatched(final IDMEvent<?> event) { + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/datamodel/CompositeDMVMContext.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/datamodel/CompositeDMVMContext.java new file mode 100644 index 00000000000..b09782f0d66 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/datamodel/CompositeDMVMContext.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * 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.ui.viewmodel.datamodel; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.cdt.dsf.datamodel.CompositeDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; +import org.eclipse.jface.viewers.TreePath; + +/** + * Object used to combine several DM Contexts found in a tree path of a viewer + * update. This object allows the view model to pass complete data model context + * information found in the view to the services. + */ +@SuppressWarnings("restriction") +public class CompositeDMVMContext extends CompositeDMContext { + + /** + * Have to pass in an empty array of contexts to parent constructor + * in order to be able to calculate the + */ + private static IDMContext[] EMPTY_CONTEXTS_ARRAY = new IDMContext[0]; + + /** + * The list of parent contexts derived from the input object and + * the path. It is calculated on demand. + */ + private IDMContext[] fParents; + + /** + * Creates a composite context based in a viewer update. + */ + public CompositeDMVMContext(IViewerUpdate update) { + this(update.getViewerInput(), update.getElementPath()); + } + + /** + * Creates a composite context based on a viewer input and a tree path. + */ + public CompositeDMVMContext(Object viewerInputObject, TreePath treePath) { + super(EMPTY_CONTEXTS_ARRAY); + List<IDMContext> parentsList = new ArrayList<IDMContext>(treePath.getSegmentCount() + 1); + for (int i = treePath.getSegmentCount() - 1; i >=0 ; i--) { + if (treePath.getSegment(i) instanceof IDMVMContext) { + parentsList.add( ((IDMVMContext)treePath.getSegment(i)).getDMContext() ); + } + } + if (viewerInputObject instanceof IDMVMContext) { + parentsList.add( ((IDMVMContext)viewerInputObject).getDMContext() ); + } + + fParents = parentsList.toArray(new IDMContext[parentsList.size()]); +} + + @Override + public IDMContext[] getParents() { + return fParents; + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/datamodel/IDMVMContext.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/datamodel/IDMVMContext.java new file mode 100644 index 00000000000..12b989fcd71 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/datamodel/IDMVMContext.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * 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.ui.viewmodel.datamodel; + +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMContext; + +/** + * Interface for a view model context based on a DSF data model context. + */ +public interface IDMVMContext extends IVMContext { + + public static Object REFRESH_EVENT = new Object(); + + /** + * returns the data model context that this view model context wraps. + */ + public IDMContext getDMContext(); +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/datamodel/RootDMVMNode.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/datamodel/RootDMVMNode.java new file mode 100644 index 00000000000..c847131020e --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/datamodel/RootDMVMNode.java @@ -0,0 +1,92 @@ +/******************************************************************************* + * 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.ui.viewmodel.datamodel; + +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.ui.viewmodel.AbstractVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.IRootVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.RootVMNode; + +/** + * This is is a standard root node which listens to the selection in Debug View. + * Views such as variables and registers base their content on the + * selection in Debug view, and this node provides tracking of that selection. + * <p> + * Note: The variables/registers views track the selection using the same + * IDebugContextListener interface, but they only use the first element of the + * selection, as in IStructuredSelection.getFirstElement(). Therefore the root + * node also has to use the first element as the root object instead of the + * whole selection. + */ +public class RootDMVMNode extends RootVMNode + implements IRootVMNode +{ + public RootDMVMNode(AbstractVMProvider provider) { + super(provider); + } + + + @Override + public String toString() { + return "RootDMVMNode"; //$NON-NLS-1$ + } + + /** + * If the input object is a Data Model context, and the event is a DMC event. + * Then we can filter the event to make sure that the view does not + * react to events that relate to objects outside this view. + * + * The logic is such: + * - iterate through the full hierarchy of the DMC in the event, + * - for each DMC in event, search for a DMC of the same type in the input + * event, + * - if an ancestor of that type is found, it indicates that the event + * and the input object share the same hierarchy + * - finally compare the DMContexts from the event to the DMC from the input + * object, + * - if there is a match then we know that the event relates + * to the hierarchy in view, + * - if there is no match, then we know that the event related to a + * some sibling of the input object, and no delta should be generated, + * - if none of the ancestor types matched, then the event is completely + * unrelated to the input object, and the layout nodes in the view must + * determine whether a delta is needed. + */ + @Override + public boolean isDeltaEvent(Object rootObject, Object event) { + if (rootObject instanceof IDMVMContext) { + IDMContext inputDmc = ((IDMVMContext)rootObject).getDMContext(); + if (event instanceof IDMEvent && inputDmc != null) { + boolean potentialMatchFound = false; + boolean matchFound = false; + + IDMContext eventDmc = ((IDMEvent<?>)event).getDMContext(); + for (IDMContext eventDmcAncestor : DMContexts.toList(eventDmc)) { + IDMContext inputDmcAncestor = DMContexts.getAncestorOfType(inputDmc, eventDmcAncestor.getClass()); + if (inputDmcAncestor != null) { + potentialMatchFound = true; + if (inputDmcAncestor.equals(eventDmcAncestor)) { + return true; + } + } + } + if (potentialMatchFound && !matchFound) { + return false; + } + } + } + + return true; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/package.html b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/package.html new file mode 100644 index 00000000000..12e15efaf52 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/package.html @@ -0,0 +1,32 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html> +<head> + <meta content="text/html; charset=ISO-8859-1" + http-equiv="content-type"> + <title>Eclipse Device Debug - Debugger Services Framework - View Model</title> +</head> +<body> +Provides a set of utilities and interfaces for defining the +conent and layout of views based on Debugger Services Framework (DSF).<br> +<br> +<h2>Package Specification</h2> +<p>The DSF data model is designed in a way to make it easy to achieve +modularity in the services that collectively make-up its +contents. This design goal makes it difficult to display the data +in a view, because its layout of the data needs to depend on the type +of view. This package is used to make it simpler to take the +contents of a data model and display them in various debug views, using +the platform debug flexible-hierarchy asynchronous debug +views. <br> +</p> +<h3>Development Plans</h3> +This package is also a work in progress and it is missing a major +feature. +This feature is to be able to create a complete view layout using a +simple data specification (XML or other format). Still further, a +feature to build on this, should allow users to modify the view layout +using GUI tools and to apply the modified layout at runtime.<br> +<p><br> +</p> +</body> +</html> diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/IElementPropertiesProvider.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/IElementPropertiesProvider.java new file mode 100644 index 00000000000..3aa62cc54eb --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/IElementPropertiesProvider.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * 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.ui.viewmodel.properties; + + +/** + * Provides context-sensitive properties. Can be registered as an adapter for + * an element or implemented directly + */ +public interface IElementPropertiesProvider { + + /** + * Updates the specified property sets. + * + * @param updates each update specifies the element and context for which + * a set of properties is requested and stores them + */ + public void update(IPropertiesUpdate[] updates); +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/ILabelAttributeChangedListener.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/ILabelAttributeChangedListener.java new file mode 100644 index 00000000000..fecec2e4ea1 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/ILabelAttributeChangedListener.java @@ -0,0 +1,19 @@ +/******************************************************************************* + * 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.ui.viewmodel.properties; + +/** + * + */ +public interface ILabelAttributeChangedListener { + + public void attributesChanged(); +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/IPropertiesUpdate.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/IPropertiesUpdate.java new file mode 100644 index 00000000000..6657c7d5499 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/IPropertiesUpdate.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * 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.ui.viewmodel.properties; + +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; + +/** + * Context sensitive properties update request for an element. + */ +@SuppressWarnings("restriction") +public interface IPropertiesUpdate extends IViewerUpdate { + /** + * Returns the list of element properties that the provider should set. + * If <code>null</code>, all available properties should be set. + */ + public String[] getProperties(); + + /** + * Sets the given property to update. + * @param property Property ID. + * @param value Property value. + */ + public void setProperty(String property, Object value); +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/LabelAttribute.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/LabelAttribute.java new file mode 100644 index 00000000000..4b98d28e81a --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/LabelAttribute.java @@ -0,0 +1,109 @@ +/******************************************************************************* + * 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.ui.viewmodel.properties; + +import java.util.Map; + +import org.eclipse.core.runtime.ListenerList; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; + +/** + * This is a base class for a label attribute used in generating label + * information based on properties of an element. There are currently + * four types of attributes: text, image, font, and color, and a given + * attribute can be either enabled or disabled based on the element + * properties. + * <p/> + * Clients are intended to override this class and its extensions to + * implement the {@link LabelAttribute#isEnabled(Map)} and + * {@link LabelAttribute#getPropertyNames()} methods as needed. Clients can + * also override how the attribute settings are stored, for example in + * order to use a preference. + * + * @see PropertyBasedLabelProvider + * @see LabelColumnInfo + */ +@SuppressWarnings("restriction") +abstract public class LabelAttribute { + public static final String[] EMPTY_PROPERTY_NAMES_ARRAY = new String[0]; + + /** + * Listeners for when this attribute is modified. + */ + private ListenerList fListeners = new ListenerList(); + + public LabelAttribute() { + } + + /** + * Disposes this attribute. + */ + public void dispose() { + } + + /** + * Registers the given listener for changes in this attribute. A change in + * the attributes of a label should cause a view to repaint. + * @param listener Listener to register. + */ + public void addChangedListener(ILabelAttributeChangedListener listener) { + fListeners.add(listener); + } + + /** + * Unregisters the given listener. + * @param listener Listener to unregister. + */ + public void removeChangedListener(ILabelAttributeChangedListener listener) { + fListeners.remove(listener); + } + + /** + * Calls the listeners to notify them that this attribute has changed. + */ + protected void fireAttributeChanged() { + Object[] listeners = fListeners.getListeners(); + for (Object listener : listeners) { + ((ILabelAttributeChangedListener)listener).attributesChanged(); + } + } + + /** + * Returns the propertis that are needed by this attribute in order to + * determine whether this attribute is enabled and/or for the actual + * attribute itself. + * @return Array of names of properties for the element properties provider. + */ + public String[] getPropertyNames() { + return EMPTY_PROPERTY_NAMES_ARRAY; + } + + /** + * Returns whether this attribute is enabled for an element which has + * the given properties. + * @param properties Map or element properties. The client should ensure + * that all properties specified by {@link #getPropertyNames()} are + * supplied in this map. + * @return true if this attribute is enabled. + */ + public boolean isEnabled(Map<String, Object> properties) { + return true; + } + + /** + * Updates the label with this attribute. + * + * @param update Label update object to write to. + * @param columnIndex Colum index to write at. + * @param properties Element properties to use. + */ + abstract public void updateAttribute(ILabelUpdate update, int columnIndex, Map<String, Object> properties); +}
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/LabelColor.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/LabelColor.java new file mode 100644 index 00000000000..ef09f7814b6 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/LabelColor.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * 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.ui.viewmodel.properties; + +import java.util.Map; + +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.swt.graphics.RGB; + +/** + * The color attribute of a label. It determines what foreground and + * background color to use for the given label. + * + * @see LabelAttribute + * @see LabelColumnInfo + * @see PropertyBasedLabelProvider + */ + +@SuppressWarnings("restriction") +public class LabelColor extends LabelAttribute { + private RGB fForeground; + private RGB fBackground; + + public LabelColor() { + this(null, null); + } + + public LabelColor(RGB foreground, RGB background) { + fForeground = foreground; + fBackground = background; + } + + public RGB getForeground() { + return fForeground; + } + + public RGB getBackground() { + return fBackground; + } + + public void setForeground(RGB foreground) { + fForeground = foreground; + fireAttributeChanged(); + } + + public void setBackground(RGB background) { + fBackground = background; + fireAttributeChanged(); + } + + @Override + public void updateAttribute(ILabelUpdate update, int columnIndex, Map<String, Object> properties) { + RGB foreground = getForeground(); + if (foreground != null) { + update.setForeground(foreground, columnIndex); + } + + RGB background = getBackground(); + if (background != null) { + update.setBackground(background, columnIndex); + } + } +}
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/LabelColumnInfo.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/LabelColumnInfo.java new file mode 100644 index 00000000000..f058385178f --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/LabelColumnInfo.java @@ -0,0 +1,158 @@ +/******************************************************************************* + * 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.ui.viewmodel.properties; + +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +import org.eclipse.core.runtime.ListenerList; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; + +/** + * Class used by the PropertyBasedLabelProvider to generate store + * label attributes related to a single column. Each column info is + * configured with an array of attributes (there are currently four + * types of attributes: text, image, font, and color), which are + * evaluated in order to generate the label. + * <p/> + * Clients are not intended to extend this class. + * + * @see PropertyBasedLabelProvider + */ +@SuppressWarnings("restriction") +@ThreadSafe +public class LabelColumnInfo implements ILabelAttributeChangedListener { + + private static final LabelAttribute[] EMPTY_ATTRIBUTES_ARRAY = new LabelAttribute[0]; + + /** + * Calculated list of property names that need to be retrieved to + * generate the label for this column. + */ + private String[] fPropertyNames; + + /** + * Array of label attribute objects. + */ + private LabelAttribute[] fLabelAttributes; + + /** + * Listeners for when column attributes are modified. + */ + private ListenerList fListeners = new ListenerList(); + + /** + * Creates the column info object with given array of attributes. + * @param attributeInfos Attributes for the label. + */ + public LabelColumnInfo(LabelAttribute[] attributes) + { + fLabelAttributes = attributes; + + List<String> names = new LinkedList<String>(); + for (LabelAttribute attr : attributes) { + attr.addChangedListener(this); + for (String name : attr.getPropertyNames()) { + names.add(name); + } + } + + fPropertyNames = names.toArray(new String[names.size()]); + } + + /** + * Disposes this column info object and the attribute objects + * within it. + */ + public void dispose() { + for (LabelAttribute attr : fLabelAttributes) { + attr.dispose(); + attr.removeChangedListener(this); + } + fLabelAttributes = EMPTY_ATTRIBUTES_ARRAY; + fPropertyNames = null; + } + + /** + * Returns the property names that need to be retrieved in order + * to generate the label for this column. + */ + public String[] getPropertyNames() { return fPropertyNames; } + + /** + * Returns the list of configured label attributes for this column. + */ + public LabelAttribute[] getLabelAttributes() { return fLabelAttributes; } + + /** + * Registers the given listener for changes in the attributes of this + * column. A change in the attributes of a label should cause + * a view to repaint. + * @param listener Listener to register. + */ + public void addChangedListener(ILabelAttributeChangedListener listener) { + fListeners.add(listener); + } + + /** + * Unregisters the given listener. + * @param listener Listener to unregister. + */ + public void removeChangedListener(ILabelAttributeChangedListener listener) { + fListeners.remove(listener); + } + + /** + * Listener method called by the attribute objects. + * @see ILabelAttributeChangedListener + */ + public void attributesChanged() { + Object[] listeners = fListeners.getListeners(); + for (Object listener : listeners) { + ((ILabelAttributeChangedListener)listener).attributesChanged(); + } + } + + /** + * Updates the label parameters for this column based on the provided + * properties. The label information is written to the givne label + * update under the given column index. + * @param update Update to write to. + * @param columnIndex Column to write label information under. + * @param properties Map of properties to use to generate the label. + */ + public void updateColumn(ILabelUpdate update, int columnIndex, Map<String,Object> properties) { + boolean textSet = false; + boolean imageSet = false; + boolean fontSet = false; + boolean colorSet = false; + + LabelAttribute[] labelAttributes = getLabelAttributes(); + for (LabelAttribute info : labelAttributes) { + + if (!(info instanceof LabelText && textSet) && + !(info instanceof LabelImage && imageSet) && + !(info instanceof LabelFont && fontSet) && + !(info instanceof LabelColor && colorSet) && + info.isEnabled(properties)) + { + info.updateAttribute(update, columnIndex, properties); + + textSet = textSet || info instanceof LabelText; + imageSet = imageSet || info instanceof LabelImage; + fontSet = fontSet || info instanceof LabelFont; + colorSet = colorSet || info instanceof LabelColor; + } + } + } +}
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/LabelFont.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/LabelFont.java new file mode 100644 index 00000000000..e4eef88041e --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/LabelFont.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * 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.ui.viewmodel.properties; + +import java.util.Map; + +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.swt.graphics.FontData; + +/** + * The font attribute of a label. + * + * @see LabelAttribute + * @see LabelColumnInfo + * @see PropertyBasedLabelProvider + */ +@SuppressWarnings("restriction") +public class LabelFont extends LabelAttribute { + private static final FontData DEFAULT_FONT = JFaceResources.getDefaultFontDescriptor().getFontData()[0]; + + /** + * The font data of this attribute. + */ + private FontData fFontData; + + public LabelFont() { + this(DEFAULT_FONT); + } + + public LabelFont(FontData fontData) { + fFontData = fontData; + } + + public FontData getFontData() { + return fFontData; + } + + public void setFontData(FontData fontData) { + fFontData = fontData; + fireAttributeChanged(); + } + + @Override + public void updateAttribute(ILabelUpdate update, int columnIndex, Map<String, Object> properties) { + update.setFontData(getFontData(), columnIndex); + } + +}
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/LabelImage.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/LabelImage.java new file mode 100644 index 00000000000..797031d14e0 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/LabelImage.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * 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.ui.viewmodel.properties; + +import java.util.Map; + +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.jface.resource.ImageDescriptor; + +/** + * The image attribute of a label. + * + * @see LabelAttribute + * @see LabelColumnInfo + * @see PropertyBasedLabelProvider + */ +@SuppressWarnings("restriction") +public class LabelImage extends LabelAttribute { + private ImageDescriptor fImageDescriptor; + + public LabelImage() { + this(null); + } + + public LabelImage(ImageDescriptor image) { + fImageDescriptor = image; + } + + public ImageDescriptor getImageDescriptor() { + return fImageDescriptor; + } + + public void setImageDescriptor(ImageDescriptor image) { + fImageDescriptor = image; + fireAttributeChanged(); + } + + @Override + public void updateAttribute(ILabelUpdate update, int columnIndex, Map<String, Object> properties) { + ImageDescriptor descriptor = getImageDescriptor(); + if (descriptor != null) { + update.setImageDescriptor(descriptor, columnIndex); + } + } +}
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/LabelText.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/LabelText.java new file mode 100644 index 00000000000..534000f21bc --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/LabelText.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * 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.ui.viewmodel.properties; + +import java.text.MessageFormat; +import java.util.Map; + +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; + +/** + * The text attribute of a label. It uses a message format string in order to + * compose the text string. The parameter names determine the array of objects + * given to the message format. + * + * @see MessageFormat#format(Object[], StringBuffer, java.text.FieldPosition) + * @see LabelAttribute + * @see LabelColumnInfo + * @see PropertyBasedLabelProvider + */ +@SuppressWarnings("restriction") +public class LabelText extends LabelAttribute { + + public static final MessageFormat DEFAULT_MESSAGE = new MessageFormat(MessagesForProperties.DefaultLabelMessage_label); + + /** + * Message format used to generate the label text. + * + */ + private MessageFormat fMessageFormat; + + /** + * The property names needed for the message format. The property values + * corresponding to these names are given the the {@link MessageFormat#format(Object[], StringBuffer, java.text.FieldPosition)} + * method. + */ + private String[] fPropertyNames; + + public LabelText() { + this(DEFAULT_MESSAGE, EMPTY_PROPERTY_NAMES_ARRAY); + } + + public LabelText(MessageFormat format, String[] propertyNames) { + fMessageFormat = format; + fPropertyNames = propertyNames; + } + + @Override + public String[] getPropertyNames() { + return fPropertyNames; + } + + public MessageFormat getMessageFormat() { + return fMessageFormat; + } + + public void setMessageFormat(MessageFormat messageFormat) { + fMessageFormat = messageFormat; + fireAttributeChanged(); + } + + @Override + public void updateAttribute(ILabelUpdate update, int columnIndex, Map<String, Object> properties) { + String[] propertyNames = getPropertyNames(); + Object[] propertyValues = new Object[propertyNames.length]; + for (int i = 0; i < propertyNames.length; i++) { + propertyValues[i] = properties.get(propertyNames[i]); + } + + try { + update.setLabel(getMessageFormat().format(propertyValues, new StringBuffer(), null).toString(), columnIndex); + } catch (IllegalArgumentException e) { + update.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, 0, "Failed formatting a message for column " + columnIndex + ", for update " + update, e)); //$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/ui/viewmodel/properties/MessagesForProperties.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/MessagesForProperties.java new file mode 100644 index 00000000000..35e5688db8c --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/MessagesForProperties.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * 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.ui.viewmodel.properties; + +import org.eclipse.osgi.util.NLS; + +class MessagesForProperties extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.cdt.dsf.ui.viewmodel.properties.messages"; //$NON-NLS-1$ + + public static String DefaultLabelMessage_label; + + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, MessagesForProperties.class); + } + + private MessagesForProperties() { + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/PropertyBasedLabelProvider.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/PropertyBasedLabelProvider.java new file mode 100644 index 00000000000..8865d9bedcd --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/PropertyBasedLabelProvider.java @@ -0,0 +1,268 @@ +/******************************************************************************* + * 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.ui.viewmodel.properties; + +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +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.ThreadSafe; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.cdt.dsf.ui.concurrent.ViewerDataRequestMonitor; +import org.eclipse.cdt.dsf.ui.viewmodel.VMViewerUpdate; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.ListenerList; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; + +/** + * A configurable label provider which uses element's property label provider + * to set element's label attributes. + * <p> + * When this provider is registered for an element it calculates the properties + * that need to be retrieved based on view's active columns, and then it calls the + * element's property provider to retrieve those properties. After the property + * values are retrieved, they are processed in order to produce correct label text, + * images, fonts, and colors, for the given element. + */ +@SuppressWarnings("restriction") +@ThreadSafe +public class PropertyBasedLabelProvider + implements IElementLabelProvider, ILabelAttributeChangedListener +{ + private static final String[] EMPTY_PROPERTY_NAMES_ARRAY = new String[0]; + + /** + * Properties update used as to collect property data from the provider. + */ + private class PropertiesUpdate extends VMViewerUpdate implements IPropertiesUpdate { + + private final String[] fProperties; + private final Map<String, Object> fValues; + + public PropertiesUpdate(String[] properties, ILabelUpdate labelUpdate, DataRequestMonitor<Map<String,Object>> rm) { + super(labelUpdate, rm); + fProperties = properties; + fValues = fProperties != null + ? new HashMap<String, Object>(properties.length * 4 / 3, 0.75f) + : new HashMap<String, Object>(); + } + + public String[] getProperties() { + return fProperties; + } + + public void setProperty(String property, Object value) { + fValues.put(property, value); + } + + /** + * Overrides the standard done in order to store the retrieved values + * in the client's request monitor. + */ + @Override + public void done() { + @SuppressWarnings("unchecked") + DataRequestMonitor<Map<String,Object>> rm = (DataRequestMonitor<Map<String,Object>>)getRequestMonitor(); + if (fProperties == null || fValues.size() >= fProperties.length) { + rm.setData(fValues); + } else { + rm.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Incomplete properties updated", null)); //$NON-NLS-1$ + } + super.done(); + } + } + + /** + * Attribute information for each column by column ID. + */ + private Map<String, LabelColumnInfo> fColumnInfos = Collections.synchronizedMap(new HashMap<String,LabelColumnInfo>()); + + private ListenerList fListeners = new ListenerList(); + + /** + * Standard constructor. A property based label constructor does not + * initialize column attribute information {@link #setColumnInfo(String, LabelColumnInfo)} + * must be called to configure each column. + */ + public PropertyBasedLabelProvider() { + } + + /** + * Disposes this label provider and its configured column info objects. + */ + public void dispose() { + LabelColumnInfo[] infos = null; + synchronized (fColumnInfos) { + infos = fColumnInfos.values().toArray(new LabelColumnInfo[fColumnInfos.size()]); + fColumnInfos.clear(); + } + for (LabelColumnInfo info : infos) { + info.dispose(); + } + } + + /** + * Sets the given column info object for the given column ID. This column + * info will be used to generate the label when the given column is visibile. + * @param columnId Column ID that the given column info is being registered for. + * @param info Column 'info' object containing column attributes. + * @return The previous column info object configured for this ID. + */ + public LabelColumnInfo setColumnInfo(String columnId, LabelColumnInfo info) { + LabelColumnInfo oldInfo = fColumnInfos.put(columnId, info); + info.addChangedListener(this); + if (oldInfo != null) { + info.removeChangedListener(this); + } + return oldInfo; + } + + /** + * Returns the given column info object for the given column ID. + * @param columnId Column ID to retrieve the column info for. + * @@return Column 'info' object containing column attributes. + */ + public LabelColumnInfo getColumnInfo(String column) { + return fColumnInfos.get(column); + } + + /** + * Registers the given listener for changes in the attributes of this + * label provider. A change in the attributes of a label should cause + * a view to repaint. + * @param listener Listener to register. + */ + public void addChangedListener(ILabelAttributeChangedListener listener) { + fListeners.add(listener); + } + + /** + * Unregisters the given listener. + * @param listener Listener to unregister. + */ + public void removeChangedListener(ILabelAttributeChangedListener listener) { + fListeners.remove(listener); + } + + /** + * Listener method called by label provider's column info objects. + * @see ILabelAttributeChangedListener + */ + public void attributesChanged() { + Object[] listeners = fListeners.getListeners(); + for (Object listener : listeners) { + ((ILabelAttributeChangedListener)listener).attributesChanged(); + } + } + + public void update(ILabelUpdate[] labelUpdates) { + IElementPropertiesProvider propertiesProvider = getElementPropertiesProvider(labelUpdates[0].getElement()); + if (propertiesProvider == null) { + for (ILabelUpdate update : labelUpdates) { + update.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, "Properties-based label provider " + this + " failed to generate a label, no properties provider registered for element: " + labelUpdates[0].getElement())); //$NON-NLS-1$ //$NON-NLS-2$ + update.done(); + } + return; + } + + String[] columnIds = labelUpdates[0].getColumnIds(); + String[] propertyNames = calcPropertyNamesForColumns(columnIds); + + // Call the properties provider. Create a request monitor for each label update. + // We can use an immediate executor for the request monitor because the label provider + // is thread safe. + IPropertiesUpdate[] propertiesUpdates = new IPropertiesUpdate[labelUpdates.length]; + for (int i = 0; i < labelUpdates.length; i++) { + final ILabelUpdate labelUpdate = labelUpdates[i]; + propertiesUpdates[i] = new PropertiesUpdate( + propertyNames, labelUpdates[i], + new ViewerDataRequestMonitor<Map<String, Object>>(ImmediateExecutor.getInstance(), labelUpdates[i]) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + updateLabel(labelUpdate, getData()); + } + labelUpdate.done(); + } + }); + } + propertiesProvider.update(propertiesUpdates); + } + + /** + * Calculates the names of properties that have to be retrieved from the property + * provider to generate the labels for given columns. + * @param columnIds Column IDs to check. + * @return Array of property names. + */ + private String[] calcPropertyNamesForColumns(String[] columnIds) { + if (columnIds == null) { + LabelColumnInfo columnInfo = getColumnInfo(null); + if (columnInfo != null) { + return columnInfo.getPropertyNames(); + } else { + return EMPTY_PROPERTY_NAMES_ARRAY; + } + } else { + List<String> properties = new LinkedList<String>(); + for (String columnId : columnIds) { + LabelColumnInfo info = getColumnInfo(columnId); + if (info != null) { + String[] infoPropertyNames = info.getPropertyNames(); + for (int i = 0; i < infoPropertyNames.length; i++) { + properties.add(infoPropertyNames[i]); + } + } + } + return properties.toArray(new String[properties.size()]); + } + } + + /** + * Updates the label information based on given map of properties. + * @param update Label update to write to. + * @param properties Properties retrieved from the element properties provider. + */ + protected void updateLabel(ILabelUpdate update, Map<String,Object> properties) { + if (update.getColumnIds() == null) { + LabelColumnInfo info = getColumnInfo(null); + if (info != null) { + info.updateColumn(update, 0, properties); + } + } else { + String[] columnIds = update.getColumnIds(); + + for (int i = 0; i < columnIds.length; i++) { + LabelColumnInfo info = getColumnInfo(columnIds[i]); + if (info != null) { + info.updateColumn(update, i, properties); + } + } + } + + update.done(); + } + + private IElementPropertiesProvider getElementPropertiesProvider(Object element) { + if (element instanceof IAdaptable) { + return (IElementPropertiesProvider)((IAdaptable)element).getAdapter(IElementPropertiesProvider.class); + } + return null; + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/messages.properties b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/messages.properties new file mode 100644 index 00000000000..6e78cb987b1 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/messages.properties @@ -0,0 +1,11 @@ +############################################################################### +# 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 +############################################################################### +DefaultLabelMessage_label=<unknown> diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/AbstractCachingVMProvider.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/AbstractCachingVMProvider.java new file mode 100644 index 00000000000..632a1dc2607 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/AbstractCachingVMProvider.java @@ -0,0 +1,1090 @@ +/******************************************************************************* + * 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.ui.viewmodel.update; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executor; +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.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMData; +import org.eclipse.cdt.dsf.datamodel.IDMService; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.cdt.dsf.ui.concurrent.SimpleDisplayExecutor; +import org.eclipse.cdt.dsf.ui.concurrent.ViewerCountingRequestMonitor; +import org.eclipse.cdt.dsf.ui.concurrent.ViewerDataRequestMonitor; +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMAdapter; +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMModelProxy; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMModelProxyExtension; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.VMChildrenCountUpdate; +import org.eclipse.cdt.dsf.ui.viewmodel.VMChildrenUpdate; +import org.eclipse.cdt.dsf.ui.viewmodel.VMHasChildrenUpdate; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.IElementPropertiesProvider; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +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.IModelProxy; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; +import org.eclipse.jface.viewers.TreePath; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.TreeItem; + +/** + * Base implementation of a caching view model provider. + */ +@SuppressWarnings("restriction") +public class AbstractCachingVMProvider extends AbstractVMProvider implements ICachingVMProvider, ICachingVMProviderExtension { + + private boolean fDelayEventHandleForViewUpdate = false; + + // debug flag + static boolean DEBUG_CACHE = false; + + static { + DEBUG_CACHE = DsfUIPlugin.DEBUG && "true".equals( //$NON-NLS-1$ + Platform.getDebugOption("org.eclipse.cdt.dsf.ui/debug/vm/cache")); //$NON-NLS-1$ + } + + private static final int MAX_CACHE_SIZE = 1000; + + /** + * Class representing a key to an element's data in the cache. The main + * components of this key are the viewer input and the path, they uniquely + * identify an element. The root element is used to track when a given + * root element is no longer in the cache and can therefore be disposed. + * The node is needed because different nodes have different lists of + * children for the same parent element. + */ + private static class ElementDataKey { + ElementDataKey(Object rootElement, IVMNode node, Object viewerInput, TreePath path) { + fRootElement = rootElement; + fNode = node; + fViewerInput = viewerInput; + fPath = path; + } + + final Object fRootElement; + final IVMNode fNode; + final Object fViewerInput; + final TreePath fPath; + + @Override + public String toString() { + return fNode.toString() + " " + //$NON-NLS-1$ + (fPath.getSegmentCount() == 0 ? fViewerInput.toString() : fPath.getLastSegment().toString()); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ElementDataKey)) return false; + ElementDataKey key = (ElementDataKey)obj; + return + (fNode == null && key.fNode == null || (fNode != null && fNode.equals(key.fNode))) && + (fRootElement == null && key.fRootElement == null || (fRootElement != null && fRootElement.equals(key.fRootElement))) && + (fViewerInput == null && key.fViewerInput == null || (fViewerInput != null && fViewerInput.equals(key.fViewerInput))) && + (fPath == null && key.fPath == null || (fPath != null && fPath.equals(key.fPath))); + } + + @Override + public int hashCode() { + return + (fRootElement != null ? fRootElement.hashCode() : 0) + + (fNode != null ? fNode.hashCode() : 0) + + (fViewerInput != null ? fViewerInput.hashCode() : 0) + + (fPath != null ? fPath.hashCode() : 0); + } + } + + /** + * A base class for the entry in the cache. Since the cache maintains + * a double-linked list through all the entries, the linked list references + * are maintained in this class. + */ + private static class Entry { + final Object fKey; + + Entry fNext; + Entry fPrevious; + + Entry(Object key) { + fKey = key; + } + + void insert(Entry nextEntry) { + fNext = nextEntry; + fPrevious = nextEntry.fPrevious; + fPrevious.fNext = this; + fNext.fPrevious = this; + } + + void remove() { + fPrevious.fNext = fNext; + fNext.fPrevious = fPrevious; + } + + void reinsert(Entry nextEntry) { + fPrevious.fNext = fNext; + fNext.fPrevious = fPrevious; + + fNext = nextEntry; + fPrevious = nextEntry.fPrevious; + fPrevious.fNext = this; + fNext.fPrevious = this; + } + } + + /** + * Entry with cached element data. + */ + static class ElementDataEntry extends Entry { + ElementDataEntry(ElementDataKey key) { + super(key); + } + + /** + * Counter of flush operations performed on this entry. It is used + * by caching update operations to make sure that an update which + * was issued for a given entry is still valid for that entry when + * it is completed by the node. + */ + int fFlushCounter = 0; + + /** + * Indicates that the data in this cache entry is out of date with + * the data on the target. + */ + Boolean fDirty = false; + + /** + * Cached {@link IHasChildrenUpdate} result. + */ + Boolean fHasChildren = null; + + /** + * Cached {@link IChildrenCountUpdate} result. + */ + Integer fChildrenCount = null; + + /** + * Flag indicating that all the children of the given element are + * alredy cached. + */ + boolean fAllChildrenKnown = false; + + /** + * Map containing children of this element, keyed by child index. + */ + Map<Integer,Object> fChildren = null; + + /** + * Map of IDMData objects, keyed by the DM context. + */ + Map<IDMContext,Object> fDataOrStatus = new HashMap<IDMContext,Object>(1); + + /** + * Previous known value of the DM data objects. + */ + Map<IDMContext,IDMData> fArchiveData = new HashMap<IDMContext,IDMData>(1);; + + void ensureChildrenMap() { + if (fChildren == null) { + Integer childrenCount = fChildrenCount; + childrenCount = childrenCount != null ? childrenCount : 0; + int capacity = Math.max((childrenCount.intValue() * 4)/3, 32); + fChildren = new HashMap<Integer,Object>(capacity); + } + } + + @Override + public String toString() { + return fKey.toString() + " = " + //$NON-NLS-1$ + "[hasChildren=" + fHasChildren + ", " +//$NON-NLS-1$ //$NON-NLS-2$ + "childrenCount=" + fChildrenCount + //$NON-NLS-1$ + ", children=" + fChildren + //$NON-NLS-1$ + ", data/status=" + fDataOrStatus + //$NON-NLS-1$ + ", oldData=" + fArchiveData + "]"; //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + /** + * A key for a special marker entry in the cache. This marker entry is used + * to optimize repeated flushing of the cache. + * @see AbstractCachingVMProvider#flush(List) + */ + private static class FlushMarkerKey { + private Object fRootElement; + private IElementUpdateTester fElementTester; + + FlushMarkerKey(Object rootElement, IElementUpdateTester pathTester) { + fRootElement = rootElement; + fElementTester = pathTester; + } + + boolean includes(FlushMarkerKey key) { + return fRootElement.equals(key.fRootElement) && + fElementTester.includes(key.fElementTester); + } + + int getUpdateFlags(ElementDataKey key) { + if (fRootElement.equals(key.fRootElement)) { + return fElementTester.getUpdateFlags(key.fViewerInput, key.fPath); + } + return 0; + } + + @Override + public String toString() { + return fElementTester.toString() + " " + fRootElement.toString(); //$NON-NLS-1$ + } + + } + + /** + * Marker used to keep track of whether any entries with the given + * root element are present in the cache. + */ + private static class RootElementMarkerKey { + + private Object fRootElement; + + RootElementMarkerKey(Object rootElement) { + fRootElement = rootElement; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof RootElementMarkerKey && ((RootElementMarkerKey)obj).fRootElement.equals(fRootElement); + } + + @Override + public int hashCode() { + return fRootElement.hashCode(); + } + + @Override + public String toString() { + return fRootElement.toString(); + } + } + + class RootElementMarkerEntry extends Entry { + RootElementMarkerEntry(RootElementMarkerKey key) { + super(key); + } + + @Override + void remove() { + super.remove(); + rootElementRemovedFromCache(((RootElementMarkerKey)fKey).fRootElement); + } + + @Override + public String toString() { + return "ROOT MARKER " + fKey; //$NON-NLS-1$ + } + } + + protected static String SELECTED_UPDATE_MODE = "org.eclipse.cdt.dsf.ui.viewmodel.update.selectedUpdateMode"; //$NON-NLS-1$ + /** + * @since 1.1 + */ + protected static String SELECTED_UPDATE_SCOPE = "org.eclipse.cdt.dsf.ui.viewmodel.update.selectedUpdateScope"; //$NON-NLS-1$ + + private IVMUpdatePolicy[] fAvailableUpdatePolicies; + private IVMUpdateScope[] fAvailableUpdateScopes; + + public Map<Object, RootElementMarkerKey> fRootMarkers = new HashMap<Object, RootElementMarkerKey>(); + + /** + * Hash map holding cache data. To store the cache information, the cache uses a + * combination of this hash map and a double-linked list running through all + * the entries in the cache. The linked list is used to organize the cache entries + * in least recently used (LRU) order. This ordering is then used to delete least + * recently used entries in the cache and keep the cache from growing indefinitely. + * Also, the ordering is used to optimize the flushing of the cache data (see + * {@link FlushMarkerKey} for more details). + */ + private final Map<Object, Entry> fCacheData = Collections.synchronizedMap(new HashMap<Object, Entry>(200, 0.75f)); + + /** + * Pointer to the first cache entry in the double-linked list of cache entries. + */ + private final Entry fCacheListHead; + + + public AbstractCachingVMProvider(AbstractVMAdapter adapter, IPresentationContext presentationContext) { + super(adapter, presentationContext); + + fCacheListHead = new Entry(null) { + @Override + public String toString() { + return "HEAD"; //$NON-NLS-1$ + } + }; + fCacheListHead.fNext = fCacheListHead; + fCacheListHead.fPrevious = fCacheListHead; + + fAvailableUpdatePolicies = createUpdateModes(); + + fAvailableUpdateScopes = createUpdateScopes(); + setActiveUpdateScope(new VisibleUpdateScope()); + } + + protected IVMUpdatePolicy[] createUpdateModes() { + return new IVMUpdatePolicy[] { new AutomaticUpdatePolicy() }; + } + + /** + * @since 1.1 + */ + protected IVMUpdateScope[] createUpdateScopes() { + return new IVMUpdateScope[] { new VisibleUpdateScope(), new AllUpdateScope() }; + } + + public IVMUpdatePolicy[] getAvailableUpdatePolicies() { + return fAvailableUpdatePolicies; + } + + public IVMUpdatePolicy getActiveUpdatePolicy() { + String updateModeId = (String)getPresentationContext().getProperty(SELECTED_UPDATE_MODE); + if (updateModeId != null) { + for (IVMUpdatePolicy updateMode : getAvailableUpdatePolicies()) { + if (updateMode.getID().equals(updateModeId)) { + return updateMode; + } + } + } + + // Default to the first one. + return getAvailableUpdatePolicies()[0]; + } + + public void setActiveUpdatePolicy(IVMUpdatePolicy updatePolicy) { + getPresentationContext().setProperty(SELECTED_UPDATE_MODE, updatePolicy.getID()); + } + + public void refresh() { + IElementUpdateTester elementTester = getActiveUpdatePolicy().getElementUpdateTester(ManualUpdatePolicy.REFRESH_EVENT); + + for (final IVMModelProxy proxyStrategy : getActiveModelProxies()) { + flush(new FlushMarkerKey(proxyStrategy.getRootElement(), elementTester)); + } + + for (final IVMModelProxy proxyStrategy : getActiveModelProxies()) { + if (!proxyStrategy.isDisposed()) { + proxyStrategy.fireModelChanged(new ModelDelta(proxyStrategy.getRootElement(), IModelDelta.CONTENT)); + } + } + } + + @Override + public void updateNode(final IVMNode node, IHasChildrenUpdate[] updates) { + LinkedList <IHasChildrenUpdate> missUpdates = new LinkedList<IHasChildrenUpdate>(); + for(final IHasChildrenUpdate update : updates) { + // Find or create the cache entry for the element of this update. + ElementDataKey key = makeEntryKey(node, update); + final ElementDataEntry entry = getElementDataEntry(key); + + if (entry.fHasChildren != null) { + // Cache Hit! Just return the value. + if (DEBUG_CACHE && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) { + DsfUIPlugin.debug("cacheHitHasChildren(node = " + node + ", update = " + update + ", " + entry.fHasChildren + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + } + update.setHasChilren(entry.fHasChildren.booleanValue()); + update.done(); + } else { + // Cache miss! Save the flush counter of the entry and create a proxy update. + final int flushCounter = entry.fFlushCounter; + missUpdates.add( + new VMHasChildrenUpdate( + update, + new ViewerDataRequestMonitor<Boolean>(getExecutor(), update) { + @Override + protected void handleCompleted() { + // Update completed. Write value to cache only if update successed + // and the cache entry wasn't flushed in the mean time. + if(isSuccess()) { + if (flushCounter == entry.fFlushCounter) { + if (DEBUG_CACHE && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) { + DsfUIPlugin.debug("cacheSavedHasChildren(node = " + node + ", update = " + update + ", " + getData() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + } + entry.fHasChildren = this.getData(); + } + update.setHasChilren(getData()); + } else { + update.setStatus(getStatus()); + } + update.done(); + } + })); + } + } + + // Issue all the update proxies with one call. + if (!missUpdates.isEmpty()) { + super.updateNode(node, missUpdates.toArray(new IHasChildrenUpdate[missUpdates.size()])); + } + } + + @Override + public void updateNode(final IVMNode node, final IChildrenCountUpdate update) { + // Find or create the cache entry for the element of this update. + ElementDataKey key = makeEntryKey(node, update); + final ElementDataEntry entry = getElementDataEntry(key); + + if(entry.fChildrenCount != null) { + // Cache Hit! Just return the value. + if (DEBUG_CACHE && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) { + DsfUIPlugin.debug("cacheHitChildrenCount(node = " + node + ", update = " + update + ", " + entry.fChildrenCount + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + } + update.setChildCount(entry.fChildrenCount.intValue()); + update.done(); + } else { + // Cache miss! Save the flush counter of the entry and create a proxy update. + final int flushCounter = entry.fFlushCounter; + IChildrenCountUpdate updateProxy = new VMChildrenCountUpdate( + update, + new ViewerDataRequestMonitor<Integer>(getExecutor(), update) { + @Override + protected void handleCompleted() { + // Update completed. Write value to cache only if update successed + // and the cache entry wasn't flushed in the mean time. + if(isSuccess()) { + if (flushCounter == entry.fFlushCounter) { + if (DEBUG_CACHE && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) { + DsfUIPlugin.debug("cacheSavedChildrenCount(node = " + node + ", update = " + update + ", " + getData() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + } + entry.fChildrenCount = this.getData(); + } + update.setChildCount(getData()); + } else { + update.setStatus(getStatus()); + } + update.done(); + } + }); + super.updateNode(node, updateProxy); + } + } + + @Override + public void updateNode(final IVMNode node, final IChildrenUpdate update) { + // Find or create the cache entry for the element of this update. + ElementDataKey key = makeEntryKey(node, update); + final ElementDataEntry entry = getElementDataEntry(key); + + final int flushCounter = entry.fFlushCounter; + if (entry.fChildren == null || (update.getOffset() < 0 && !entry.fAllChildrenKnown)) { + // Need to retrieve all the children if there is no children information yet. + // Or if the client requested all children (offset = -1, length -1) and all + // the children are not yet known. + IChildrenUpdate updateProxy = new VMChildrenUpdate( + update, update.getOffset(), update.getLength(), + new ViewerDataRequestMonitor<List<Object>>(getExecutor(), update){ + @Override + protected void handleSuccess() { + // Check if the update retrieved all children by specifying "offset = -1, length = -1" + int updateOffset = update.getOffset(); + if (updateOffset < 0) + { + updateOffset = 0; + if (entry.fFlushCounter == flushCounter) { + entry.fAllChildrenKnown = true; + } + } + + if (DEBUG_CACHE && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) { + DsfUIPlugin.debug("cacheSavedChildren(node = " + node + ", update = " + update + ", children = {" + updateOffset + "->" + (updateOffset + getData().size()) + "})"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + } + + if (flushCounter == entry.fFlushCounter) { + entry.ensureChildrenMap(); + } + + // Set the children to map and update. + for(int j = 0; j < getData().size(); j++) { + int offset = updateOffset + j; + Object child = getData().get(j); + if (child != null) { + if (flushCounter == entry.fFlushCounter) { + entry.fChildren.put(offset, child); + } + update.setChild(child, offset); + } + } + update.done(); + } + + @Override + protected void handleCancel() { + if (DEBUG_CACHE && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) { + DsfUIPlugin.debug("cacheCanceledChildren(node = " + node + ", update = " + update + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + super.handleCancel(); + } + }); + super.updateNode(node, updateProxy); + } else if (update.getOffset() < 0 ) { + // The update requested all children. Fill in all children assuming that + // the children array is complete. + + if (DEBUG_CACHE && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) { + DsfUIPlugin.debug("cacheHitChildren(node = " + node + ", update = " + update + ", children = " + entry.fChildren.keySet() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + } + + // The following assert should never fail given the first if statement. + assert entry.fAllChildrenKnown; + + // we have all of the children in cache; return from cache + for(int position = 0; position < entry.fChildren.size(); position++) { + update.setChild(entry.fChildren.get(position), position); + } + update.done(); + } else { + // Update for a partial list of children was requested. + // Iterate through the known children and make a list of missing + // indexes. + List<Integer> childrenMissingFromCache = new LinkedList<Integer>(); + for (int i = update.getOffset(); i < update.getOffset() + update.getLength(); i++) { + childrenMissingFromCache.add(i); + } + + // Write known children from cache into the update. + for(Integer position = update.getOffset(); position < update.getOffset() + update.getLength(); position++) { + Object child = entry.fChildren.get(position); + if (child != null) { + update.setChild(entry.fChildren.get(position), position); + childrenMissingFromCache.remove(position); + } + } + + if (DEBUG_CACHE && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) { + DsfUIPlugin.debug("cachePartialHitChildren(node = " + node + ", update = " + update + ", missing = " + childrenMissingFromCache + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + } + + if (childrenMissingFromCache.size() > 0) { + // Some children were not found in the cache, create separate + // proxy updates for the continuous ranges of missing children. + List<IChildrenUpdate> partialUpdates = new ArrayList<IChildrenUpdate>(2); + final CountingRequestMonitor multiRm = new ViewerCountingRequestMonitor(getExecutor(), update); + while(childrenMissingFromCache.size() > 0) + { + final int offset = childrenMissingFromCache.get(0); + childrenMissingFromCache.remove(0); + int length = 1; + while(childrenMissingFromCache.size() > 0 && childrenMissingFromCache.get(0) == offset + length) + { + length++; + childrenMissingFromCache.remove(0); + } + + partialUpdates.add(new VMChildrenUpdate( + update, offset, length, + new DataRequestMonitor<List<Object>>(getExecutor(), multiRm) { + @Override + protected void handleSuccess() { + // Only save the children to the cahce if the entry wasn't flushed. + if (flushCounter == entry.fFlushCounter) { + if (DEBUG_CACHE && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) { + DsfUIPlugin.debug("cachePartialSaveChildren(node = " + node + ", update = " + update + ", saved = {" + offset + "->" + (offset + getData().size()) + "})"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + } + entry.ensureChildrenMap(); + } + + for (int i = 0; i < getData().size(); i++) { + if (getData().get(i) != null) { + update.setChild(getData().get(i), offset + i); + if (flushCounter == entry.fFlushCounter) { + // Only save the children to the cahce if the entry wasn't flushed. + entry.fChildren.put(offset + i, getData().get(i)); + } + } + } + multiRm.done(); + } + })); + } + + for (IChildrenUpdate partialUpdate : partialUpdates) { + super.updateNode(node, partialUpdate); + } + multiRm.setDoneCount(partialUpdates.size()); + } else { + // All children were found in cache. Compelte the update. + update.done(); + } + } + + } + + /** + * Flushes the cache with given DMC as the root element. + * @param dmcToFlush DM Context which is the root of the flush operation. Entries + * for all DMCs that have this DMC as their ancestor will be flushed. If this + * parameter is null, then all entries are flushed. + * @param archive + */ + private void flush(FlushMarkerKey flushKey) { + if (DEBUG_CACHE && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) { + DsfUIPlugin.debug("cacheFlushing(" + flushKey + ")"); //$NON-NLS-1$ //$NON-NLS-2$ + } + // For each entry that has the given context as a parent, perform the flush. + // Iterate through the cache entries backwards. This means that we will be + // iterating in order of most-recently-used to least-recently-used. + Entry entry = fCacheListHead.fPrevious; + while (entry != fCacheListHead) { + if (entry.fKey instanceof FlushMarkerKey) { + FlushMarkerKey entryFlushKey = (FlushMarkerKey)entry.fKey; + // If the context currently being flushed includes the flush + // context in current entry, remove the current entry since it will + // be replaced with one at the end of the list. + // Use special handling for null contexts, which we treat like it's an + // ancestor of all other contexts. + if (flushKey.includes(entryFlushKey)) { + fCacheData.remove(entryFlushKey); + entry.remove(); + } + + // If the flush context in current entry includes the current context + // being flushed, we can stop iterating through the cache entries + // now. + if (entryFlushKey.includes(flushKey)) { + break; + + } + } + else if (entry instanceof ElementDataEntry) { + ElementDataEntry elementDataEntry = (ElementDataEntry)entry; + int updateFlags = flushKey.getUpdateFlags((ElementDataKey)elementDataEntry.fKey); + if ((updateFlags & IVMUpdatePolicy.FLUSH) != 0) { + if ((updateFlags & IVMUpdatePolicy.ARCHIVE) == IVMUpdatePolicy.ARCHIVE) { + // We are saving current data for change history, check if the data is valid. + // If it valid, save it for archive, if it's not valid old archive data will be used + // if there is any. And if there is no old archive data, just remove the cache entry. + for (Iterator<Map.Entry<IDMContext, Object>> itr = elementDataEntry.fDataOrStatus.entrySet().iterator(); + itr.hasNext();) + { + Map.Entry<IDMContext, Object> dataOrStatusEntry = itr.next(); + if (dataOrStatusEntry.getValue() instanceof IDMData) { + elementDataEntry.fArchiveData.put(dataOrStatusEntry.getKey(), (IDMData)dataOrStatusEntry.getValue()); + } + } + elementDataEntry.fDataOrStatus.clear(); + if (elementDataEntry.fArchiveData.isEmpty()) { + fCacheData.remove(entry.fKey); + entry.remove(); + } + } else { + // We are not changing the archived data. If archive data exists in the entry, leave it. + // Otherwise remove the whole entry. + if (!elementDataEntry.fArchiveData.isEmpty()) { + elementDataEntry.fDataOrStatus.clear(); + } else { + fCacheData.remove(entry.fKey); + entry.remove(); + } + } + elementDataEntry.fFlushCounter++; + elementDataEntry.fHasChildren = null; + elementDataEntry.fChildrenCount = null; + elementDataEntry.fChildren = null; + elementDataEntry.fAllChildrenKnown = false; + elementDataEntry.fDirty = false; + } else if ((updateFlags & IVMUpdatePolicy.DIRTY) != 0) { + elementDataEntry.fDirty = true; + } + } + entry = entry.fPrevious; + } + + // Insert a marker for this flush operation. + Entry flushMarkerEntry = new Entry(flushKey); + fCacheData.put(flushKey, flushMarkerEntry); + flushMarkerEntry.insert(fCacheListHead); + } + + @Override + protected void handleEvent(final IVMModelProxy proxyStrategy, final Object event, final RequestMonitor rm) { + IElementUpdateTester elementTester = getActiveUpdatePolicy().getElementUpdateTester(event); + + flush(new FlushMarkerKey(proxyStrategy.getRootElement(), elementTester)); + + if (proxyStrategy instanceof IVMModelProxyExtension) { + IVMModelProxyExtension proxyStrategyExtension = (IVMModelProxyExtension)proxyStrategy; + + CountingRequestMonitor multiRm = new CountingRequestMonitor(getExecutor(), rm); + super.handleEvent(proxyStrategy, event, multiRm); + int rmCount = 1; + + if(fDelayEventHandleForViewUpdate) { + if(this.getActiveUpdateScope().getID().equals(AllUpdateScope.ALL_UPDATE_SCOPE_ID)) { + new MultiLevelUpdateHandler(getExecutor(), proxyStrategyExtension, getPresentationContext(), this, multiRm). + startUpdate(); + rmCount++; + } else if (!proxyStrategy.isDisposed()) { + // block updating only the viewport + TreeViewer viewer = (TreeViewer) proxyStrategyExtension.getViewer(); + Tree tree = viewer.getTree(); + int count = tree.getSize().y / tree.getItemHeight(); + + TreeItem topItem = tree.getTopItem(); + int index = computeTreeIndex(topItem); + + MultiLevelUpdateHandler handler = new MultiLevelUpdateHandler( + getExecutor(), proxyStrategyExtension, getPresentationContext(), this, multiRm); + handler.setRange(index, index + count); + handler.startUpdate(); + rmCount++; + } + } else { + if(this.getActiveUpdateScope().getID().equals(AllUpdateScope.ALL_UPDATE_SCOPE_ID)) { + MultiLevelUpdateHandler handler = new MultiLevelUpdateHandler( + getExecutor(), proxyStrategyExtension, getPresentationContext(), this, multiRm); + handler.startUpdate(); + rmCount++; + } + } + multiRm.setDoneCount(rmCount); + } else { + super.handleEvent(proxyStrategy, event, rm); + } + } + + private static int computeTreeIndex(TreeItem child) { + if (child != null) { + if(child.getParentItem() != null) { + int previous = 0; + int index = child.getParentItem().indexOf(child); + while (--index >= 0) { + previous += computeTreeExtent(child.getParentItem().getItem(index)); + } + return computeTreeIndex(child.getParentItem()) + previous; + } else { + int previous = 0; + int index = child.getParent().indexOf(child); + while (--index >= 0) { + previous += computeTreeExtent(child.getParent().getItem(index)); + } + return previous; + } + } + return 0; + } + + private static int computeTreeExtent(TreeItem item) { + int extent = 1; + if (item.getExpanded()) { + for (TreeItem i : item.getItems()) { + extent += computeTreeExtent(i); + } + } + return extent; + } + + /** + * Override default implementation to avoid automatically removing disposed proxies from + * list of active proxies. The caching provider only removes a proxy after its root element + * is no longer in the cache. + */ + @Override + public IModelProxy createModelProxy(Object element, IPresentationContext context) { + // Iterate through the current active proxies to try to find a proxy with the same + // element and re-use it if found. At the same time purge proxies that are no longer + IVMModelProxy proxy = null; + for (Iterator<IVMModelProxy> itr = getActiveModelProxies().iterator(); itr.hasNext();) { + IVMModelProxy next = itr.next(); + if (next != null && next.getRootElement().equals(element)) { + proxy = next; + break; + } + } + if (proxy == null) { + proxy = createModelProxyStrategy(element); + getActiveModelProxies().add(proxy); + } else if (proxy.isDisposed()) { + // DSF is capable of re-using old proxies which were previously + // disposed. However, the viewer which installs a proxy using + // a background job to install the proxy calls + // IModelProxy.isDisposed(), to check whether the proxy was disposed + // before it could be installed. We need to clear the disposed flag + // of the re-used proxy here, otherwise the proxy will never get used. + // Calling init here will cause the init() method to be called twice + // so the IVMModelProxy needs to be prepared for that. + // See bug 241024. + proxy.init(context); + } + return proxy; + } + + /** + * Called when a given all cache entries for the given root element have + * been removed from the cache. In order to property track changed elements, + * the caching VM provider does not immediately remove entries for a given root + * element, when the viewer root element changes. Instead it keeps this root + * element and keeps processing deltas for that root element until the + * cache entries for this element are gone. + */ + protected void rootElementRemovedFromCache(Object rootElement) { + fRootMarkers.remove(rootElement); + + for (Iterator<IVMModelProxy> proxiesItr = getActiveModelProxies().iterator(); proxiesItr.hasNext();) { + IVMModelProxy proxy = proxiesItr.next(); + if (proxy.isDisposed() && proxy.getRootElement().equals(rootElement) ) { + proxiesItr.remove(); + } + } + } + + /** + * Convenience class that searches for the root element for the given + * update and creates an element cache entry key. + */ + private ElementDataKey makeEntryKey(IVMNode node, IViewerUpdate update) { + Object rootElement = update.getViewerInput(); // Default + outer: for (IVMModelProxy proxy : getActiveModelProxies()) { + Object proxyRoot = proxy.getRootElement(); + if (proxyRoot.equals(update.getViewerInput())) { + rootElement = proxyRoot; + break; + } + TreePath path = update.getElementPath(); + for (int i = 0; i < path.getSegmentCount(); i++) { + if (proxyRoot.equals(path.getSegment(i))) { + rootElement = proxyRoot; + break outer; + } + } + } + + return new ElementDataKey(rootElement, node, update.getViewerInput(), update.getElementPath()); + } + + /** + * This is the only method that should be used to access a cache entry. + * It creates a new entry if needed and it maintains the ordering in + * the least-recently-used linked list. + */ + private ElementDataEntry getElementDataEntry(ElementDataKey key) { + assert key != null; + ElementDataEntry entry = (ElementDataEntry)fCacheData.get(key); + if (entry == null) { + // Create a new entry and add it to the end of the list. + entry = new ElementDataEntry(key); + addEntry(key, entry); + } else { + // Entry exists, move it to the end of the list. + entry.reinsert(fCacheListHead); + } + + // Update the root element marker: + // - ensure that the root marker is root markers' map, + // - ensure that the root marker is in the cache map, + // - and ensure that it's at the end of the cache. + RootElementMarkerKey rootMarker = fRootMarkers.get(key.fRootElement); + if (rootMarker == null) { + rootMarker = new RootElementMarkerKey(key.fRootElement); + fRootMarkers.put(key.fRootElement, rootMarker); + } + Entry rootMarkerEntry = fCacheData.get(rootMarker); + if (rootMarkerEntry == null) { + rootMarkerEntry = new RootElementMarkerEntry(rootMarker); + addEntry(rootMarker, rootMarkerEntry); + } else if (rootMarkerEntry.fNext != fCacheListHead) { + rootMarkerEntry.reinsert(fCacheListHead); + } + + return entry; + } + + /** + * Convenience method used by {@link #getElementDataEntry(ElementDataKey)} + */ + private void addEntry(Object key, Entry entry) { + fCacheData.put(key, entry); + entry.insert(fCacheListHead); + // If we are at capacity in the cache, remove the entry from head. + if (fCacheData.size() > MAX_CACHE_SIZE) { + fCacheData.remove(fCacheListHead.fNext.fKey); + fCacheListHead.fNext.remove(); + } + } + + /** + * Retrieves the deprecated IDMData object for the given IDMContext. This + * method should be removed once the use of IDMData is replaced with + * {@link IElementPropertiesProvider}. + */ + @Deprecated + public void getModelData(final IVMNode node, final IViewerUpdate update, final IDMService service, final IDMContext dmc, + final DataRequestMonitor rm, final Executor executor) + { + // Determine if this request is being issues on the a VM executor thread. If so + // then we do not need to create a new one to insure data integrity. + Executor vmExecutor = getExecutor(); + if ( vmExecutor instanceof SimpleDisplayExecutor && + Display.getDefault().getThread() == Thread.currentThread() ) + { + getCacheModelData(node, update, service, dmc, rm, executor ); + } else { + vmExecutor.execute(new DsfRunnable() { + public void run() { + getCacheModelData(node, update, service, dmc, rm, executor ); + } + }); + } + } + + private void getCacheModelData(final IVMNode node, final IViewerUpdate update, final IDMService service, final IDMContext dmc, + final DataRequestMonitor rm, final Executor executor) + { + ElementDataKey key = makeEntryKey(node, update); + final ElementDataEntry entry = getElementDataEntry(key); + /*if (entry.fDirty) { + rm.setStatus(Status.CANCEL_STATUS); + rm.done(); + } else */{ + Object dataOrStatus = entry.fDataOrStatus.get(dmc); + if(dataOrStatus != null) { + if (dataOrStatus instanceof IDMData) { + rm.setData( dataOrStatus ); + } else { + rm.setStatus((IStatus)dataOrStatus ); + } + rm.done(); + } else { + // Determine if we are already running on a DSF executor thread. if so then + // we do not need to create a new one to issue the request to the service. + DsfExecutor dsfExecutor = service.getExecutor(); + if ( dsfExecutor.isInExecutorThread() ) { + getModelDataFromService(node, update, service, dmc, rm, executor, entry ); + } + else { + try { + dsfExecutor.execute(new DsfRunnable() { + public void run() { + getModelDataFromService(node, update, service, dmc, rm, executor, entry ); + } + }); + } catch (RejectedExecutionException e) { + rm.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, "Service session's executor shut down.", null)); //$NON-NLS-1$ + rm.done(); + } + } + } + } + } + + private void getModelDataFromService(final IVMNode node, final IViewerUpdate update, final IDMService service, final IDMContext dmc, + final DataRequestMonitor rm, final Executor executor, final ElementDataEntry entry) + { + service.getModelData( + dmc, + new ViewerDataRequestMonitor<IDMData>(executor, update) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + entry.fDataOrStatus.put(dmc, getData()); + rm.setData(getData()); + } else { + if (!isCanceled()) { + entry.fDataOrStatus.put(dmc, getStatus()); + } + rm.setStatus(getStatus()); + } + rm.done(); + } + }); + } + + /** + * Retrieves the deprecated IDMData object for the given IDMContext. This + * method should be removed once the use of IDMData is replaced with + * {@link IElementPropertiesProvider}. + */ + @Deprecated + public IDMData getArchivedModelData(IVMNode node, IViewerUpdate update, IDMContext dmc) { + ElementDataKey key = makeEntryKey(node, update); + final Entry entry = fCacheData.get(key); + if ( entry instanceof ElementDataEntry) { + Map<IDMContext,IDMData> archiveData = ((ElementDataEntry)entry).fArchiveData; + if (archiveData != null) { + return archiveData.get(dmc); + } + } + return null; + } + + /** + * @since 1.1 + */ + public IVMUpdateScope[] getAvailableUpdateScopes() { + return fAvailableUpdateScopes; + } + + /** + * @since 1.1 + */ + public IVMUpdateScope getActiveUpdateScope() { + String updateScopeId = (String)getPresentationContext().getProperty(SELECTED_UPDATE_SCOPE); + if (updateScopeId != null) { + for (IVMUpdateScope updateScope : getAvailableUpdateScopes()) { + if (updateScope.getID().equals(updateScopeId)) { + return updateScope; + } + } + } + + // Default to the first one. + return getAvailableUpdateScopes()[0]; + } + + /** + * @since 1.1 + */ + public void setActiveUpdateScope(IVMUpdateScope updateScope) { + getPresentationContext().setProperty(SELECTED_UPDATE_SCOPE, updateScope.getID()); + } + + @Override + public boolean shouldWaitHandleEventToComplete() { + return fDelayEventHandleForViewUpdate; + } + + /** + * @since 1.1 + */ + protected void setDelayEventHandleForViewUpdate(boolean on) { + fDelayEventHandleForViewUpdate = on; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/AllUpdateScope.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/AllUpdateScope.java new file mode 100644 index 00000000000..6d0410643ac --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/AllUpdateScope.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * 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.ui.viewmodel.update; + + +/** + * An "automatic" update policy which causes the view model provider cache to + * be flushed whenever an event causes a delta to be generated in the given + * model. + * + * @since 1.1 + */ +public class AllUpdateScope implements IVMUpdateScope { + + public static String ALL_UPDATE_SCOPE_ID = "org.eclipse.cdt.dsf.ui.viewmodel.update.allUpdateScope"; //$NON-NLS-1$ + + public String getID() { + return ALL_UPDATE_SCOPE_ID; + } + + public String getName() { + return ViewModelUpdateMessages.AllUpdateScope_name; + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/AutomaticUpdatePolicy.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/AutomaticUpdatePolicy.java new file mode 100644 index 00000000000..db371896070 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/AutomaticUpdatePolicy.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * 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.ui.viewmodel.update; + +import org.eclipse.jface.viewers.TreePath; + +/** + * An "automatic" update policy which causes the view model provider cache to + * be flushed whenever an event causes a delta to be generated in the given + * model. + */ +public class AutomaticUpdatePolicy implements IVMUpdatePolicy { + + public static String AUTOMATIC_UPDATE_POLICY_ID = "org.eclipse.cdt.dsf.ui.viewmodel.update.defaultUpdatePolicy"; //$NON-NLS-1$ + + public static IElementUpdateTester fgUpdateTester = new IElementUpdateTester() { + public int getUpdateFlags(Object viewerInput, TreePath path) { + return FLUSH | ARCHIVE; + } + + public boolean includes(IElementUpdateTester tester) { + return tester.equals(this); + } + + @Override + public String toString() { + return "Automatic update tester"; //$NON-NLS-1$ + } + }; + + public String getID() { + return AUTOMATIC_UPDATE_POLICY_ID; + } + + public String getName() { + return ViewModelUpdateMessages.AutomaticUpdatePolicy_name; + } + + public IElementUpdateTester getElementUpdateTester(Object event) { + return fgUpdateTester; + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/ICachingVMProvider.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/ICachingVMProvider.java new file mode 100644 index 00000000000..296c88aa38a --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/ICachingVMProvider.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * 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.ui.viewmodel.update; + +import org.eclipse.cdt.dsf.ui.viewmodel.IVMProvider; + +/** + * A view model provider which supports caching of data returned by view model + * nodes. The methods in this interface allow clients to configure how the + * cache should be updated in response to different events. + */ +public interface ICachingVMProvider extends IVMProvider { + + /** + * Returns the update policies that the given provider supports. + */ + public IVMUpdatePolicy[] getAvailableUpdatePolicies(); + + /** + * Returns the active update policy. + */ + public IVMUpdatePolicy getActiveUpdatePolicy(); + + /** + * Sets the active update policy. This has to be one of the update + * policies supported by the provider. + */ + public void setActiveUpdatePolicy(IVMUpdatePolicy mode); + + /** + * Forces the view to flush its cache and re-fetch data from the view + * model nodes. + */ + public void refresh(); + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/ICachingVMProviderExtension.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/ICachingVMProviderExtension.java new file mode 100644 index 00000000000..ba770ccc28e --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/ICachingVMProviderExtension.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.ui.viewmodel.update; + + +/** + * @since 1.1 + */ +public interface ICachingVMProviderExtension extends ICachingVMProvider { + + /** + * Returns the update policies that the given provider supports. + */ + public IVMUpdateScope[] getAvailableUpdateScopes(); + + /** + * Returns the active update policy. + */ + public IVMUpdateScope getActiveUpdateScope(); + + /** + * Sets the active update policy. This has to be one of the update + * policies supported by the provider. + */ + public void setActiveUpdateScope(IVMUpdateScope mode); +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/IElementUpdateTester.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/IElementUpdateTester.java new file mode 100644 index 00000000000..0b9b4dfc474 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/IElementUpdateTester.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * 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.ui.viewmodel.update; + +import org.eclipse.jface.viewers.TreePath; + +/** + * Tester object used to determine how individual update cache + * entries should be updated during a flush operation. + * + * @see IVMUpdatePolicy + */ +public interface IElementUpdateTester { + + /** + * Returns the flags indicating what updates should be performed on the + * cache entry of the given element. + */ + public int getUpdateFlags(Object viewerInput, TreePath path); + + /** + * Returns whether update represented by this tester includes another + * update. For example if update A was created as a result of an element X, + * and update B was created for an element Y, and element X is a parent of + * element Y, then tester A should include tester B. Also a tester should + * always include itself. + * <p/> + * This method is used to optimize the repeated flushing of the cache as + * it allows the cache to avoid needlessly updating the same cache entries. + */ + public boolean includes(IElementUpdateTester tester); + +}
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/IVMUpdatePolicy.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/IVMUpdatePolicy.java new file mode 100644 index 00000000000..96eb81e3a79 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/IVMUpdatePolicy.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * 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.ui.viewmodel.update; + + +/** + * Interface for an update policy. The main function of an update policy is + * to create an element tester for each given event. The element tester + * is then used to update the viewer cache. + */ +public interface IVMUpdatePolicy { + + /** + * Flag indicating that a given entry in the cache should be cleared. + */ + public static int FLUSH = 0x1; + + /** + * Flag indicating that a given entry in the cache should be cleared + * and saved for purpose of change tracking. + */ + public static int ARCHIVE = FLUSH | 0x2; // Flush is required when archiving. + + /** + * Flag indicating that the a given cache entry should be marked as dirty. + * A dirty cache entry is one that is known not to be consistent with + * target data. + */ + public static int DIRTY = 0x4; + + /** + * Returns unique ID of this update policy. + */ + public String getID(); + + /** + * Returns the user-presentable name of this update policy. + */ + public String getName(); + + /** + * Creates an element tester for the given event. + */ + public IElementUpdateTester getElementUpdateTester(Object event); +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/IVMUpdateScope.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/IVMUpdateScope.java new file mode 100644 index 00000000000..ab0158a2b95 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/IVMUpdateScope.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * 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.ui.viewmodel.update; + + +/** + * @since 1.1 + */ +public interface IVMUpdateScope { + + /** + * Returns unique ID of this update policy. + */ + public String getID(); + + /** + * Returns the user-presentable name of this update policy. + */ + public String getName(); +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/ManualUpdatePolicy.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/ManualUpdatePolicy.java new file mode 100644 index 00000000000..1d8a93e2e5b --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/ManualUpdatePolicy.java @@ -0,0 +1,106 @@ +/******************************************************************************* + * 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.ui.viewmodel.update; + +import java.util.Set; + +import org.eclipse.jface.viewers.TreePath; + + +/** + * An "manual" update policy which causes the view model provider cache to be + * flushed only as a result of an explicit user action. + */ +public class ManualUpdatePolicy implements IVMUpdatePolicy { + + public static String MANUAL_UPDATE_POLICY_ID = "org.eclipse.cdt.dsf.ui.viewmodel.update.manualUpdatePolicy"; //$NON-NLS-1$ + + public static Object REFRESH_EVENT = new Object(); + + private static class UserEditEventUpdateTester implements IElementUpdateTester { + private final Set<Object> fElements; + + public UserEditEventUpdateTester(Set<Object> elements) { + fElements = elements; + } + + public int getUpdateFlags(Object viewerInput, TreePath path) { + if (fElements.contains(viewerInput)) { + return FLUSH; + } + for (int i = 0; i < path.getSegmentCount(); i++) { + if (fElements.contains(path.getSegment(i))) { + return FLUSH; + } + } + return 0; + } + + public boolean includes(IElementUpdateTester tester) { + return + tester instanceof UserEditEventUpdateTester && + fElements.equals(((UserEditEventUpdateTester)tester).fElements); + } + + @Override + public String toString() { + return "Edit (" + fElements + ") update tester"; //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + private static IElementUpdateTester fgUpdateTester = new IElementUpdateTester() { + public int getUpdateFlags(Object viewerInput, TreePath path) { + return DIRTY; + } + + public boolean includes(IElementUpdateTester tester) { + return tester.equals(this); + } + + @Override + public String toString() { + return "Manual (refresh = false) update tester"; //$NON-NLS-1$ + } + }; + + private static IElementUpdateTester fgRefreshUpdateTester = new IElementUpdateTester() { + public int getUpdateFlags(Object viewerInput, TreePath path) { + return FLUSH | ARCHIVE; + } + + public boolean includes(IElementUpdateTester tester) { + return tester.equals(this) || tester.equals(fgUpdateTester) || tester instanceof UserEditEventUpdateTester; + } + + @Override + public String toString() { + return "Manual (refresh = true) update tester"; //$NON-NLS-1$ + } + }; + + public String getID() { + return MANUAL_UPDATE_POLICY_ID; + } + + public String getName() { + return ViewModelUpdateMessages.ManualUpdatePolicy_name; + } + + public IElementUpdateTester getElementUpdateTester(Object event) { + if (event.equals(REFRESH_EVENT)) { + return fgRefreshUpdateTester; + } else if (event instanceof UserEditEvent) { + return new UserEditEventUpdateTester(((UserEditEvent)event).getElements()); + } + return fgUpdateTester; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/MultiLevelUpdateHandler.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/MultiLevelUpdateHandler.java new file mode 100644 index 00000000000..4605629a504 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/MultiLevelUpdateHandler.java @@ -0,0 +1,233 @@ +/******************************************************************************* + * 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.ui.viewmodel.update; + +import java.util.List; +import java.util.Stack; +import java.util.concurrent.Executor; +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.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMModelProxyExtension; +import org.eclipse.cdt.dsf.ui.viewmodel.VMChildrenUpdate; +import org.eclipse.cdt.dsf.ui.viewmodel.VMHasChildrenUpdate; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider; +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.IPresentationContext; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.TreePath; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.graphics.RGB; + +@SuppressWarnings("restriction") +class MultiLevelUpdateHandler extends DataRequestMonitor<List<Object>> { + + private static final boolean DEBUG = Boolean.parseBoolean(Platform.getDebugOption("org.eclipse.cdt.dsf.ui/debug/vm/atomicUpdate")); //$NON-NLS-1$ //; + + private static final class UpdateLevel { + private final List<Object> fChildren; + private final TreePath fPath; + private int fChildIndex; + private UpdateLevel(TreePath path, List<Object> children) { + fPath = path; + fChildren = children; + fChildIndex = 0; + assert !isDone(); + } + private boolean isDone() { + return fChildIndex == fChildren.size(); + } + private Object nextChild() { + if (isDone()) { + return null; + } + return fChildren.get(fChildIndex++); + } + } + private final class DummyLabelUpdate implements ILabelUpdate { + private final RequestMonitor fMonitor; + private final Object fData; + private final TreePath fPath; + + private DummyLabelUpdate(Object data, TreePath path, RequestMonitor rm) { + fMonitor= rm; + fData= data; + fPath= path; + } + public Object getElement() { + return fData; + } + public TreePath getElementPath() { + return fPath.createChildPath(fData); + } + public IPresentationContext getPresentationContext() { + return fPresentationContext; + } + public Object getViewerInput() { + return fViewerInput; + } + public void cancel() { } + public IStatus getStatus() { + return null; + } + public boolean isCanceled() { + return false; + } + public void setStatus(IStatus status) { } + public String[] getColumnIds() { + return fColumns; + } + public void setBackground(RGB arg0, int arg1) { } + public void setFontData(FontData arg0, int arg1) { } + public void setForeground(RGB arg0, int arg1) { } + public void setImageDescriptor(ImageDescriptor arg0, int arg1) { } + public void setLabel(String arg0, int arg1) { + } + public void done() { + fMonitor.done(); + } + } + + private final Executor fExecutor; + private final IElementContentProvider fContentProvider; + private final IPresentationContext fPresentationContext; + private final String[] fColumns; + private final Viewer fViewer; + private final Object fViewerInput; + private final Stack<UpdateLevel> fStack = new Stack<UpdateLevel>(); + private final CountingRequestMonitor fRequestMonitor; + + private int fIndex = 0; + private TreePath fCurrentPath; + private int fLowIndex = 0; + private int fHighIndex = Integer.MAX_VALUE - 1; + private int fPendingUpdates; + + public MultiLevelUpdateHandler(Executor executor, + IVMModelProxyExtension modelProxy, + IPresentationContext presentationContext, + IElementContentProvider contentProvider, + RequestMonitor parentRequestMonitor) { + super(executor, null); + fExecutor = executor; + fViewer = modelProxy.getViewer(); + fViewerInput = modelProxy.getViewerInput(); + fCurrentPath = modelProxy.getRootPath(); + fPresentationContext = presentationContext; + fColumns = presentationContext.getColumns(); + fContentProvider = contentProvider; + + fRequestMonitor = new CountingRequestMonitor(fExecutor, parentRequestMonitor); + } + void startUpdate() { + if (DEBUG) System.out.println("[MultiLevelUpdateHandler] startUpdate " + fLowIndex + '-' + fHighIndex); //$NON-NLS-1$ + fContentProvider.update(new IChildrenUpdate[] { + new VMChildrenUpdate(fCurrentPath, fViewerInput, fPresentationContext, -1, -1, this) + }); + } + void setRange(int low, int high) { + fLowIndex = low; + fHighIndex = high; + } + boolean isDone() { + return fStack.isEmpty(); + } + @Override + public synchronized void done() { + try { + fExecutor.execute(new DsfRunnable() { + public void run() { + final List<Object> data= getData(); + if (data != null && !data.isEmpty()) { + if (DEBUG) System.out.println("[MultiLevelUpdateHandler] gotChildUpdate " + data.size()); //$NON-NLS-1$ + fStack.push(new UpdateLevel(fCurrentPath, data)); + } + processNext(); + } + @Override + public String toString() { + return "Completed: " + MultiLevelUpdateHandler.this.toString(); //$NON-NLS-1$ + } + }); + } catch (RejectedExecutionException e) { + handleRejectedExecutionException(); + } + } + protected void processNext() { + while (true) { + if (fIndex > fHighIndex) { + fStack.clear(); + } + if (fStack.isEmpty()) { + fRequestMonitor.setDoneCount(fPendingUpdates); + super.done(); + return; + } + UpdateLevel current = fStack.peek(); + assert !current.isDone(); + + TreePath path = current.fPath; + Object data = current.nextChild(); + if (current.isDone()) { + fStack.pop(); + } + if (data == null) { + // consider null children - http://bugs.eclipse.org/250309 + ++fIndex; + continue; + } + path = path.createChildPath(data); + + if (fIndex >= fLowIndex && fIndex <= fHighIndex) { + if(data instanceof IAdaptable) { + IElementLabelProvider labelProvider = (IElementLabelProvider) ((IAdaptable)data).getAdapter(IElementLabelProvider.class); + if (labelProvider != null) { + ++fPendingUpdates; + if (DEBUG) System.out.println("[MultiLevelUpdateHandler] labelUpdate " + data); //$NON-NLS-1$ + labelProvider.update(new ILabelUpdate[] { + new DummyLabelUpdate(data, path, fRequestMonitor) + }); + } + } + } + fIndex++; + if (fViewer instanceof TreeViewer) { + TreeViewer treeViewer = (TreeViewer) fViewer; + if (treeViewer.getExpandedState(data)) { + fCurrentPath = path; + if (DEBUG) System.out.println("[MultiLevelUpdateHandler] childrenUpdate " + data); //$NON-NLS-1$ + fContentProvider.update(new IChildrenUpdate[] { + new VMChildrenUpdate(path, fViewerInput, fPresentationContext, -1, -1, this) + }); + return; + } else if (fIndex >= fLowIndex) { + // update also the hasChildren flag + ++fPendingUpdates; + if (DEBUG) System.out.println("[MultiLevelUpdateHandler] hasChildUpdate " + data); //$NON-NLS-1$ + fContentProvider.update(new IHasChildrenUpdate[] { + new VMHasChildrenUpdate(path, fViewerInput, fPresentationContext, new DataRequestMonitor<Boolean>(fExecutor, fRequestMonitor)) + }); + } + } + } + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/UpdatePolicyDecorator.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/UpdatePolicyDecorator.java new file mode 100644 index 00000000000..a0e4834f192 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/UpdatePolicyDecorator.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * 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.ui.viewmodel.update; + +/** + * An update policy decorator which can override behaviour of an underlying update policy. + * + * @since 1.1 + */ +public abstract class UpdatePolicyDecorator implements IVMUpdatePolicy { + + private final IVMUpdatePolicy fBasePolicy; + + protected UpdatePolicyDecorator(IVMUpdatePolicy base) { + fBasePolicy= base; + } + + protected final IVMUpdatePolicy getBaseUpdatePolicy() { + return fBasePolicy; + } + + public final String getID() { + return fBasePolicy.getID(); + } + + public String getName() { + return fBasePolicy.getName(); + } + + public IElementUpdateTester getElementUpdateTester(Object event) { + return fBasePolicy.getElementUpdateTester(event); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/UserEditEvent.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/UserEditEvent.java new file mode 100644 index 00000000000..c0244fa3c8e --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/UserEditEvent.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * 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.ui.viewmodel.update; + +import java.util.HashSet; +import java.util.Set; + +/** + * An event representing a user editing of the data in the viewer. Typically, when + * a viewer is configured to be in a manual update mode, if user edits a value, the + * viewer should still update at least the value that the user editor. This event + * is used to accomplish that behavior. + */ +public class UserEditEvent { + private final Set<Object> fElements; + + public UserEditEvent(Object element) { + fElements = new HashSet<Object>(); + fElements.add(element); + } + + public UserEditEvent(Set<Object> elements) { + fElements = elements; + } + + public Set<Object> getElements() { + return fElements; + } +}
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/ViewModelUpdateMessages.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/ViewModelUpdateMessages.java new file mode 100644 index 00000000000..ff6b1b5deef --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/ViewModelUpdateMessages.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.ui.viewmodel.update; + +import org.eclipse.osgi.util.NLS; + +public class ViewModelUpdateMessages extends NLS { + + private static final String BUNDLE_NAME = "org.eclipse.cdt.dsf.ui.viewmodel.update.ViewModelUpdateMessages";//$NON-NLS-1$ + + public static String AutomaticUpdatePolicy_name; + public static String ManualUpdatePolicy_name; + /** + * @since 1.1 + */ + public static String AllUpdateScope_name; + /** + * @since 1.1 + */ + public static String VisibleUpdateScope_name; + + static { + // load message values from bundle file + NLS.initializeMessages(BUNDLE_NAME, ViewModelUpdateMessages.class); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/ViewModelUpdateMessages.properties b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/ViewModelUpdateMessages.properties new file mode 100644 index 00000000000..9de3c47ec66 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/ViewModelUpdateMessages.properties @@ -0,0 +1,14 @@ +############################################################################### +# 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 +############################################################################### +AutomaticUpdatePolicy_name=Automatic +ManualUpdatePolicy_name=Manual +AllUpdateScope_name=All Elements +VisibleUpdateScope_name=Visible Elements
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/VisibleUpdateScope.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/VisibleUpdateScope.java new file mode 100644 index 00000000000..093a33ba5de --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/VisibleUpdateScope.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * 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.ui.viewmodel.update; + + +/** + * An "automatic" update policy which causes the view model provider cache to + * be flushed whenever an event causes a delta to be generated in the given + * model. + * @since 1.1 + */ +public class VisibleUpdateScope implements IVMUpdateScope { + + public static String VISIBLE_UPDATE_SCOPE_ID = "org.eclipse.cdt.dsf.ui.viewmodel.update.visibleUpdateScope"; //$NON-NLS-1$ + + public String getID() { + return VISIBLE_UPDATE_SCOPE_ID; + } + + public String getName() { + return ViewModelUpdateMessages.VisibleUpdateScope_name; + } +} diff --git a/dsf/org.eclipse.cdt.dsf/.classpath b/dsf/org.eclipse.cdt.dsf/.classpath new file mode 100644 index 00000000000..304e86186aa --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/.classpath @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/> + <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/dsf/org.eclipse.cdt.dsf/.cvsignore b/dsf/org.eclipse.cdt.dsf/.cvsignore new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/.cvsignore @@ -0,0 +1 @@ + diff --git a/dsf/org.eclipse.cdt.dsf/.options b/dsf/org.eclipse.cdt.dsf/.options new file mode 100644 index 00000000000..77f365ef759 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/.options @@ -0,0 +1,3 @@ +org.eclipse.cdt.dsf/debug = false +org.eclipse.cdt.dsf/debug/executor = false +org.eclipse.cdt.dsf/debug/executorName = diff --git a/dsf/org.eclipse.cdt.dsf/.project b/dsf/org.eclipse.cdt.dsf/.project new file mode 100644 index 00000000000..4dc6a12b637 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/.project @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>org.eclipse.cdt.dsf</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.ManifestBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.SchemaBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.api.tools.apiAnalysisBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.pde.PluginNature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + <nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature> + </natures> +</projectDescription> diff --git a/dsf/org.eclipse.cdt.dsf/.settings/org.eclipse.jdt.core.prefs b/dsf/org.eclipse.cdt.dsf/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..5ca4ed2212c --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,70 @@ +#Tue Jun 24 11:02:22 PDT 2008 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.5 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.autoboxing=ignore +org.eclipse.jdt.core.compiler.problem.deprecation=warning +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning +org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=warning +org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore +org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=warning +org.eclipse.jdt.core.compiler.problem.nullReference=error +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning +org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled +org.eclipse.jdt.core.compiler.problem.unusedImport=error +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=warning +org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.source=1.5 diff --git a/dsf/org.eclipse.cdt.dsf/.settings/org.eclipse.jdt.ui.prefs b/dsf/org.eclipse.cdt.dsf/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 00000000000..087537fe3fb --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,3 @@ +#Sun Aug 06 18:10:27 CEST 2006 +eclipse.preferences.version=1 +internal.default.compliance=default diff --git a/dsf/org.eclipse.cdt.dsf/META-INF/MANIFEST.MF b/dsf/org.eclipse.cdt.dsf/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..ac497668cd7 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/META-INF/MANIFEST.MF @@ -0,0 +1,23 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %pluginName +Bundle-Vendor: %providerName +Bundle-SymbolicName: org.eclipse.cdt.dsf;singleton:=true +Bundle-Version: 2.0.0.qualifier +Bundle-Activator: org.eclipse.cdt.dsf.internal.DsfPlugin +Bundle-Localization: plugin +Require-Bundle: org.eclipse.core.runtime, + org.eclipse.debug.core, + org.eclipse.cdt.dsf, + org.eclipse.cdt.core, + org.eclipse.cdt.debug.core +Bundle-ActivationPolicy: lazy +Export-Package: org.eclipse.cdt.dsf.concurrent, + org.eclipse.cdt.dsf.datamodel, + org.eclipse.cdt.dsf.debug.internal.provisional.model, + org.eclipse.cdt.dsf.debug.model, + org.eclipse.cdt.dsf.debug.service, + org.eclipse.cdt.dsf.debug.service.command, + org.eclipse.cdt.dsf.debug.sourcelookup, + org.eclipse.cdt.dsf.service +Bundle-RequiredExecutionEnvironment: J2SE-1.5 diff --git a/dsf/org.eclipse.cdt.dsf/about.html b/dsf/org.eclipse.cdt.dsf/about.html new file mode 100644 index 00000000000..cb740ae8bc8 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/about.html @@ -0,0 +1,24 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"><head> + + +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>About</title></head><body lang="EN-US"> +<h2>About This Content</h2> + +<p>June 5, 2007</p> +<h3>License</h3> + +<p>The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>. +For purposes of the EPL, "Program" will mean the Content.</p> + +<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p> + +</body></html>
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.dsf/buckminster.cspex b/dsf/org.eclipse.cdt.dsf/buckminster.cspex new file mode 100644 index 00000000000..1aaa95e7dfd --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/buckminster.cspex @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<cs:cspecExtension xmlns:cs="http://www.eclipse.org/buckminster/CSpec-1.0" name="org.eclipse.cdt.dsf"> + <cs:artifacts> + <cs:public name="source" path="src/"/> + </cs:artifacts> + <cs:actions> + <cs:public name="java.binary.archives" actor="ant"> + <cs:actorProperties> + <cs:property key="buildFile" value="make/build.xml"/> + </cs:actorProperties> + <cs:prerequisites alias="input"> + <cs:local attribute="eclipse.build"/> + </cs:prerequisites> + <cs:products alias="output" base="bin/jars/"> + <cs:path path="dsf.jar"/> + </cs:products> + </cs:public> + <cs:private name="eclipse.build"> + <cs:prerequisites> + <cs:local attribute="source"/> + </cs:prerequisites> + <cs:products base="bin/classes/"> + <cs:path path="."/> + </cs:products> + </cs:private> + </cs:actions> + <cs:groups> + <cs:public name="java.binaries"> + <cs:local attribute="eclipse.build"/> + </cs:public> + </cs:groups> +</cs:cspecExtension> diff --git a/dsf/org.eclipse.cdt.dsf/build.properties b/dsf/org.eclipse.cdt.dsf/build.properties new file mode 100644 index 00000000000..e061b4e53d0 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/build.properties @@ -0,0 +1,17 @@ +############################################################################### +# 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 +############################################################################### +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + plugin.xml,\ + plugin.properties,\ + about.html diff --git a/dsf/org.eclipse.cdt.dsf/make/build.xml b/dsf/org.eclipse.cdt.dsf/make/build.xml new file mode 100644 index 00000000000..51a4fb7a32f --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/make/build.xml @@ -0,0 +1,10 @@ +<?xml version="1.0"?> +<project name="org.eclipse.cdt.dsf"> + <target name="java.binary.archives"> + <dirname property="output.dir" file="${output}"/> + <mkdir dir="${output.dir}"/> + <jar destfile="${output}"> + <fileset refid="input.fileset"/> + </jar> + </target> +</project> diff --git a/dsf/org.eclipse.cdt.dsf/make/dsf.jardesc b/dsf/org.eclipse.cdt.dsf/make/dsf.jardesc new file mode 100644 index 00000000000..76daa653d13 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/make/dsf.jardesc @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<jardesc> +<jar path="bin/jars/worlds.jar"/> +<options buildIfNeeded="true" compress="true" descriptionLocation="/org.eclipse.cdt.dsf/make/dsf.jardesc" exportErrors="true" exportWarnings="true" includeDirectoryEntries="false" overwrite="false" saveDescription="true" storeRefactorings="false" useSourceFolders="false"/> +<storedRefactorings deprecationInfo="true" structuralOnly="false"/> +<selectedProjects/> +<manifest generateManifest="true" manifestLocation="" manifestVersion="1.0" reuseManifest="false" saveManifest="false" usesManifest="true"> +<sealing sealJar="false"> +<packagesToSeal/> +<packagesToUnSeal/> +</sealing> +</manifest> +<selectedElements exportClassFiles="true" exportJavaFiles="false" exportOutputFolder="false"> +<javaElement handleIdentifier="=org.eclipse.cdt.dsf"/> +</selectedElements> +</jardesc> diff --git a/dsf/org.eclipse.cdt.dsf/plugin.properties b/dsf/org.eclipse.cdt.dsf/plugin.properties new file mode 100644 index 00000000000..7c8259ba857 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/plugin.properties @@ -0,0 +1,13 @@ +############################################################################### +# Copyright (c) 2006 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 +############################################################################### +pluginName=Debug Services Framework Core +providerName=Eclipse.org + diff --git a/dsf/org.eclipse.cdt.dsf/plugin.xml b/dsf/org.eclipse.cdt.dsf/plugin.xml new file mode 100644 index 00000000000..33ca93ceb1b --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/plugin.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<?eclipse version="3.2"?> +<plugin> + +</plugin> diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/ConfinedToDsfExecutor.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/ConfinedToDsfExecutor.java new file mode 100644 index 00000000000..27452c25061 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/ConfinedToDsfExecutor.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * 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.concurrent; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation idicating that given package, class, method, or field can be + * access safely only from a DSF executor thread. If declared on package or type, + * a field or method could still be declared with an annotation indicating that it's + * thread-safe. + * <p> + * Note: the runtime retention policy is there to allow automated testing + * and validation code. + * + * @param value The value indicates the method to use to obtain the executor. + * It should be null if it cannot be determined from the given object. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.PACKAGE, ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR}) +@Inherited +@Documented +public @interface ConfinedToDsfExecutor { + String value(); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/CountingRequestMonitor.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/CountingRequestMonitor.java new file mode 100644 index 00000000000..ea9fa9d6c72 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/CountingRequestMonitor.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * 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.concurrent; + +import java.util.concurrent.Executor; + +import org.eclipse.cdt.dsf.internal.DsfPlugin; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; + +/** + * Utility class to collect multiple request monitor results of commands + * that are initiated simultaneously. The usage is as follows: + * <code><pre> + * final CountingRequestMonitor countingRm = new CountingRequestMonitor(fExecutor, null) { + * public void handleCompleted() { + * System.out.println("All complete, errors=" + !getStatus().isOK()); + * } + * }; + * + * int count = 0; + * for (int i : elements) { + * service.call(i, countingRm); + * count++; + * } + * + * countingRm.setDoneCount(count); + * </pre></code> + */ +public class CountingRequestMonitor extends RequestMonitor { + /** + * Counter tracking the remaining number of times that the done() method + * needs to be called before this request monitor is actually done. + */ + private int fDoneCounter; + + /** + * Flag indicating whether the initial count has been set on this monitor. + */ + private boolean fInitialCountSet = false; + + public CountingRequestMonitor(Executor executor, RequestMonitor parentRequestMonitor) { + super(executor, parentRequestMonitor); + super.setStatus(new MultiStatus(DsfPlugin.PLUGIN_ID, 0, "Collective status for set of sub-operations.", null)); //$NON-NLS-1$ + } + + /** + * Sets the number of times that this request monitor needs to be called + * before this monitor is truly considered done. This method must be called + * exactly once in the life cycle of each counting request monitor. + * @param count Number of times that done() has to be called to mark the request + * monitor as complete. If count is '0', then the counting request monitor is + * marked as done immediately. + */ + public synchronized void setDoneCount(int count) { + assert !fInitialCountSet; + fInitialCountSet = true; + fDoneCounter += count; + if (fDoneCounter <= 0) { + assert fDoneCounter == 0; // Mismatch in the count. + super.done(); + } + } + + /** + * Called to indicate that one of the calls using this monitor is finished. + * Only when <code>done</done> is called the number of times corresponding to the + * count, the request monitor will be considered complete. This method can be + * called before {@link #setDoneCount(int)}. + */ + @Override + public synchronized void done() { + fDoneCounter--; + if (fInitialCountSet && fDoneCounter <= 0) { + assert fDoneCounter == 0; // Mismatch in the count. + super.done(); + } + } + + @Override + public String toString() { + return "CountingRequestMonitor: " + getStatus().toString(); //$NON-NLS-1$ + } + + @Override + public synchronized void setStatus(IStatus status) { + if ((getStatus() instanceof MultiStatus)) { + ((MultiStatus)getStatus()).add(status); + } + }; +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/DataRequestMonitor.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/DataRequestMonitor.java new file mode 100644 index 00000000000..58daf516459 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/DataRequestMonitor.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2006, 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.concurrent; + +import java.util.concurrent.Executor; + + +/** + * Request monitor that allows data to be returned to the request initiator. + * + * @param V The type of the data object that this monitor handles. + */ +public class DataRequestMonitor<V> extends RequestMonitor { + + /** Data object reference */ + private V fData; + + public DataRequestMonitor(Executor executor, RequestMonitor parentRequestMonitor) { + super(executor, parentRequestMonitor); + } + + /** + * Sets the data object to specified value. To be called by the + * asynchronous method implementor. + * @param data Data value to set. + */ + public synchronized void setData(V data) { fData = data; } + + /** + * Returns the data value, null if not set. + */ + public synchronized V getData() { return fData; } + + @Override + public String toString() { + if (getData() != null) { + return getData().toString(); + } else { + return super.toString(); + } + } +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/DefaultDsfExecutor.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/DefaultDsfExecutor.java new file mode 100644 index 00000000000..e2c7efc5642 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/DefaultDsfExecutor.java @@ -0,0 +1,374 @@ +/******************************************************************************* + * 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.concurrent; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; + +import org.eclipse.cdt.dsf.internal.DsfPlugin; +import org.eclipse.core.runtime.ILog; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; + +/** + * Default implementation of a DSF executor interfaces, based on the + * standard java.util.concurrent.ThreadPoolExecutor. + */ + +public class DefaultDsfExecutor extends ScheduledThreadPoolExecutor + implements DsfExecutor +{ + /** + * Instance counter for DSF executors. Used in the executor's thread name. + */ + private static int fgInstanceCounter = 0; + + /** + * Name of the executor, used in the executor's thread name. + */ + private String fName; + + /** + * Instance number of this executor, used with the executor name. + */ + private int fInstanceNumber; + + /** Thread factory that creates the single thread to be used for this executor */ + static class DsfThreadFactory implements ThreadFactory { + private String fThreadName; + DsfThreadFactory(String name) { + fThreadName = name; + } + + Thread fThread; + public Thread newThread(Runnable r) { + assert fThread == null; // Should be called only once. + fThread = new Thread(new ThreadGroup(fThreadName), r, fThreadName, 0); + return fThread; + } + } + + public DefaultDsfExecutor() { + this("DSF Executor"); //$NON-NLS-1$ + } + + /** + * Creates a new DSF Executor with the given name. + * @param name Name used to create executor's thread. + */ + public DefaultDsfExecutor(String name) { + super(1, new DsfThreadFactory(name + " - " + fgInstanceCounter)); //$NON-NLS-1$ + fName = name; + fInstanceNumber = fgInstanceCounter++; + + if(DEBUG_EXECUTOR || ASSERTIONS_ENABLED) { + // If tracing, pre-start the dispatch thread, and add it to the map. + prestartAllCoreThreads(); + fThreadToExecutorMap.put(((DsfThreadFactory)getThreadFactory()).fThread, DefaultDsfExecutor.this); + } + } + + public boolean isInExecutorThread() { + return Thread.currentThread().equals( ((DsfThreadFactory)getThreadFactory()).fThread ); + } + + protected String getName() { + return fName; + } + + static void logException(Throwable t) { + DsfPlugin plugin = DsfPlugin.getDefault(); + if (plugin == null) return; + + ILog log = plugin.getLog(); + if (log != null) { + log.log(new Status( + IStatus.ERROR, DsfPlugin.PLUGIN_ID, -1, "Uncaught exception in DSF executor thread", t)); //$NON-NLS-1$ + } + // Print out the stack trace to console if assertions are enabled. + if(ASSERTIONS_ENABLED) { + ByteArrayOutputStream outStream = new ByteArrayOutputStream(512); + PrintStream printStream = new PrintStream(outStream); + try { + printStream.write("Uncaught exception in session executor thread: ".getBytes()); //$NON-NLS-1$ + } catch (IOException e2) {} + t.printStackTrace(new PrintStream(outStream)); + System.err.println(outStream.toString()); + } + } + + // + // Utilities used for tracing. + // + protected static boolean DEBUG_EXECUTOR = false; + protected static String DEBUG_EXECUTOR_NAME = ""; //$NON-NLS-1$ + protected static boolean ASSERTIONS_ENABLED = false; + static { + DEBUG_EXECUTOR = DsfPlugin.DEBUG && "true".equals( //$NON-NLS-1$ + Platform.getDebugOption("org.eclipse.cdt.dsf/debug/executor")); //$NON-NLS-1$ + DEBUG_EXECUTOR_NAME = DsfPlugin.DEBUG + ? Platform.getDebugOption("org.eclipse.cdt.dsf/debug/executorName") : ""; //$NON-NLS-1$ //$NON-NLS-2$ + assert (ASSERTIONS_ENABLED = true) == true; + } + + /** + * This map is used by DsfRunnable/Query/DsfCallable to track by which executor + * an executable object was created. + * <br>Note: Only used when tracing. + */ + static Map<Thread, DefaultDsfExecutor> fThreadToExecutorMap = new HashMap<Thread, DefaultDsfExecutor>(); + + /** + * Currently executing runnable/callable. + * <br>Note: Only used when tracing. + */ + TracingWrapper fCurrentlyExecuting; + + /** + * Counter number saved by each tracing runnable when executed + * <br>Note: Only used when tracing. + */ + int fSequenceCounter; + + /** + * Wrapper for runnables/callables, is used to store tracing information + * <br>Note: Only used when tracing. + */ + abstract class TracingWrapper { + /** Sequence number of this runnable/callable */ + int fSequenceNumber = -1; + + /** Trace of where the runnable/callable was submitted to the executor */ + StackTraceWrapper fSubmittedAt = null; + + /** Reference to the runnable/callable that submitted this runnable/callable to the executor */ + TracingWrapper fSubmittedBy = null; + + /** + * @param offset the number of items in the stack trace not to be printed + */ + TracingWrapper(int offset) { + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + // guard against the offset being greater than the stack trace + offset = Math.min(offset, stackTrace.length); + fSubmittedAt = new StackTraceWrapper(new StackTraceElement[stackTrace.length - offset]); + System.arraycopy(stackTrace, offset - 1, fSubmittedAt.fStackTraceElements, 0, fSubmittedAt.fStackTraceElements.length); + if (isInExecutorThread() && fCurrentlyExecuting != null) { + fSubmittedBy = fCurrentlyExecuting; + } + } + + void traceExecution() { + fSequenceNumber = fSequenceCounter++; + fCurrentlyExecuting = this; + + // Write to console only if tracing is enabled (as opposed to tracing or assertions). + if (DEBUG_EXECUTOR && ("".equals(DEBUG_EXECUTOR_NAME) || fName.equals(DEBUG_EXECUTOR_NAME))) { //$NON-NLS-1$ + StringBuilder traceBuilder = new StringBuilder(); + + // Record the time + traceBuilder.append(DsfPlugin.getDebugTime()); + traceBuilder.append(' '); + + // Record the executor # + traceBuilder.append('#'); + traceBuilder.append(fSequenceNumber); + + // Record the executor name + traceBuilder.append('('); + traceBuilder.append(fName); + traceBuilder.append(" - "); //$NON-NLS-1$ + traceBuilder.append(fInstanceNumber); + traceBuilder.append(')'); + traceBuilder.append(' '); + + // Append executable class name + traceBuilder.append(getExecutable().getClass().getName()); + + // Add executable's toString(). + traceBuilder.append("\n "); //$NON-NLS-1$ + traceBuilder.append(getExecutable().toString()); + + // Append "create by" info. + if (getExecutable() instanceof DsfExecutable) { + DsfExecutable dsfExecutable = (DsfExecutable)getExecutable(); + if (dsfExecutable.fCreatedAt != null || dsfExecutable.fCreatedBy != null) { + traceBuilder.append("\n created "); //$NON-NLS-1$ + if (dsfExecutable.fCreatedBy != null) { + traceBuilder.append(" by #"); //$NON-NLS-1$ + traceBuilder.append(dsfExecutable.fCreatedBy.fSequenceNumber); + } + if (dsfExecutable.fCreatedAt != null) { + traceBuilder.append("\n at "); //$NON-NLS-1$ + traceBuilder.append(dsfExecutable.fCreatedAt.fStackTraceElements[0].toString()); + for (int i = 1; i < dsfExecutable.fCreatedAt.fStackTraceElements.length && i < 3; i++) { + traceBuilder.append("\n "); //$NON-NLS-1$ + traceBuilder.append(dsfExecutable.fCreatedAt.fStackTraceElements[i].toString()); + } + } + } + } + + // Submitted info + traceBuilder.append("\n submitted"); //$NON-NLS-1$ + if (fSubmittedBy != null) { + traceBuilder.append(" by #"); //$NON-NLS-1$ + traceBuilder.append(fSubmittedBy.fSequenceNumber); + } + traceBuilder.append("\n at "); //$NON-NLS-1$ + traceBuilder.append(fSubmittedAt.fStackTraceElements[0].toString()); + for (int i = 1; i < fSubmittedAt.fStackTraceElements.length && i < 3; i++) { + traceBuilder.append("\n "); //$NON-NLS-1$ + traceBuilder.append(fSubmittedAt.fStackTraceElements[i].toString()); + } + traceBuilder.append(" at "); //$NON-NLS-1$ + traceBuilder.append(fSubmittedAt.fStackTraceElements[0].toString()); + + // Finally write out to console + DsfPlugin.debug(traceBuilder.toString()); + } + } + + abstract protected Object getExecutable(); + } + + + class TracingWrapperRunnable extends TracingWrapper implements Runnable { + final Runnable fRunnable; + public TracingWrapperRunnable(Runnable runnable, int offset) { + super(offset); + if (runnable == null) throw new NullPointerException(); + fRunnable = runnable; + + // Check if executable wasn't executed already. + if (DEBUG_EXECUTOR && fRunnable instanceof DsfExecutable) { + assert !((DsfExecutable)fRunnable).getSubmitted() : "Executable was previously executed."; //$NON-NLS-1$ + ((DsfExecutable)fRunnable).setSubmitted(); + } + } + + @Override + protected Object getExecutable() { return fRunnable; } + + public void run() { + traceExecution(); + + // Finally invoke the runnable code. + try { + fRunnable.run(); + } catch (RuntimeException e) { + // If an exception was thrown in the Runnable, trace it. + // Because there is no one else to catch it, it is a + // programming error. + logException(e); + throw e; + } + } + } + + public class TracingWrapperCallable<T> extends TracingWrapper implements Callable<T> { + final Callable<T> fCallable; + public TracingWrapperCallable(Callable<T> callable, int offset) { + super(offset); + if (callable == null) throw new NullPointerException(); + fCallable = callable; + } + + @Override + protected Object getExecutable() { return fCallable; } + + public T call() throws Exception { + traceExecution(); + + // Finally invoke the runnable code. + // Note that callables can throw exceptions that can be caught + // by clients that invoked them using ExecutionException. + return fCallable.call(); + } + } + + @Override + public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) { + if(DEBUG_EXECUTOR || ASSERTIONS_ENABLED) { + if ( !(callable instanceof TracingWrapper) ) { + callable = new TracingWrapperCallable<V>(callable, 6); + } + } + return super.schedule(callable, delay, unit); + } + @Override + public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) { + if(DEBUG_EXECUTOR || ASSERTIONS_ENABLED) { + if ( !(command instanceof TracingWrapper) ) { + command = new TracingWrapperRunnable(command, 6); + } + } + return super.schedule(command, delay, unit); + } + + @Override + public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { + if(DEBUG_EXECUTOR || ASSERTIONS_ENABLED) { + command = new TracingWrapperRunnable(command, 6); + } + return super.scheduleAtFixedRate(command, initialDelay, period, unit); + } + + @Override + public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { + if(DEBUG_EXECUTOR || ASSERTIONS_ENABLED) { + command = new TracingWrapperRunnable(command, 6); + } + return super.scheduleWithFixedDelay(command, initialDelay, delay, unit); + } + + @Override + public void execute(Runnable command) { + if(DEBUG_EXECUTOR || ASSERTIONS_ENABLED) { + command = new TracingWrapperRunnable(command, 6); + } + super.execute(command); + } + + @Override + public Future<?> submit(Runnable command) { + if(DEBUG_EXECUTOR || ASSERTIONS_ENABLED) { + command = new TracingWrapperRunnable(command, 6); + } + return super.submit(command); + } + + @Override + public <T> Future<T> submit(Callable<T> callable) { + if(DEBUG_EXECUTOR || ASSERTIONS_ENABLED) { + callable = new TracingWrapperCallable<T>(callable, 6); + } + return super.submit(callable); + } + + @Override + public <T> Future<T> submit(Runnable command, T result) { + if(DEBUG_EXECUTOR || ASSERTIONS_ENABLED) { + command = new TracingWrapperRunnable(command, 6); + } + return super.submit(command, result); + } +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/DsfExecutable.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/DsfExecutable.java new file mode 100644 index 00000000000..52d1d43dd25 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/DsfExecutable.java @@ -0,0 +1,139 @@ +/******************************************************************************* + * 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.concurrent; + +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.cdt.dsf.internal.DsfPlugin; +import org.eclipse.core.runtime.Platform; + +/** + * Base class for DSF-instrumented alternative to the Runnable/Callable interfaces. + * <p> + * While it is perfectly fine for clients to call the DSF executor with + * an object only implementing the Runnable/Callable interface, the DsfExecutable + * contains fields and methods that used for debugging and tracing when + * tracing is enabled. + */ +@Immutable +public class DsfExecutable { + /** + * Flag indicating that tracing of the DSF executor is enabled. It enables + * storing of the "creator" information as well as tracing of disposed + * runnables that have not been submitted to the executor. + */ + static boolean DEBUG_EXECUTOR = false; + + /** + * Flag indicating that assertions are enabled. It enables storing of the + * "creator" executable for debugging purposes. + */ + static boolean ASSERTIONS_ENABLED = false; + + static { + assert (ASSERTIONS_ENABLED = true) == true; + DEBUG_EXECUTOR = DsfPlugin.DEBUG && "true".equals( //$NON-NLS-1$ + Platform.getDebugOption("org.eclipse.cdt.dsf/debug/executor")); //$NON-NLS-1$ + } + + /** + * Field that holds the stack trace of where this executable was created. + * Used for tracing and debugging only. + */ + final StackTraceWrapper fCreatedAt; + + /** + * Field holding the reference of the executable that created this + * executable. Used for tracing only. + */ + final DefaultDsfExecutor.TracingWrapper fCreatedBy; + + /** + * Flag indicating whether this executable was ever executed by an + * executor. Used for tracing only. + */ + private volatile boolean fSubmitted = false; + + @SuppressWarnings("unchecked") + public DsfExecutable() { + // Use assertion flag (-ea) to jre to avoid affecting performance when not debugging. + if (ASSERTIONS_ENABLED || DEBUG_EXECUTOR) { + // Find the runnable/callable that is currently running. + DefaultDsfExecutor executor = DefaultDsfExecutor.fThreadToExecutorMap.get(Thread.currentThread()); + if (executor != null) { + fCreatedBy = executor.fCurrentlyExecuting; + } else { + fCreatedBy = null; + } + + // Get the stack trace and find the first method that is not a + // constructor of this object. + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + Class thisClass = getClass(); + Set<String> classNamesSet = new HashSet<String>(); + while(thisClass != null) { + classNamesSet.add(thisClass.getName()); + thisClass = thisClass.getSuperclass(); + } + int i; + for (i = 3; i < stackTrace.length; i++) { + if ( !classNamesSet.contains(stackTrace[i].getClassName()) ) break; + } + fCreatedAt = new StackTraceWrapper(new StackTraceElement[stackTrace.length - i]); + System.arraycopy(stackTrace, i, fCreatedAt.fStackTraceElements, 0, fCreatedAt.fStackTraceElements.length); + } else { + fCreatedAt = null; + fCreatedBy = null; + } + } + + public boolean getSubmitted() { + return fSubmitted; + } + + /** + * Marks this executable to indicate that it has been executed by the + * executor. To be invoked only by DsfExecutor. + */ + public void setSubmitted() { + fSubmitted = true; + } + + /** + * Returns whether the runnable/callable is expected to be always executed. + * Overriding classes can implement this method and return false, to avoid + * unnecessary trace output. + * @return true if this runnable is expected to run. + */ + protected boolean isExecutionRequired() { + return true; + } + + @Override + protected void finalize() { + if (DEBUG_EXECUTOR && !fSubmitted && isExecutionRequired()) { + StringBuilder traceBuilder = new StringBuilder(); + + // Record the time + traceBuilder.append(DsfPlugin.getDebugTime()); + traceBuilder.append(' '); + + // Record the event + traceBuilder.append("DsfExecutable was never executed:\n "); //$NON-NLS-1$ + traceBuilder.append(this); + traceBuilder.append("\nCreated at:"); //$NON-NLS-1$ + traceBuilder.append(fCreatedAt); + + DsfPlugin.debug(traceBuilder.toString()); + } + } +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/DsfExecutor.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/DsfExecutor.java new file mode 100644 index 00000000000..8c7cd554bc2 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/DsfExecutor.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * 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.concurrent; + +import java.util.concurrent.ScheduledExecutorService; + +/** + * DSF executor service. Implementations of this executor must ensure + * that all runnables and callables are executed in the same thread: the + * executor's single dispatch thread. + * <br>Note: A DSF executor dispatch thread does not necessarily have + * to be exclusive to the executor, it could be shared with + * another event dispatch service, such as the SWT display dispatch thread. + */ +@ThreadSafe +public interface DsfExecutor extends ScheduledExecutorService +{ + /** + * Checks if the thread that this method is called in is the same as the + * executor's dispatch thread. + * @return true if in DSF executor's dispatch thread + */ + public boolean isInExecutorThread(); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/DsfRunnable.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/DsfRunnable.java new file mode 100644 index 00000000000..f315288668a --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/DsfRunnable.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2006 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.concurrent; + + +/** + * A DSF-instrumented alternative to the Runnable interface. + * <p> + * While it is perfectly fine for clients to call the DSF executor with + * an object only implementing the Runnable interface, the DsfRunnable + * contains fields and methods that used for debugging and tracing when + * tracing is enabled. + */ +abstract public class DsfRunnable extends DsfExecutable implements Runnable { +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/IDsfStatusConstants.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/IDsfStatusConstants.java new file mode 100644 index 00000000000..d7be9a7ad67 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/IDsfStatusConstants.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * 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.concurrent; + +/** + * Interface that hold the codes used when reporting status using the DSF + * Request Monitor. + */ +public interface IDsfStatusConstants { + /** + * Error code indicating that the service is in a state which does not allow the + * request to be processed. For example if the client requested target information + * after target was disconnected. + */ + final static int INVALID_STATE = 10001; + + /** + * Error code indicating that client supplied an invalid handle to the service. + * A handle could become invalid after an object it represents is removed from + * the system. + */ + final static int INVALID_HANDLE = 10002; + + /** + * Error code indicating that the client request is not supported/implemented. + */ + final static int NOT_SUPPORTED = 10003; + + /** + * Error code indicating that the request to a sub-service or an external process + * failed. + */ + final static int REQUEST_FAILED = 10004; + + /** + * Error code indicating an unexpected condition in the service, i.e. programming error. + */ + final static int INTERNAL_ERROR = 10005; + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/ImmediateExecutor.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/ImmediateExecutor.java new file mode 100644 index 00000000000..6176514e775 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/ImmediateExecutor.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * 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.concurrent; + +import java.util.concurrent.Executor; + +import org.eclipse.cdt.dsf.internal.DsfPlugin; +import org.eclipse.core.runtime.Platform; + +/** + * Executor that executes a runnable immediately as it is submitted. This + * executor is useful for clients that need to create <code>RequestMonitor</code> + * objects, but which do not have their own executor. + * @see RequestMonitor + */ +public class ImmediateExecutor implements Executor { + + /** + * Debug flag used for tracking runnables that were never executed, + * or executed multiple times. + */ + protected static boolean DEBUG_EXECUTOR = false; + static { + DEBUG_EXECUTOR = DsfPlugin.DEBUG && "true".equals( //$NON-NLS-1$ + Platform.getDebugOption("org.eclipse.cdt.dsf/debug/executor")); //$NON-NLS-1$ + } + + private static ImmediateExecutor fInstance = new ImmediateExecutor(); + + /** + * The default constructor is hidden. {@link #getInstance()} should be + * used instead. + */ + private ImmediateExecutor() {} + + /** + * Returns the singleton instance of ImmediateExecutor. + */ + public static Executor getInstance() { + return fInstance; + } + + public void execute(Runnable command) { + // Check if executable wasn't executed already. + if (DEBUG_EXECUTOR && command instanceof DsfExecutable) { + assert !((DsfExecutable)command).getSubmitted() : "Executable was previously executed."; //$NON-NLS-1$ + ((DsfExecutable)command).setSubmitted(); + } + command.run(); + } +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/Immutable.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/Immutable.java new file mode 100644 index 00000000000..a336b893792 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/Immutable.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2006 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.concurrent; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation idicating that given class is immutable and thus thread-safe. + * This annotation is not automatically inherited by sub-classes, since + * sub-classes need to make sure that they are immutable as well. + * <p> + * Note: the runtime retention policy is there to allow automated testing + * and validation code. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Documented +public @interface Immutable { + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/MultiRequestMonitor.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/MultiRequestMonitor.java new file mode 100644 index 00000000000..ba127835243 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/MultiRequestMonitor.java @@ -0,0 +1,108 @@ +/******************************************************************************* + * 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.concurrent; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executor; + +import org.eclipse.cdt.dsf.internal.DsfPlugin; +import org.eclipse.core.runtime.MultiStatus; + +/** + * Utility class to collect multiple request monitor results of commands + * that are initiated simultaneously. The usage is as follows: + * <pre> + * final MultiRequestMonitor multiRequestMon = new MultiRequestMonitor(fExecutor, null) { + * public void handleCompleted() { + * System.out.println("All complete, errors=" + !getStatus().isOK()); + * } + * }; + * + * for (int i = 0; i < 10; i++) { + * service.call(i, multiRequestMon.addRequestMonitor( + * new RequestMonitor(fExecutor, null) { + * public void handleCompleted() { + * System.out.println(Integer.toString(i) + " complete"); + * multiRequestMon.requestMonitorDone(this); + * } + * })); + * } + * </pre> + */ +public class MultiRequestMonitor<V extends RequestMonitor> extends RequestMonitor { + private List<V> fRequestMonitorList = new LinkedList<V>(); + private Map<V,Boolean> fStatusMap = new HashMap<V,Boolean>(); + private int fDoneCounter; + + public MultiRequestMonitor(Executor executor, RequestMonitor parentRequestMonitor) { + super(executor, parentRequestMonitor); + setStatus(new MultiStatus(DsfPlugin.PLUGIN_ID, 0, "Collective status for set of sub-operations.", null)); //$NON-NLS-1$ + } + + /** + * Adds a new RequestMonitor callback to this tracker's list. + * @param <T> Client-specific class of the RequestMonitor callback, it's used here to avoid an + * unnecessary cast by the client. + * @param rm Request monitor object to add to the tracker + * @return The request monitor that was just added, it allows this method to be used + * inlined in service method calls + */ + public <T extends V> T add(T rm) { + assert !fStatusMap.containsKey(rm); + fRequestMonitorList.add(rm); + fStatusMap.put(rm, false); + fDoneCounter++; + return rm; + } + + /** + * Marks the given RequestMonitor callback as completed. Client implementations of + * the RequestMonitor callback have to call this method in order for the tracker + * to complete. + * <br> + * @param requestMonitor + */ + public synchronized void requestMonitorDone(V requestMonitor) { + if (getStatus() instanceof MultiStatus) { + ((MultiStatus)getStatus()).merge(requestMonitor.getStatus()); + } + assert fStatusMap.containsKey(requestMonitor); + fStatusMap.put(requestMonitor, true); + assert fDoneCounter > 0; + fDoneCounter--; + if (fDoneCounter == 0) { + assert !fStatusMap.containsValue(false); + super.done(); + } + } + + /** + * Returns the list of requested monitors, sorted in order as they were added. + */ + public List<V> getRequestMonitors() { + return fRequestMonitorList; + } + + /** + * Returns true if given monitor is finished. + */ + public boolean isRequestMonitorDone(V rm) { + return fStatusMap.get(rm); + } + + @Override + public String toString() { + return "Multi-RequestMonitor: " + getStatus().toString(); //$NON-NLS-1$ + } +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/Query.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/Query.java new file mode 100644 index 00000000000..eff2c70c5b4 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/Query.java @@ -0,0 +1,220 @@ +/******************************************************************************* + * 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.concurrent; + +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.AbstractQueuedSynchronizer; + +import org.eclipse.core.runtime.CoreException; + + +/** + * A convenience class that allows a client to retrieve data from services + * synchronously from a non-dispatch thread. This class is different from + * a Callable<V> in that it allows the implementation code to calculate + * the result in several dispatches, rather than requiring it to return the + * data at end of Callable#call method. + * <p> + * Usage:<br/> + * <pre> + * class DataQuery extends Query<Data> { + * protected void execute(DataRequestMonitor<Data> rm) { + * rm.setData(fSlowService.getData()); + * rm.done(); + * } + * } + * + * DsfExecutor executor = getExecutor(); + * DataQuery query = new DataQuery(); + * executor.submit(query); + * + * try { + * Data data = query.get(); + * } + * + * </pre> + * <p> + * @see java.util.concurrent.Callable + */ +@ThreadSafe +abstract public class Query<V> extends DsfRunnable + implements Future<V> +{ + /** The synchronization object for this query */ + private final Sync fSync = new Sync(); + + /** + * The Query constructor no longer requires an executor to be specified. + * This executor was used to contruct the DataRequestMonitor argument to the + * {@link #execute(DataRequestMonitor)} method. But a simplification in the + * RequestMonitor object, made this unnecessary. + * @param executor + */ + @Deprecated + public Query(DsfExecutor executor) { + } + + /** + * The no-argument constructor + */ + public Query() {} + + public V get() throws InterruptedException, ExecutionException { return fSync.doGet(); } + + public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + return fSync.doGet(unit.toNanos(timeout)); + } + + /** + * Don't try to interrupt the DSF executor thread, just ignore the request + * if set. + */ + public boolean cancel(boolean mayInterruptIfRunning) { + return fSync.doCancel(); + } + + public boolean isCancelled() { return fSync.doIsCancelled(); } + + public boolean isDone() { return fSync.doIsDone(); } + + + protected void doneException(Throwable t) { + fSync.doSetException(t); + } + + abstract protected void execute(DataRequestMonitor<V> rm); + + public void run() { + if (fSync.doRun()) { + try { + /* + * Create the executor which is going to handle the completion of the + * request monitor. Normally a DSF executor is supplied here which + * causes the request monitor to be invoked in a new dispatch loop. + * But since the query is a synchronization object, it can handle + * the completion of the request in any thread. + * Avoiding the use of a DSF executor is very useful because queries are + * meant to be used by clients calling from non-dispatch thread, and there + * is a chance that a client may execute a query just as a session is being + * shut down. In that case, the DSF executor may throw a + * RejectedExecutionException which would have to be handled by the query. + */ + execute(new DataRequestMonitor<V>(ImmediateExecutor.getInstance(), null) { + @Override + public void handleCompleted() { + if (isSuccess()) fSync.doSet(getData()); + else fSync.doSetException(new CoreException(getStatus())); + } + }); + } catch(Throwable t) { + /* + * Catching the exception here will only work if the exception + * happens within the execute. It will not work in cases when + * the execute submits other runnables, and the other runnables + * encounter the exception. + */ + fSync.doSetException(t); + + /* + * Since we caught the exception, it will not be logged by + * DefaultDsfExecutable.afterExecution(). So log it here. + */ + DefaultDsfExecutor.logException(t); + } + } + } + + @SuppressWarnings("serial") + final class Sync extends AbstractQueuedSynchronizer { + private static final int STATE_RUNNING = 1; + private static final int STATE_DONE = 2; + private static final int STATE_CANCELLED = 4; + + private V fResult; + private Throwable fException; + + private boolean ranOrCancelled(int state) { + return (state & (STATE_DONE | STATE_CANCELLED)) != 0; + } + + @Override + protected int tryAcquireShared(int ignore) { + return doIsDone()? 1 : -1; + } + + @Override + protected boolean tryReleaseShared(int ignore) { + return true; + } + + boolean doIsCancelled() { + return getState() == STATE_CANCELLED; + } + + boolean doIsDone() { + return ranOrCancelled(getState()); + } + + V doGet() throws InterruptedException, ExecutionException { + acquireSharedInterruptibly(0); + if (getState() == STATE_CANCELLED) throw new CancellationException(); + if (fException != null) throw new ExecutionException(fException); + return fResult; + } + + V doGet(long nanosTimeout) throws InterruptedException, ExecutionException, TimeoutException { + if (!tryAcquireSharedNanos(0, nanosTimeout)) throw new TimeoutException(); + if (getState() == STATE_CANCELLED) throw new CancellationException(); + if (fException != null) throw new ExecutionException(fException); + return fResult; + } + + void doSet(V v) { + while(true) { + int s = getState(); + if (ranOrCancelled(s)) return; + if (compareAndSetState(s, STATE_DONE)) break; + } + fResult = v; + releaseShared(0); + } + + void doSetException(Throwable t) { + while(true) { + int s = getState(); + if (ranOrCancelled(s)) return; + if (compareAndSetState(s, STATE_DONE)) break; + } + fException = t; + fResult = null; + releaseShared(0); + } + + boolean doCancel() { + while(true) { + int s = getState(); + if (ranOrCancelled(s)) return false; + if (compareAndSetState(s, STATE_CANCELLED)) break; + } + releaseShared(0); + return true; + } + + boolean doRun() { + return compareAndSetState(0, STATE_RUNNING); + } + } +} + diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/RequestMonitor.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/RequestMonitor.java new file mode 100644 index 00000000000..ac5b466e94e --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/RequestMonitor.java @@ -0,0 +1,401 @@ +/******************************************************************************* + * 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.concurrent; + +import java.util.concurrent.Executor; +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.dsf.internal.DsfPlugin; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.ListenerList; +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.Status; + +/** + * Used to monitor the result of an asynchronous request. Because of the + * asynchronous nature of DSF code, a very large number of methods needs to + * signal the result of an operation through a call-back. This class is the base + * class for such call backs. + * <p> + * The intended use of this class, is that a client who is calling an asynchronous + * method, will sub-class RequestMonitor, and implement the method {@link #handleCompleted()}, + * or any of the other <code>handle...</code> methods, in order to interpret the + * results of the request. The object implementing the asynchronous method is required + * to call the {@link #done()} method on the request monitor object that it received + * as an argument. + * </p> + * <p> + * The severity of the {@link IStatus> returned by #getStatus() can be used to + * determine the success or failure of the asynchronous operation. By convention + * the error codes returned by asynchronous method should be interpreted as follows: + * <ul> + * <li>OK and INFO - Result is a success. In DataRequestMonitor, getData() should + * return a value.</li> + * <li>WARNING - Acceptable error condition (getData() may return null). Where for + * example user tried to retrieve variable data, but the program resumed in the + * mean time and an event will be generated shortly which will clear the variables + * view.</li> + * <li>ERROR - An error condition that should probably be reported to the user.</li> + * <li>CANCEL - The request was canceled, and the asynchronous method was not + * completed.</li> + * </ul> + * </p> + * <p> + * The RequestMonitor constructor accepts an optional "parent" request monitor. If a + * parent monitor is specified, it will automatically be invoked by this monitor when + * the request is completed. The parent option is useful when implementing a method + * which is asynchronous (and accepts a request monitor as an argument) and which itself + * calls another asynchronous method to complete its operation. For example, in the + * request monitor implementation below, the implementation only needs to override + * <code>handleOK()</code>, because the base implementation will handle notifying the + * parent <code>rm</code> in case the <code>getIngredients()</code> call fails. + * <pre> + * public void createCupCakes(final DataRequestMonitor<CupCake[]> rm) { + * getIngredients(new DataRequestMonitor<Ingredients>(fExecutor, rm) { + * public void handleOK() { + * rm.setData( new CupCake(getData().getFlour(), getData().getSugar(), + * getData().getBakingPowder())); + * rm.done(); + * } + * }); + * } + * </pre> + * </p> + */ +@ThreadSafe +public class RequestMonitor { + + /** + * Interface used by RequestMonitor to notify when a given request monitor + * is canceled. + * + * @see RequestMonitor + */ + public static interface ICanceledListener { + + /** + * Called when the given request monitor is canceled. + */ + public void requestCanceled(RequestMonitor rm); + } + + /** + * The executor that will be used in order to invoke the handler of the results + * of the request. + */ + private final Executor fExecutor; + + /** + * The request monitor which was used to call into the method that created this + * monitor. + */ + private final RequestMonitor fParentRequestMonitor; + + private ListenerList fCancelListeners; + + /** + * Status + */ + private IStatus fStatus = Status.OK_STATUS; + private boolean fCanceled = false; + private boolean fDone = false; + + /** + * Constructor with an optional parent monitor. + * @param executor This executor will be used to invoke the runnable that + * will allow processing the completion code of this request monitor. + * @param parentRequestMonitor The optional parent request monitor to be invoked by + * default when this request completes. Parameter may be null. + */ + public RequestMonitor(Executor executor, RequestMonitor parentRequestMonitor) { + fExecutor = executor; + fParentRequestMonitor = parentRequestMonitor; + + // If the parent rm is not null, add ourselves as a listener so that + // this request monitor will automatically be canceled when the parent + // is canceled. + if (fParentRequestMonitor != null) { + fParentRequestMonitor.addCancelListener( + new ICanceledListener() { + public void requestCanceled(RequestMonitor rm) { + cancel(); + } + }); + } + } + + /** + * Sets the status of the result of the request. If status is OK, this + * method does not need to be called. + */ + public synchronized void setStatus(IStatus status) { + assert isCanceled() || status.getSeverity() != IStatus.CANCEL; + fStatus = status; + } + + /** Returns the status of the completed method. */ + public synchronized IStatus getStatus() { + if (isCanceled()) { + return Status.CANCEL_STATUS; + } + return fStatus; + } + + /** + * Sets this request monitor as canceled and calls the cancel listeners if any. + * <p> + * Note: Calling cancel() does not automatically complete the RequestMonitor. + * The asynchronous call still has to call done(). + * </p> + * <p> + * Note: logically a request should only be canceled by the client that issued + * the request in the first place. After a request is canceled, the method + * that is fulfilling the request may call {@link #setStatus(IStatus)} with + * severity of <code>IStatus.CANCEL</code> to indicate that it recognized that + * the given request was canceled and it did not perform the given operation. + * </p> + */ + public void cancel() { + Object[] listeners = null; + synchronized (this) { + // Check to make sure the request monitor wasn't previously canceled. + if (!fCanceled) { + fCanceled = true; + if (fCancelListeners != null) { + listeners = fCancelListeners.getListeners(); + } + } + } + + // Call the listeners outside of a synchronized section to reduce the + // risk of deadlocks. + if (listeners != null) { + for (Object listener : listeners) { + ((ICanceledListener)listener).requestCanceled(this); + } + } + } + + /** + * Returns whether the request was canceled. Even if the request is + * canceled by the client, the implementor handling the request should + * still call {@link #done()} in order to complete handling + * of the request monitor. + */ + public synchronized boolean isCanceled() { + return fCanceled || (fParentRequestMonitor != null && fParentRequestMonitor.isCanceled()); + } + + /** + * Adds the given listener to list of listeners that are notified when this + * request monitor is canceled. + */ + public synchronized void addCancelListener(ICanceledListener listener) { + if (fCancelListeners == null) { + fCancelListeners = new ListenerList(); + } + fCancelListeners.add(listener); + } + + /** + * Removes the given listener from the list of listeners that are notified + * when this request monitor is canceled. + */ + public synchronized void removeCancelListener(ICanceledListener listener) { + if (fCancelListeners != null) { + fCancelListeners.remove(listener); + } + } + + /** + * Marks this request as completed. Once this method is called, the + * monitor submits a runnable to the DSF Executor to call the + * <code>handle...</code> methods. + * <p> + * Note: This method should be called once and only once, for every request + * issued. Even if the request was canceled. + * </p> + */ + public synchronized void done() { + if (fDone) { + throw new IllegalStateException("RequestMonitor: " + this + ", done() method called more than once"); //$NON-NLS-1$//$NON-NLS-2$ + } + fDone = true; + try { + fExecutor.execute(new DsfRunnable() { + public void run() { + RequestMonitor.this.handleCompleted(); + } + @Override + public String toString() { + return "Completed: " + RequestMonitor.this.toString(); //$NON-NLS-1$ + } + }); + } catch (RejectedExecutionException e) { + handleRejectedExecutionException(); + } + } + + @Override + public String toString() { + return "RequestMonitor (" + super.toString() + "): " + getStatus().toString(); //$NON-NLS-1$ //$NON-NLS-2$ + } + + /** + * Checks whether the given request monitor completed with success or + * failure result. If the request monitor was canceled it is considered + * that it failed, regardless of the status. + */ + public boolean isSuccess() { + return !isCanceled() && getStatus().getSeverity() <= IStatus.INFO; + } + + /** + * Default handler for the completion of a request. The implementation + * calls {@link #handleSuccess()} if the request succeeded, and calls + * {@link #handleFailure()} or cancel otherwise. + * <br> + * Note: Sub-classes may override this method. + */ + @ConfinedToDsfExecutor("fExecutor") + protected void handleCompleted() { + if (isSuccess()) { + handleSuccess(); + } else { + handleFailure(); + } + } + + /** + * Default handler for a successful the completion of a request. If this + * monitor has a parent monitor that was configured by the constructor, that + * parent monitor is notified. Otherwise this method does nothing. + * {@link #handleFailure()} or cancel otherwise. + * <br> + * Note: Sub-classes may override this method. + */ + @ConfinedToDsfExecutor("fExecutor") + protected void handleSuccess() { + if (fParentRequestMonitor != null) { + fParentRequestMonitor.done(); + } + } + + /** + * The default implementation of a cancellation or an error result of a + * request. The implementation delegates to {@link #handleCancel()} and + * {@link #handleErrorOrWarning()} as needed. + * <br> + * Note: Sub-classes may override this method. + */ + @ConfinedToDsfExecutor("fExecutor") + protected void handleFailure() { + assert !isSuccess(); + + if (isCanceled()) { + handleCancel(); + } else { + if (getStatus().getSeverity() == IStatus.CANCEL) { + DsfPlugin.getDefault().getLog().log(new Status( + IStatus.ERROR, DsfPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Request monitor: '" + this + "' resulted in a cancel status: " + getStatus() + ", even though the request is not set to cancel.", null)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + handleErrorOrWarning(); + } + } + + /** + * The default implementation of an error or warning result of a request. + * The implementation delegates to {@link #handleError()} and + * {@link #handleWarning()} as needed. + * <br> + * Note: Sub-classes may override this method. + */ + @ConfinedToDsfExecutor("fExecutor") + protected void handleErrorOrWarning() { + if (getStatus().getSeverity() == IStatus.ERROR) { + handleError(); + } else { + handleWarning(); + } + } + + /** + * The default implementation of an error result of a request. If this + * monitor has a parent monitor that was configured by the constructor, that + * parent monitor is configured with a new status containing this error. + * Otherwise the error is logged. + * <br> + * Note: Sub-classes may override this method. + */ + @ConfinedToDsfExecutor("fExecutor") + protected void handleError() { + if (fParentRequestMonitor != null) { + fParentRequestMonitor.setStatus(getStatus()); + fParentRequestMonitor.done(); + } else { + MultiStatus logStatus = new MultiStatus(DsfPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Request for monitor: '" + toString() + "' resulted in an error.", null); //$NON-NLS-1$ //$NON-NLS-2$ + logStatus.merge(getStatus()); + DsfPlugin.getDefault().getLog().log(logStatus); + } + } + + /** + * The default implementation of an error result of a request. If this + * monitor has a parent monitor that was configured by the constructor, that + * parent monitor is configured with a new status containing this warning. + * Otherwise the warning is logged. + * <br> + * Note: Sub-classes may override this method. + */ + @ConfinedToDsfExecutor("fExecutor") + protected void handleWarning() { + if (fParentRequestMonitor != null) { + fParentRequestMonitor.setStatus(getStatus()); + fParentRequestMonitor.done(); + } + } + + /** + * Default handler for a canceled the completion of a request. If this + * monitor has a parent monitor that was configured by the constructor, that + * parent monitor is notified. Otherwise this method does nothing. + * <br> + * Note: Sub-classes may override this method. + */ + @ConfinedToDsfExecutor("fExecutor") + protected void handleCancel() { + if (fParentRequestMonitor != null) { + if (getStatus().getSeverity() == IStatus.CANCEL && !fParentRequestMonitor.isCanceled()) { + fParentRequestMonitor.setStatus(new Status( + IStatus.ERROR, DsfPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Sub-request " + toString() + " was canceled and not handled.'", null)); //$NON-NLS-1$ //$NON-NLS-2$ + } else { + fParentRequestMonitor.setStatus(getStatus()); + } + fParentRequestMonitor.done(); + } + } + + /** + * Default handler for when the executor supplied in the constructor + * rejects the runnable that is submitted invoke this request monitor. + * This usually happens only when the executor is shutting down. + */ + protected void handleRejectedExecutionException() { + MultiStatus logStatus = new MultiStatus(DsfPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Request for monitor: '" + toString() + "' resulted in a rejected execution exception.", null); //$NON-NLS-1$ //$NON-NLS-2$ + logStatus.merge(getStatus()); + if (fParentRequestMonitor != null) { + fParentRequestMonitor.setStatus(logStatus); + fParentRequestMonitor.done(); + } else { + DsfPlugin.getDefault().getLog().log(logStatus); + } + } +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/RequestMonitorWithProgress.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/RequestMonitorWithProgress.java new file mode 100644 index 00000000000..7cdd2719799 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/RequestMonitorWithProgress.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * 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.concurrent; + +import java.util.concurrent.Executor; + +import org.eclipse.core.runtime.IProgressMonitor; + +/** + * A request monitor which uses a progress monitor as a parent. When the parent + * progress monitor is canceled, the request monitor will also be canceled, + * although the cancellation listeners will not be called. + * + * @since 1.1 + */ +public class RequestMonitorWithProgress extends RequestMonitor { + + private final IProgressMonitor fProgressMonitor; + + public RequestMonitorWithProgress(Executor executor, IProgressMonitor progressMonitor) { + super(executor, null); + fProgressMonitor = progressMonitor; + } + + public IProgressMonitor getProgressMonitor() { + return fProgressMonitor; + } + + @Override + public synchronized boolean isCanceled() { + return super.isCanceled() || fProgressMonitor.isCanceled(); + } +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/Sequence.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/Sequence.java new file mode 100644 index 00000000000..334a7c69e4e --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/Sequence.java @@ -0,0 +1,682 @@ +/******************************************************************************* + * 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 + * Nokia - added StepWithProgress. Oct, 2008 + *******************************************************************************/ +package org.eclipse.cdt.dsf.concurrent; + +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.AbstractQueuedSynchronizer; + +import org.eclipse.cdt.dsf.concurrent.RequestMonitor.ICanceledListener; +import org.eclipse.cdt.dsf.internal.DsfPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubProgressMonitor; + +/** + * Convenience class for implementing a series of commands that need to be + * executed asynchronously. + * <p> + * Certain complex tasks require multiple commands to be executed in a chain, + * because for example result of one command is used as input into another + * command. The typical DSF pattern of solving this problem is the following: + * <li> + * <br> 1. original caller passes a RequestMonitor callback to a method and invokes it + * <br> 2. the method is executed by a subsystem + * <br> 3. when the method is finished it calls another method and passes + * the original callback to it + * <br> 4. steps 2-3 are repeated number of times + * <br> 5. when the last method in a chain is executed, it submits the original + * RequestMonitor callback + * </li> + * <p> + * This pattern is very useful in itself, but it proves very difficult to follow + * because the methods can be scattered accross many classes and systems. Also + * if progress reporting, cancellability, and roll-back ability is required, it + * has to be re-implemented every time. The Sequence class tries to address + * this problem by containing this pattern in a single class. + */ +@ThreadSafe +abstract public class Sequence extends DsfRunnable implements Future<Object> { + + /** + * The abstract class that each step has to implement. + */ + abstract public static class Step { + private Sequence fSequence; + + /** + * Sets the sequence that this step belongs to. It is only accessible + * by the sequence itself, and is not meant to be called by sequence + * sub-classes. + */ + void setSequence(Sequence sequence) { fSequence = sequence; } + + /** Returns the sequence that this step is running in. */ + public Sequence getSequence() { return fSequence; } + + /** + * Executes the step. Overriding classes should perform the + * work in this method. + * @param rm Result token to submit to executor when step is finished. + */ + public void execute(RequestMonitor rm) { + rm.done(); + } + + /** + * Roll back gives the step implementation a chance to undo the + * operation that was performed by execute(). + * <br> + * Note if the {@link #execute(RequestMonitor)} call completes with a + * non-OK status, then rollBack will not be called for that step. + * Instead it will be called for the previous step. + * @param rm Result token to submit to executor when rolling back the step is finished. + */ + public void rollBack(RequestMonitor rm) { + rm.done(); + } + + /** + * Returns the number of progress monitor ticks corresponding to this + * step. + */ + public int getTicks() { return 1; } + + /** + * Task name for this step. This will be displayed in the label of the + * progress monitor of the owner sequence. + * + * @return name of the task carried out by the step, can be + * <code>null</code>, in which case the overall task name will be used. + * + * @since 1.1 + */ + public String getTaskName() { + return ""; //$NON-NLS-1$ + } + } + + /** + * A step that will report execution progress by itself on the progress + * monitor of the owner sequence.<br> + * <br> + * Note we don't offer a rollBack(RequestMonitor, IProgressMonitor) as we + * don't want end user to be able to cancel the rollback. + * + * @since 1.1 + */ + abstract public static class StepWithProgress extends Step { + + @Override + // don't allow subclass to implement this by "final" it. + final public void execute(RequestMonitor rm) { + assert false : "execute(RequestMonitor rm, IProgressMonitor pm) should be called instead"; //$NON-NLS-1$ + } + + /** + * Execute the step with a progress monitor. Note the given progress + * monitor is a sub progress monitor of the owner sequence which is + * supposed to be fully controlled by the step. Namely the step should + * call beginTask() and done() of the monitor. + * + * @param rm + * @param pm + */ + public void execute(RequestMonitor rm, IProgressMonitor pm) { + rm.done(); + pm.done(); + } + } + + /** The synchronization object for this future */ + final Sync fSync = new Sync(); + + /** + * Executor that this sequence is running in. It is used by the sequence + * to submit the runnables for steps, and for submitting the result. + */ + final private DsfExecutor fExecutor; + + /** + * Result callback to invoke when the sequence is finished. Intended to + * be used when the sequence is created and invoked from the executor + * thread. Otherwise, the {@link Future#get()} method is the appropriate + * method of retrieving the result. + */ + final private RequestMonitor fRequestMonitor; + + /** Status indicating the success/failure of the test. Used internally only. */ + @ConfinedToDsfExecutor("getExecutor") + private IStatus fStatus = Status.OK_STATUS; + + @ConfinedToDsfExecutor("getExecutor") + private int fCurrentStepIdx = 0; + + /** Task name for this sequence used with the progress monitor */ + final private String fTaskName; + + /** Task name used when the sequence is being rolled back. */ + final private String fRollbackTaskName; + + final private IProgressMonitor fProgressMonitor; + + /** Convenience constructor with limited arguments. */ + public Sequence(DsfExecutor executor) { + this(executor, new NullProgressMonitor(), "", "", null); //$NON-NLS-1$ //$NON-NLS-2$ + } + + /** + * Creates a sequence with a request monitor. If the client cancels the + * request monitor, then the request monitors in the + * {@link Step#execute(RequestMonitor)} + * implementations will immediately call the cancel listeners to notify. + * + * @param executor The DSF executor which will be used to invoke all + * steps. + * @param rm The request monitor which will be invoked when the sequence + * is completed. + */ + public Sequence(DsfExecutor executor, RequestMonitor rm) { + this(executor, new NullProgressMonitor(), "", "", rm); //$NON-NLS-1$ //$NON-NLS-2$ + } + + /** + * Creates a sequence with a progress monitor. If the progress monitor is + * canceled, then request monitors in the + * {@link Step#execute(RequestMonitor)} implementations will need to call + * rm.isCanceled() to discover the cancellation. + * @param executor The DSF executor which will be used to invoke all + * steps. + * @param pm Progress monitor for monitoring this sequence. + * @param taskName Name that will be used in call to + * {@link IProgressMonitor#beginTask(String, int)},when the task is + * started. + * @param rollbackTaskName Name that will be used in call to + * {@link IProgressMonitor#subTask(String)} if the task is canceled or + * aborted. + * + * @since 1.1 + */ + public Sequence(DsfExecutor executor, IProgressMonitor pm, String taskName, String rollbackTaskName) { + this(executor, pm, taskName, rollbackTaskName, new RequestMonitorWithProgress(ImmediateExecutor.getInstance(), pm)); + } + + /** + * Creates a sequence with a request monitor that includes a progress + * monitor. + * @param executor The DSF executor which will be used to invoke all + * steps. + * @param rm The request monitor containing the progress monitor + * @param taskName Name that will be used in call to + * {@link IProgressMonitor#beginTask(String, int)},when the task is + * started. + * @param rollbackTaskName Name that will be used in call to + * {@link IProgressMonitor#subTask(String)} if the task is canceled or + * aborted. + * + * @since 1.1 + */ + public Sequence(DsfExecutor executor, RequestMonitorWithProgress rm, String taskName, String rollbackTaskName) { + this(executor, rm.getProgressMonitor(), taskName, rollbackTaskName, rm); + } + + /** + * Constructor that initialized the steps and the result callback. + * @param executor The DSF executor which will be used to invoke all + * steps. + * @param pm Progress monitor for monitoring this sequence. This + * parameter cannot be null. + * @param taskName Name that will be used in call to + * {@link IProgressMonitor#beginTask(String, int)},when the task is + * started. + * @param rollbackTaskName Name that will be used in call to + * {@link IProgressMonitor#subTask(String)} if the task is canceled or + * aborted. + * @param Result that will be submitted to executor when sequence is + * finished. Can be null if calling from non-executor thread and using + * {@link Future#get()} method to wait for the sequence result. + * + * @deprecated This constructor should not be used because it creates a + * potential ambiguity when one of the two monitors is canceled. + */ + @Deprecated + public Sequence(DsfExecutor executor, IProgressMonitor pm, String taskName, String rollbackTaskName, RequestMonitor rm) { + fExecutor = executor; + fProgressMonitor = pm; + fTaskName = taskName; + fRollbackTaskName = rollbackTaskName; + fRequestMonitor = rm; + + if (fRequestMonitor != null) { + fRequestMonitor.addCancelListener(new ICanceledListener() { + public void requestCanceled(RequestMonitor rm) { + fSync.doCancel(); + } + }); + } + } + + /** + * Returns the steps to be executed. It is up to the deriving class to + * supply the steps and to ensure that the list of steps will not be + * modified after the sequence is constructed. + * <p> + * Steps are purposely not accepted as part of the DsfConstructor, in + * order to allow deriving classes to create the steps as a field. And a + * setSteps() method is not provided, to guarantee that the steps will not + * be modified once set (perhaps this is a bit paranoid, but oh well). + */ + abstract public Step[] getSteps(); + + + /** Returns the DSF executor for this sequence */ + public DsfExecutor getExecutor() { return fExecutor; } + + /** + * Returns the RequestMonitor callback that is registered with the Sequence + */ + public RequestMonitor getRequestMonitor() { return fRequestMonitor; } + + /** + * The get method blocks until sequence is complete, but always returns null. + * @see java.concurrent.Future#get + */ + public Object get() throws InterruptedException, ExecutionException { + fSync.doGet(); + return null; + } + + /** + * The get method blocks until sequence is complete or until timeout is + * reached, but always returns null. + * @see java.concurrent.Future#get + */ + public Object get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + fSync.doGet(); + return null; + } + + /** + * Don't try to interrupt the DSF executor thread, just ignore the request + * if set. + * <p>If a request monitor was specified when creating a sequence, that + * request monitor will be canceled by this method as well. The client + * can also use the request monitor's cancel method to cancel the sequence. + * + * @see RequestMonitor#cancel() + */ + public boolean cancel(boolean mayInterruptIfRunning) { + // Cancel the request monitor first, to avoid a situation where + // the request monitor is not canceled but the status is set + // to canceled. + if (fRequestMonitor != null) { + fRequestMonitor.cancel(); + } + return fSync.doCancel(); + } + + public boolean isCancelled() { return fSync.doIsCancelled(); } + + public boolean isDone() { return fSync.doIsDone(); } + + + public void run() { + // Change the state to running. + if (fSync.doRun()) { + // Set the reference to this sequence in each step. + int totalTicks = 0; + for (Step step : getSteps()) { + step.setSequence(this); + totalTicks += step.getTicks(); + } + + // Set the task name + if (fTaskName != null) { + fProgressMonitor.beginTask(fTaskName, totalTicks); + } + + // Call the first step + executeStep(0); + } else { + fSync.doFinish(); + } + } + + /** + * To be called only by the step implementation, Tells the sequence to + * submit the next step. + */ + private void executeStep(int nextStepIndex) { + /* + * At end of each step check progress monitor to see if it's cancelled. + * If progress monitor is cancelled, mark the whole sequence as + * cancelled. + */ + if (fProgressMonitor.isCanceled()) { + cancel(false); + } + + /* + * If sequence was cencelled during last step (or before the sequence + * was ever executed), start rolling back the execution. + */ + if (isCancelled()) { + cancelExecution(); + return; + } + + /* + * Check if we've reached the last step. Note that if execution was + * cancelled during the last step (and thus the sequence is + * technically finished, since it was cancelled it will be rolled + * back. + */ + if (nextStepIndex >= getSteps().length) { + finish(); + return; + } + + // Proceed with executing next step. + fCurrentStepIdx = nextStepIndex; + try { + Step currentStep = getSteps()[fCurrentStepIdx]; + final boolean stepControlsProgress = (currentStep instanceof StepWithProgress); + + RequestMonitor rm = new RequestMonitor(fExecutor, fRequestMonitor) { + final private int fStepIdx = fCurrentStepIdx; + + @Override + public void handleSuccess() { + // Check if we're still the correct step. + assert fStepIdx == fCurrentStepIdx; + if (!stepControlsProgress) { + // then sequence handles the progress report. + fProgressMonitor.worked(getSteps()[fStepIdx].getTicks()); + } + executeStep(fStepIdx + 1); + } + + @Override + protected void handleCancel() { + Sequence.this.cancel(false); + cancelExecution(); + }; + + @Override + protected void handleErrorOrWarning() { + abortExecution(getStatus()); + }; + + @Override + public String toString() { + return "Sequence \"" + fTaskName + "\", result for executing step #" + fStepIdx + " = " + getStatus(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + }; + + fProgressMonitor.subTask(currentStep.getTaskName()); + + if (stepControlsProgress) { + + // Create a sub-monitor that will be controlled by the step. + SubProgressMonitor subMon = new SubProgressMonitor(fProgressMonitor, currentStep.getTicks(), + SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK); + + ((StepWithProgress) currentStep).execute(rm, subMon); + } else { // regular Step + currentStep.execute(rm); + } + + } catch (Throwable t) { + /* + * Catching the exception here will only work if the exception + * happens within the execute method. It will not work in cases + * when the execute submits other runnables, and the other runnables + * encounter the exception. + */ + abortExecution(new Status( + IStatus.ERROR, DsfPlugin.PLUGIN_ID, 0, + "Unhandled exception when executing Sequence " + this + ", step #" + fCurrentStepIdx, //$NON-NLS-1$ //$NON-NLS-2$ + t)); + + /* + * Since we caught the exception, it will not be logged by + * DefaultDsfExecutable.afterExecution(). So log it here. + */ + DefaultDsfExecutor.logException(t); + } + } + + /** + * To be called only by the step implementation. Tells the sequence to + * roll back next step. + */ + private void rollBackStep(int stepIdx) { + // If we reach before step 0, finish roll back. + if (stepIdx < 0) { + finish(); + return; + } + + // Proceed with rolling back given step. + fCurrentStepIdx = stepIdx; + try { + getSteps()[fCurrentStepIdx].rollBack(new RequestMonitor(fExecutor, null) { + final private int fStepIdx = fCurrentStepIdx; + @Override + public void handleCompleted() { + // Check if we're still the correct step. + assert fStepIdx == fCurrentStepIdx; + + // Proceed to the next step. + if (isSuccess()) { + // NOTE: The getTicks() is ticks for executing the step, + // not for rollBack, + // though it does not really hurt to use it here. + fProgressMonitor.worked(getSteps()[fStepIdx].getTicks()); + + rollBackStep(fStepIdx - 1); + } else { + abortRollBack(getStatus()); + } + } + @Override + public String toString() { + return "Sequence \"" + fTaskName + "\", result for rolling back step #" + fStepIdx + " = " + getStatus(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + }); + } catch(Throwable t) { + /* + * Catching the exception here will only work if the exception + * happens within the execute method. It will not work in cases + * when the execute submits other runnables, and the other runnables + * encounter the exception. + */ + abortRollBack(new Status( + IStatus.ERROR, DsfPlugin.PLUGIN_ID, 0, + "Unhandled exception when rolling back Sequence " + this + ", step #" + fCurrentStepIdx, //$NON-NLS-1$ //$NON-NLS-2$ + t)); + + /* + * Since we caught the exception, it will not be logged by + * DefaultDsfExecutable.afterExecution(). So log it here. + */ + DefaultDsfExecutor.logException(t); + } + } + + /** + * Tells the sequence that its execution is to be aborted and it + * should start rolling back the sequence as if it was cancelled by user. + */ + private void cancelExecution() { + if (fRollbackTaskName != null) { + fProgressMonitor.subTask(fRollbackTaskName); + } + fStatus = new Status(IStatus.CANCEL, DsfPlugin.PLUGIN_ID, -1, "Sequence \"" + fTaskName + "\" cancelled.", null); //$NON-NLS-1$ //$NON-NLS-2$ + if (fRequestMonitor != null) { + fRequestMonitor.setStatus(fStatus); + } + + /* + * No need to call fSync, it should have been taken care of by + * Future#cancel method. + * + * Note that we're rolling back starting with the current step, + * because the current step was fully executed. This is unlike + * abortExecution() where the current step caused the roll-back. + */ + rollBackStep(fCurrentStepIdx); + } + + /** + * Tells the sequence that its execution is to be aborted and it + * should start rolling back the sequence as if it was cancelled by user. + */ + private void abortExecution(final IStatus error) { + if (fRollbackTaskName != null) { + fProgressMonitor.subTask(fRollbackTaskName); + } + fStatus = error; + if (fRequestMonitor != null) { + fRequestMonitor.setStatus(error); + } + fSync.doAbort(new CoreException(error)); + + // Roll back starting with previous step, since current step failed. + rollBackStep(fCurrentStepIdx - 1); + } + + /** + * Tells the sequence that that is rolling back, to abort roll back, and + * notify the clients. + */ + private void abortRollBack(final IStatus error) { + if (fRollbackTaskName != null) { + fProgressMonitor.subTask(fRollbackTaskName); + } + + /* + * Compose new status based on previous status information and new + * error information. + */ + MultiStatus newStatus = + new MultiStatus(DsfPlugin.PLUGIN_ID, error.getCode(), + "Sequence \"" + fTaskName + "\" failed while rolling back.", null); //$NON-NLS-1$ //$NON-NLS-2$ + newStatus.merge(error); + newStatus.merge(fStatus); + fStatus = newStatus; + + if (fRequestMonitor != null) { + fRequestMonitor.setStatus(newStatus); + } + + finish(); + } + + private void finish() { + if (fRequestMonitor != null) fRequestMonitor.done(); + fSync.doFinish(); + } + + @SuppressWarnings("serial") + final class Sync extends AbstractQueuedSynchronizer { + private static final int STATE_RUNNING = 1; + private static final int STATE_FINISHED = 2; + private static final int STATE_ABORTING = 4; + private static final int STATE_ABORTED = 8; + private static final int STATE_CANCELLING = 16; + private static final int STATE_CANCELLED = 32; + + private Throwable fException; + + private boolean isFinished(int state) { + return (state & (STATE_FINISHED | STATE_CANCELLED | STATE_ABORTED)) != 0; + } + + @Override + protected int tryAcquireShared(int ignore) { + return doIsDone()? 1 : -1; + } + + @Override + protected boolean tryReleaseShared(int ignore) { + return true; + } + + boolean doIsCancelled() { + int state = getState(); + return (state & (STATE_CANCELLING | STATE_CANCELLED)) != 0; + } + + boolean doIsDone() { + return isFinished(getState()); + } + + void doGet() throws InterruptedException, ExecutionException { + acquireSharedInterruptibly(0); + if (getState() == STATE_CANCELLED) throw new CancellationException(); + if (fException != null) throw new ExecutionException(fException); + } + + void doGet(long nanosTimeout) throws InterruptedException, ExecutionException, TimeoutException { + if (!tryAcquireSharedNanos(0, nanosTimeout)) throw new TimeoutException(); + if (getState() == STATE_CANCELLED) throw new CancellationException(); + if (fException != null) throw new ExecutionException(fException); + } + + void doAbort(Throwable t) { + while(true) { + int s = getState(); + if (isFinished(s)) return; + if (compareAndSetState(s, STATE_ABORTING)) break; + } + fException = t; + } + + boolean doCancel() { + while(true) { + int s = getState(); + if (isFinished(s)) return false; + if (s == STATE_ABORTING) return false; + if (compareAndSetState(s, STATE_CANCELLING)) break; + } + return true; + } + + void doFinish() { + while(true) { + int s = getState(); + if (isFinished(s)) return; + if (s == STATE_ABORTING) { + if (compareAndSetState(s, STATE_ABORTED)) break; + } else if (s == STATE_CANCELLING) { + if (compareAndSetState(s, STATE_CANCELLED)) break; + } else { + if (compareAndSetState(s, STATE_FINISHED)) break; + } + } + releaseShared(0); + } + + boolean doRun() { + return compareAndSetState(0, STATE_RUNNING); + } + } + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/StackTraceWrapper.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/StackTraceWrapper.java new file mode 100644 index 00000000000..b48d69afa7b --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/StackTraceWrapper.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2006, 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.concurrent; + +/** + * Untility class for easy pretty-printing stack traces. Local to the + * concurrent package. + */ +@Immutable +class StackTraceWrapper { + final StackTraceElement[] fStackTraceElements; + + StackTraceWrapper(StackTraceElement[] elements) { fStackTraceElements = elements; } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(fStackTraceElements.length * 30); + for (int i = 0; i < fStackTraceElements.length && i < 10; i++) { + builder.append(fStackTraceElements[i]); + if (i < fStackTraceElements.length && i < 10) builder.append("\n at "); //$NON-NLS-1$ + } + return builder.toString(); + } +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/ThreadSafe.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/ThreadSafe.java new file mode 100644 index 00000000000..5ebcdec963a --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/ThreadSafe.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2006 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.concurrent; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation idicating that given package, class, method, or field can be + * access safely from any thread. If declared on package or type, a field + * or method could still be declared with an annotation indicating that it's + * not thread-safe. + * <p> + * Note: the runtime retention policy is there to allow automated testing + * and validation code. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.PACKAGE, ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR}) +@Inherited +@Documented +public @interface ThreadSafe { + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/ThreadSafeAndProhibitedFromDsfExecutor.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/ThreadSafeAndProhibitedFromDsfExecutor.java new file mode 100644 index 00000000000..4f3188b468d --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/ThreadSafeAndProhibitedFromDsfExecutor.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2006 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.concurrent; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation idicating that given package, class, method, can be accessed on + * any thread, except on the dispatch thread of given DsfExecutor. + * <br> This restriction is desirable if it is expected that the implementation + * behavior is to block the calling thread and execute a transaction using an + * executor. In this situation, if the call is made on the executor's dispach + * thread, the execution would dead-lock. + * <br> + * If declared on package or type, a field or method could still be declared + * with an annotation indicating that it's thread-safe. + * <p> + * Note: the runtime retention policy is there to allow automated testing + * and validation code. + * + * @param value The value indicates the method to use to obtain the executor. + * It should be null if it cannot be determined from the given object. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.PACKAGE, ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR}) +@Inherited +@Documented +public @interface ThreadSafeAndProhibitedFromDsfExecutor { + String value(); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/AbstractDMContext.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/AbstractDMContext.java new file mode 100644 index 00000000000..fd96e0d99f3 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/AbstractDMContext.java @@ -0,0 +1,141 @@ +/******************************************************************************* + * Copyright (c) 2006, 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.datamodel; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.service.IDsfService; +import org.eclipse.core.runtime.PlatformObject; + +/** + * Base implementation of the IDMContext interface. There are two pieces of + * functionality here: <br> + * 1) The {@link #getAdapter(Class)} implementation which retrieves model + * adapters registered with the session. <br> + * 2) Methods to help compare DM Contexts. <br> + * <p> + * Note: The {@link #equals(Object)} and {@link #hashCode()} methods are + * made abstract to force the deriving classes to provide a proper + * implementation. Data Model Context objects are meant to be used as handles, + * therefore a proper equals implementation is critical. + * </p> + * @param <V> Data Model data type that this context is for. + */ +@Immutable +abstract public class AbstractDMContext extends PlatformObject + implements IDMContext +{ + private final String fSessionId; + private final IDMContext[] fParents; + + /** + * Main constructor provides all data needed to implement the IModelContext + * interface. + */ + public AbstractDMContext(String sessionId, IDMContext[] parents) { + fSessionId = sessionId; + fParents = parents; + for (IDMContext parent : parents) { + assert(parent != null); + } + } + + /** Convenience constructor */ + public AbstractDMContext(IDsfService service, IDMContext[] parents) { + this(service.getSession().getId(), parents); + } + + /** + * Should be used by the deriving class to compare the basic context object + * information. + * @param other the other service to compare to + * @return true if contexts are equal + */ + protected boolean baseEquals(Object other) { + if (other == null) return false; + if ( !(other.getClass().equals(getClass()))) return false; + IDMContext otherCtx = (IDMContext)other; + return getSessionId().equals(otherCtx.getSessionId()) && + areParentsEqual(otherCtx.getParents()); + } + + private boolean areParentsEqual(IDMContext[] otherParents) { + if ( !(fParents.length == otherParents.length) ) return false; + for (int i = 0; i < fParents.length; i++) { + if (!fParents[i].equals(otherParents[i])) { + return false; + } + } + return true; + } + + protected int baseHashCode() { + int parentsHash = 0; + for (Object parent : getParents()) { + parentsHash += parent.hashCode(); + } + return getSessionId().hashCode() + parentsHash; + } + + protected String baseToString() { + StringBuffer retVal = new StringBuffer(); + for (IDMContext parent : fParents) { + retVal.append(parent); + } + return retVal.toString(); + } + + public String getSessionId() { return fSessionId; } + public IDMContext[] getParents() { return fParents; } + + /** + * Overrides the standard platform getAdapter to provide session-specific + * adapters. + * <p> + * ModelContext is intended to be used in views, which call the + * contexts.getAdapter() method to retrieve model-specific content and + * label providers. But since many different sessions could be active + * at the same time, each requiring different content providers, the + * standard platform <code>IAdapterManager</code> is not sufficient in + * handling adapters for the model context object. This is because + * <code>IAdapterManager</code> uses only the class of the adaptable to + * select the correct adapter factoru, while for model context, the + * session is equally important. + * @see org.eclipse.runtime.IAdapterManager + */ + @Override + @SuppressWarnings("unchecked") + public Object getAdapter(Class adapterType) { + Object retVal = null; + DsfSession session = DsfSession.getSession(fSessionId); + if (session != null) { + retVal = session.getModelAdapter(adapterType); + } + if (retVal == null) { + retVal = super.getAdapter(adapterType); + } + return retVal; + } + + /** + * Deriving classes must implement proper equals and hashCode operations + * based on context data. + */ + @Override + abstract public boolean equals(Object obj); + + /** + * Deriving classes must implement proper equals and hashCode operations + * based on context data. + */ + @Override + abstract public int hashCode(); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/AbstractDMEvent.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/AbstractDMEvent.java new file mode 100644 index 00000000000..296433e85fd --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/AbstractDMEvent.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2006, 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.datamodel; + +import org.eclipse.cdt.dsf.concurrent.Immutable; + +/** + * Base implementation of the IDMEvent interface. It only handles the + * required DM-Context reference. + */ +@Immutable +abstract public class AbstractDMEvent<V extends IDMContext> implements IDMEvent<V> { + + private final V fModelContext; + public AbstractDMEvent(V context) { + fModelContext = context; + } + + public V getDMContext() { + return fModelContext; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/CompositeDMContext.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/CompositeDMContext.java new file mode 100644 index 00000000000..8f114e2dd44 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/CompositeDMContext.java @@ -0,0 +1,92 @@ +/******************************************************************************* + * 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.datamodel; + +import java.util.Arrays; + +/** + * Generic DM context used to combine several DM Contexts. This object allows + * clients and other services to combine several contexts into one in order to + * pass them as an argument to a method which takes a generic context as an + * argument. + */ +public class CompositeDMContext implements IDMContext { + + public static String INVALID_SESSION_ID = ""; //$NON-NLS-1$ + + /** + * The list of parent contexts that this composite context is made up of. + */ + private final IDMContext[] fParents; + + /** + * Main constructor provides all data needed to implement the IModelContext + * interface. + * @param parents Array of parent contexts that this composite context is + * made up of. It can be an empty array, but it cannot be null. + */ + public CompositeDMContext(IDMContext[] parents) { + fParents = parents; + } + + /** + * Returns the session ID of the first element in the array of parents of this + * context. May return an empty string if the parents array has no elements. + * <p> + * Note: The session ID is primarily used by UI components to get access to the + * correct session and executor for the given context. The composite context is + * intended to be created by clients which already know the session ID so + * the fact that this method may not return a reliable result is acceptable. + * </p> + */ + public String getSessionId() { + IDMContext[] parents = getParents(); + if (parents.length > 0) { + return parents[0].getSessionId(); + } else { + return INVALID_SESSION_ID; + } + } + + /** + * Returns the list of parents that this composite context is based on. Subclasses + * may override this method to calculate their own set of parents. + */ + public IDMContext[] getParents() { + return fParents; + } + + /** + * Returns the given adapter of the last DMVMContext element found in the tree + * path of this composite context. Will return null if no DMVMContext is found + * in path. + * @see #getSessionId() + */ + @SuppressWarnings("unchecked") + public Object getAdapter(Class adapterType) { + IDMContext[] parents = getParents(); + if (parents.length > 0) { + return parents[0].getAdapter(adapterType); + } else { + return null; + } + } + + @Override + public boolean equals(Object obj) { + return obj instanceof CompositeDMContext && Arrays.equals(((CompositeDMContext)obj).getParents(), getParents()); + } + + @Override + public int hashCode() { + return Arrays.hashCode(getParents()); + } +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/DMContexts.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/DMContexts.java new file mode 100644 index 00000000000..bf0c7102c5c --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/DMContexts.java @@ -0,0 +1,157 @@ +/******************************************************************************* + * Copyright (c) 2006, 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 + * Ericsson - Modified for additional features in DSF Reference implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.datamodel; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; + + +/** + * Holder for utility static methods for manipulating IDMContext objects. + */ +public class DMContexts { + + /** + * Convenience constant. + */ + public static final IDMContext[] EMPTY_CONTEXTS_ARRAY = new IDMContext[0]; + + /** + * Finds a data model context of given type among ancestors of the + * specified context. The returned ancestor is the one closest to the + * specified context, in terms of depth. + * + * Note that for efficiency, this method does not re-use getAllAncestorsOfType() + * to avoid the unnecessary creation of an array. + * + * @param ctx DMC to search. + * @param ancestorType Class type of the desired DMC ancestor. + * @return Returns the ancestor if found, null otherwise. + */ + @ThreadSafe + @SuppressWarnings("unchecked") + public static <V extends IDMContext> V getAncestorOfType(IDMContext ctx, Class<V> ancestorType) { + if(ctx == null) + return null; + // Check the first context here for efficiency + if (ancestorType.isAssignableFrom(ctx.getClass())) { + return (V)ctx; + } + + // Use a LinkedHashSet to avoid duplicates and preserver insertion-order + Set<IDMContext> nodes = new LinkedHashSet<IDMContext>(); + nodes.addAll(Arrays.asList(ctx.getParents())); + while (nodes.isEmpty() == false) { + Set<IDMContext> parents = nodes; + nodes = new LinkedHashSet<IDMContext>(); + for (IDMContext parent : parents) { + if (ancestorType.isAssignableFrom(parent.getClass())) { + return (V)parent; + } + + nodes.addAll(Arrays.asList(parent.getParents())); + } + } + + return null; + } + + /** + * Finds all data model contexts of given type among ancestors of the + * specified context. Ancestors are returned in order of closest to farthest, + * in terms of depth. + * @param ctx DMC to search. + * @param ancestorType Class type of the desired DMC ancestor. + * @return Returns all ancestors found, null if none. + * @since 1.1 + */ + @ThreadSafe + @SuppressWarnings("unchecked") + public static <V extends IDMContext> V[] getAllAncestorsOfType(IDMContext ctx, Class<V> ancestorType) { + if(ctx == null) + return null; + + // Use a LinkedHashSet to avoid duplicates and preserver insertion-order + Set<V> requestedAncestors = new LinkedHashSet<V>(); + Set<IDMContext> nodes = new LinkedHashSet<IDMContext>(); + nodes.add(ctx); + while (nodes.isEmpty() == false) { + Set<IDMContext> parents = nodes; + nodes = new LinkedHashSet<IDMContext>(); + for (IDMContext parent : parents) { + if (ancestorType.isAssignableFrom(parent.getClass())) { + requestedAncestors.add((V)parent); + } + + nodes.addAll(Arrays.asList(parent.getParents())); + } + } + + if (requestedAncestors.isEmpty()) return null; + else { + V[] v = (V[])Array.newInstance(ancestorType, 0); + return requestedAncestors.toArray(v); + } + } + + /** + * Checks all ancestors for a given context to see if the given + * potentialAncestor is in fact an ancestor. + * @param dmc DM Contexts who's ancestors to check. + * @param potentialAncestor Ancestor context to look for. + * @return true if a match is found. + */ + @ThreadSafe + public static boolean isAncestorOf(IDMContext dmc, IDMContext potentialAncestor) { + // Check the direct parents for a match. + for (IDMContext parentDmc : dmc.getParents()) { + if (potentialAncestor.equals(parentDmc)) { + return true; + } + } + + // Recursively check the parents' parents for a match. + for (IDMContext parentDmc : dmc.getParents()) { + if (isAncestorOf(parentDmc, potentialAncestor)) { + return true; + } + } + + // No match. + return false; + } + + /** + * Traverses all the parents of a context and converts the whole + * into a list. + */ + @ThreadSafe + public static List<IDMContext> toList(IDMContext dmc) { + /* + * This method is implemented recursively, which is not necessarily + * the most efficient way to do this. + */ + List<IDMContext> list = new ArrayList<IDMContext>(); + list.add(dmc); + + for (IDMContext parentDmc : dmc.getParents()) { + list.addAll(toList(parentDmc)); + } + return list; + } +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/IDMContext.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/IDMContext.java new file mode 100644 index 00000000000..2d8292ba17d --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/IDMContext.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * 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.datamodel; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.core.runtime.IAdaptable; + +/** + * The base class for data model objects. + * <p> + * DSF services need to return objects to clients which can be used as + * handles to track data stored in the service. Clients such as lazy-loading + * tree and table views retrieve a list of handles, then as needed, they + * retrieve the children and label information for these handles. Because of + * this pattern, services need to be able to return a set of handle objects, + * then as needed clients can retrieve data corresponding to these handles. + * The Data Model Context object is the interface that DSF services should use + * to represent the handle objects that are to be referenced by view model. + * <p> + * <i>Note: DM contexts are meant to be immutable and thus accessible from + * any thread instead of just the services dispatch thread. This is because + * clients may need to call context objects' methods on non-dispatch thread, + * especially equals and hashCode.</i> + * <p> + * <i>Note #2: DM Contexts should also avoid holding references to service + * instances or other large chunks of data, because some of the clients may + * hold onto these objects for longer time than the life of the service. + * This may prevent the service from being garbage collected, possibly keeping + * a lot of resources tied up. + * + */ +@Immutable +public interface IDMContext extends IAdaptable +{ + /** + * Each model context object needs to track the session from which it + * originated. The session ID allows clients to choose the correct + * dispatch thread with which to access the service, and it allows the + * service to be uniquely identified among other sessions. + * @return Session ID of the service that originated the context. + */ + public String getSessionId(); + + /** + * Returns the parent context of this context. ModelContext objects can be + * chained this way to allow methods that require context from multiple + * services to retrieve this context from a single handle that comes from + * the client. + * @return parent context of this context. + */ + public IDMContext[] getParents(); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/IDMData.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/IDMData.java new file mode 100644 index 00000000000..045f8c92013 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/IDMData.java @@ -0,0 +1,22 @@ +/******************************************************************************* + * Copyright (c) 2006, 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.datamodel; + +import org.eclipse.cdt.dsf.concurrent.Immutable; + +/** + * Marker interface for data corresponding to IDMContext, retrieved from a + * service. These data objects are meant to be processed by clients on + * different threads, therefore they should be immutable. + */ +@Immutable +public interface IDMData { +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/IDMEvent.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/IDMEvent.java new file mode 100644 index 00000000000..40edf672e51 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/IDMEvent.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2006, 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.datamodel; + +/** + * Common interface for events that signify changes in the data model. + * The sub-classes should contain specific information about the event, while + * this base class only identifies the DM Context that is affected. + * @param <V> Data Model context type that is affected by this event. + */ +public interface IDMEvent <V extends IDMContext> { + V getDMContext(); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/IDMService.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/IDMService.java new file mode 100644 index 00000000000..f0071711a62 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/IDMService.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * 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.datamodel; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.service.IDsfService; + +/** + * Interface for DSF services that provide model data to clients. + * <p> + * For completeness this service interface derives from <code>IDMData</data> + * and has a method which allows clients to retrieve the DM Context that + * represents the service data. + * + * @deprecated Without getModelData method this service has no function. + * There's also no need for it as a marker interface so we may as well + * get rid of it. + */ +public interface IDMService extends IDsfService { + /** + * Retrieves model data object for given context. This method makes it + * un-necessary for every model service to declare a separate method + * for retrieving model data of specific type. + * + * @param <V> The Data Model Data type that is to be retrieved. + * @param dmc Data Model Context for the data model data object to be retrieved. + * @param rm Request completion monitor to be filled in with the Data Model Data. + * + * @deprecated This method is now deprecated as there is no compile-time linking + * between IDMContext and IDMData objects (see bug 205132) + */ + @Deprecated + void getModelData(IDMContext dmc, DataRequestMonitor<?> rm); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/package.html b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/package.html new file mode 100644 index 00000000000..2ea664a1ee4 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/package.html @@ -0,0 +1,26 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html> +<head> + <meta content="text/html; charset=ISO-8859-1" + http-equiv="content-type"> + <title>Eclipse Device Debug - Debugger Services Framework - Data Model</title> +</head> +<body> +Provides a base API and utilities for expoding data model through DSF +services.<br> +<br> +<h2>Package Specification</h2> +Practically speaking, all state data held by the DSF services makes up +the "data mode" of the service session. However, to make it easy +to present this data in standard debug views, as well as customizable +views, it is useful to present the data using a consisten pattern and +with a set of published APIs and utilities. This package aims to +provide these APIs and utilities.<br> +<h3>Development Plans</h3> +This package is a work in progress and it is missing a major +feature. This feature is being able to automatically parametrize +the contents of the data model in order to generically traverse it, and +to write data-driven framework for populating views with model data.<br> +<br> +</body> +</html> diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/internal/provisional/model/IMemoryBlockUpdatePolicyProvider.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/internal/provisional/model/IMemoryBlockUpdatePolicyProvider.java new file mode 100644 index 00000000000..7f57a6c812c --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/internal/provisional/model/IMemoryBlockUpdatePolicyProvider.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 - Ted Williams - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.debug.internal.provisional.model; + +/* + * This interface is EXPERIMENTAL. +*/ + +public interface IMemoryBlockUpdatePolicyProvider +{ + public String[] getUpdatePolicies(); + + public String getUpdatePolicyDescription(String id); + + public String getUpdatePolicy(); + + public void setUpdatePolicy(String id); + + public void clearCache(); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/model/DsfMemoryBlock.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/model/DsfMemoryBlock.java new file mode 100644 index 00000000000..88c8a119eb1 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/model/DsfMemoryBlock.java @@ -0,0 +1,634 @@ +/******************************************************************************* + * 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 Communication - upgrade IF to IMemoryBlockExtension + * Ericsson Communication - added support for 64 bit processors + * Ericsson Communication - added support for changed bytes + * Ericsson Communication - better management of exceptions + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.model; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.core.IAddress; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Query; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.debug.internal.provisional.model.IMemoryBlockUpdatePolicyProvider; +import org.eclipse.cdt.dsf.debug.service.IMemory; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryChangedEvent; +import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.StateChangeReason; +import org.eclipse.cdt.dsf.internal.DsfPlugin; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.utils.Addr64; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.PlatformObject; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.DebugEvent; +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.model.IDebugTarget; +import org.eclipse.debug.core.model.IMemoryBlockExtension; +import org.eclipse.debug.core.model.IMemoryBlockRetrieval; +import org.eclipse.debug.core.model.MemoryByte; + +/** + * This class manages the memory block retrieved from the target as a result + * of a getBytesFromAddress() call from the platform. + * + * It performs its read/write functions using the MemoryService. + */ +public class DsfMemoryBlock extends PlatformObject implements IMemoryBlockExtension, IMemoryBlockUpdatePolicyProvider +{ + private final static String UPDATE_POLICY_AUTOMATIC = "Automatic"; //$NON-NLS-1$ + private final static String UPDATE_POLICY_MANUAL = "Manual"; //$NON-NLS-1$ + private final static String UPDATE_POLICY_BREAKPOINT = "On Breakpoint"; //$NON-NLS-1$ + + private final IMemoryDMContext fContext; + private final ILaunch fLaunch; + private final IDebugTarget fDebugTarget; + private final DsfMemoryBlockRetrieval fRetrieval; + private final String fModelId; + private final String fExpression; + private final BigInteger fBaseAddress; + + private BigInteger fBlockAddress; + private int fLength; + private int fWordSize; + private MemoryByte[] fBlock; + + private String fUpdatePolicy = UPDATE_POLICY_AUTOMATIC; + + private ArrayList<Object> fConnections = new ArrayList<Object>(); + + @SuppressWarnings("unused") + private boolean isEnabled; + + /** + * Constructor. + * + * @param retrieval - the MemoryBlockRetrieval (session context) + * @param modelId - + * @param expression - the displayed expression in the UI + * @param address - the actual memory block start address + * @param word_size - the number of bytes per address + * @param length - the requested block length (could be 0) + */ + DsfMemoryBlock(DsfMemoryBlockRetrieval retrieval, IMemoryDMContext context, String modelId, String expression, BigInteger address, int word_size, long length) { + fLaunch = retrieval.getLaunch(); + fDebugTarget = retrieval.getDebugTarget(); + fRetrieval = retrieval; + fContext = context; + fModelId = modelId; + fExpression = expression; + fBaseAddress = address; + + // Current block information + fBlockAddress = address; + fWordSize = word_size; + fLength = (int) length; + fBlock = null; + + try { + fRetrieval.getExecutor().execute(new Runnable() { + public void run() { + fRetrieval.getSession().addServiceEventListener(DsfMemoryBlock.this, null); + } + }); + } catch (RejectedExecutionException e) { + // Session is shut down. + } + } + + // //////////////////////////////////////////////////////////////////////// + // IAdaptable + // //////////////////////////////////////////////////////////////////////// + + /* (non-Javadoc) + * @see org.eclipse.core.runtime.PlatformObject#getAdapter(java.lang.Class) + */ + @SuppressWarnings("unchecked") + @Override + public Object getAdapter(Class adapter) { + if (adapter.isAssignableFrom(DsfMemoryBlockRetrieval.class)) { + return fRetrieval; + } + return super.getAdapter(adapter); + } + + // //////////////////////////////////////////////////////////////////////// + // IDebugElement + // //////////////////////////////////////////////////////////////////////// + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IDebugElement#getDebugTarget() + */ + public IDebugTarget getDebugTarget() { + return fDebugTarget; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IDebugElement#getModelIdentifier() + */ + public String getModelIdentifier() { + return fModelId; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IDebugElement#getLaunch() + */ + public ILaunch getLaunch() { + return fLaunch; + } + + // //////////////////////////////////////////////////////////////////////// + // IMemoryBock interface - obsoleted by IMemoryBlockExtension + // //////////////////////////////////////////////////////////////////////// + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlock#getStartAddress() + */ + public long getStartAddress() { + // Not implemented (obsolete) + return 0; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlock#getLength() + */ + public long getLength() { + // Not implemented (obsolete) + return 0; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlock#getBytes() + */ + public byte[] getBytes() throws DebugException { + // Not implemented (obsolete) + return new byte[0]; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlock#supportsValueModification() + */ + public boolean supportsValueModification() { + return fRetrieval.supportsValueModification(); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlock#setValue(long, byte[]) + */ + public void setValue(long offset, byte[] bytes) throws DebugException { + // Not implemented (obsolete) + } + + // //////////////////////////////////////////////////////////////////////// + // IMemoryBlockExtension interface + // //////////////////////////////////////////////////////////////////////// + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#getExpression() + */ + public String getExpression() { + return fExpression; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#getBigBaseAddress() + */ + public BigInteger getBigBaseAddress() throws DebugException { + return fBaseAddress; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#getMemoryBlockStartAddress() + */ + public BigInteger getMemoryBlockStartAddress() throws DebugException { + // Null indicates that memory can be retrieved at addresses lower than the block base address + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#getMemoryBlockEndAddress() + */ + public BigInteger getMemoryBlockEndAddress() throws DebugException { + // Null indicates that memory can be retrieved at addresses higher the block base address + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#getBigLength() + */ + public BigInteger getBigLength() throws DebugException { + // -1 indicates that memory block is unbounded + return BigInteger.valueOf(-1); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#getAddressSize() + */ + public int getAddressSize() throws DebugException { + return fRetrieval.getAddressSize(); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#supportBaseAddressModification() + */ + public boolean supportBaseAddressModification() throws DebugException { + return fRetrieval.supportBaseAddressModification(); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#supportsChangeManagement() + */ + public boolean supportsChangeManagement() { + return true; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#setBaseAddress(java.math.BigInteger) + */ + public void setBaseAddress(BigInteger address) throws DebugException { + fBlockAddress = address; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#getBytesFromOffset(java.math.BigInteger, long) + */ + public MemoryByte[] getBytesFromOffset(BigInteger offset, long units) throws DebugException { + return getBytesFromAddress(fBlockAddress.add(offset), units); + } + + private boolean fUseCachedData = false; + + public void clearCache() { + fUseCachedData = false; + } + + @DsfServiceEventHandler + public void handleCacheSuspendEvent(IRunControl.ISuspendedDMEvent e) { + if (e.getReason() == StateChangeReason.BREAKPOINT) + fUseCachedData = false; + } + + private boolean isUseCacheData() + { + if (fUpdatePolicy.equals(DsfMemoryBlock.UPDATE_POLICY_BREAKPOINT)) + return fUseCachedData; + + if (fUpdatePolicy.equals(DsfMemoryBlock.UPDATE_POLICY_MANUAL)) + return fUseCachedData; + + return false; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#getBytesFromAddress(java.math.BigInteger, long) + */ + public MemoryByte[] getBytesFromAddress(BigInteger address, long units) throws DebugException { + + if (isUseCacheData() && fBlockAddress.compareTo(address) == 0 && units * getAddressableSize() <= fBlock.length) + return fBlock; + + MemoryByte[] newBlock = fetchMemoryBlock(address, units); + int newLength = (newBlock != null) ? newBlock.length : 0; + + // If the retrieved block overlaps with the cached block, flag the changed bytes + // so they can be properly highlighted in the platform Memory view. + // Note: In the standard Memory view, the values are displayed in cells of 4 bytes + // (on 4-bytes address boundaries) and all 4 bytes have to be flagged so the + // cell is highlighted (and the delta sigh is shown). + if (fBlock != null && newLength > 0) { + switch (fBlockAddress.compareTo(address)) { + // Cached block begins before the retrieved block location + // If there is no overlap, there are no bytes to flag + case -1: + { + // Determine the distance between the cached and the + // requested block addresses + BigInteger bigDistance = address.subtract(fBlockAddress); + + // If the distance does not exceed the length of the cached block, + // then there is some overlap between the blocks and we have to + // mark the changed bytes (if applicable) + if (bigDistance.compareTo(BigInteger.valueOf(fLength)) == -1) { + // Here we can reasonably assume that java integers are OK + int distance = bigDistance.intValue(); + int length = fLength - distance; + + // Work by cells of 4 bytes + for (int i = 0; i < length; i += 4) { + // Determine if any byte within the cell was modified + boolean changed = false; + for (int j = i; j < (i + 4) && j < length; j++) { + newBlock[j].setFlags(fBlock[distance + j].getFlags()); + if (newBlock[j].getValue() != fBlock[distance + j].getValue()) + changed = true; + } + // If so, flag the whole cell as modified + if (changed) + for (int j = i; j < (i + 4) && j < length; j++) { + newBlock[j].setHistoryKnown(true); + newBlock[j].setChanged(true); + } + } + + } + break; + } + + // Cached block begins before the retrieved block location + // If there is no overlap, there are no bytes to flag + // (this block of code is symmetric with the previous one) + case 0: + case 1: + { + // Determine the distance between the cached and the + // requested block addresses + BigInteger bigDistance = fBlockAddress.subtract(address); + + // If the distance does not exceed the length of the new block, + // then there is some overlap between the blocks and we have to + // mark the changed bytes (if applicable) + if (bigDistance.compareTo(BigInteger.valueOf(newLength)) == -1) { + // Here we can reasonably assume that java integers are OK + int distance = bigDistance.intValue(); + int length = newLength - distance; + + // Work by cells of 4 bytes + for (int i = 0; i < length; i += 4) { + // Determine if any byte within the cell was modified + boolean changed = false; + for (int j = i; j < (i + 4) && j < length; j++) { + newBlock[distance + j].setFlags(fBlock[j].getFlags()); + if (newBlock[distance + j].getValue() != fBlock[j].getValue()) + changed = true; + } + // If so, flag the whole cell as modified + if (changed) + for (int j = i; j < (i + 4) && j < length; j++) { + newBlock[distance + j].setHistoryKnown(true); + newBlock[distance + j].setChanged(true); + } + } + } + break; + } + + default: + break; + } + } + + // Update the internal state + fBlock = newBlock; + fBlockAddress = address; + fLength = newLength; + + if (fUpdatePolicy.equals(DsfMemoryBlock.UPDATE_POLICY_BREAKPOINT)) + fUseCachedData = true; + else if (fUpdatePolicy.equals(DsfMemoryBlock.UPDATE_POLICY_MANUAL)) + fUseCachedData = true; + + return fBlock; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#setValue(java.math.BigInteger, byte[]) + */ + public void setValue(BigInteger offset, byte[] bytes) throws DebugException { + writeMemoryBlock(offset.longValue(), bytes); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#connect(java.lang.Object) + */ + public void connect(Object client) { + if (!fConnections.contains(client)) + fConnections.add(client); + if (fConnections.size() == 1) + enable(); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#disconnect(java.lang.Object) + */ + public void disconnect(Object client) { + if (fConnections.contains(client)) + fConnections.remove(client); + if (fConnections.size() == 0) + disable(); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#getConnections() + */ + public Object[] getConnections() { + return fConnections.toArray(); + } + + private void enable() { + isEnabled = true; + } + + private void disable() { + isEnabled = false; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#dispose() + */ + public void dispose() throws DebugException { + try { + fRetrieval.getExecutor().execute(new Runnable() { + public void run() { + fRetrieval.getSession().removeServiceEventListener(DsfMemoryBlock.this); + } + }); + } catch (RejectedExecutionException e) { + // Session is down. + } + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#getMemoryBlockRetrieval() + */ + public IMemoryBlockRetrieval getMemoryBlockRetrieval() { + return fRetrieval; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#getAddressableSize() + */ + public int getAddressableSize() throws DebugException { + return fRetrieval.getAddressableSize(); + } + + /////////////////////////////////////////////////////////////////////////// + // Helper functions + /////////////////////////////////////////////////////////////////////////// + + /* + * The real thing. Since the original call is synchronous (from a platform + * Job), we use a Query that will patiently wait for the underlying + * asynchronous calls to complete before returning. + * + * @param bigAddress + * @param length + * @return MemoryByte[] + * @throws DebugException + */ + private MemoryByte[] fetchMemoryBlock(BigInteger bigAddress, final long length) throws DebugException { + + // For the IAddress interface + final Addr64 address = new Addr64(bigAddress); + + // Use a Query to synchronize the downstream calls + Query<MemoryByte[]> query = new Query<MemoryByte[]>() { + @Override + protected void execute(final DataRequestMonitor<MemoryByte[]> drm) { + IMemory memoryService = (IMemory) fRetrieval.getServiceTracker().getService(); + if (memoryService != null) { + // Go for it + memoryService.getMemory( + fContext, address, 0, fWordSize, (int) length, + new DataRequestMonitor<MemoryByte[]>(fRetrieval.getExecutor(), drm) { + @Override + protected void handleSuccess() { + drm.setData(getData()); + drm.done(); + } + }); + } + + } + }; + fRetrieval.getExecutor().execute(query); + + try { + return query.get(); + } catch (InterruptedException e) { + throw new DebugException(new Status(IStatus.ERROR, + DsfPlugin.PLUGIN_ID, DebugException.INTERNAL_ERROR, + "Error reading memory block (InterruptedException)", e)); //$NON-NLS-1$ + } catch (ExecutionException e) { + throw new DebugException(new Status(IStatus.ERROR, + DsfPlugin.PLUGIN_ID, DebugException.INTERNAL_ERROR, + "Error reading memory block (ExecutionException)", e)); //$NON-NLS-1$ + } + } + + /* Writes an array of bytes to memory. + * + * @param offset + * @param bytes + * @throws DebugException + */ + private void writeMemoryBlock(final long offset, final byte[] bytes) throws DebugException { + + // For the IAddress interface + final Addr64 address = new Addr64(fBaseAddress); + + // Use a Query to synchronize the downstream calls + Query<MemoryByte[]> query = new Query<MemoryByte[]>() { + @Override + protected void execute(final DataRequestMonitor<MemoryByte[]> drm) { + IMemory memoryService = (IMemory) fRetrieval.getServiceTracker().getService(); + if (memoryService != null) { + // Go for it + memoryService.setMemory( + fContext, address, offset, fWordSize, bytes.length, bytes, + new RequestMonitor(fRetrieval.getExecutor(), null)); + } + + } + }; + fRetrieval.getExecutor().execute(query); + + try { + query.get(); + } catch (InterruptedException e) { + throw new DebugException(new Status(IStatus.ERROR, + DsfPlugin.PLUGIN_ID, DebugException.INTERNAL_ERROR, + "Error writing memory block (InterruptedException)", e)); //$NON-NLS-1$ + } catch (ExecutionException e) { + throw new DebugException(new Status(IStatus.ERROR, + DsfPlugin.PLUGIN_ID, DebugException.INTERNAL_ERROR, + "Error writing memory block (ExecutionException)", e)); //$NON-NLS-1$ + } + } + + /////////////////////////////////////////////////////////////////////////// + // Event Handlers + /////////////////////////////////////////////////////////////////////////// + + @DsfServiceEventHandler + public void eventDispatched(IRunControl.ISuspendedDMEvent e) { + + // Clear the "Changed" flags after each run/resume/step + for (int i = 0; i < fLength; i++) + fBlock[i].setChanged(false); + + // Generate the MemoryChangedEvents + handleMemoryChange(BigInteger.ZERO); + } + + @DsfServiceEventHandler + public void eventDispatched(IMemoryChangedEvent e) { + + // Check if we are in the same address space + if (e.getDMContext().equals(fContext)) { + IAddress[] addresses = e.getAddresses(); + for (int i = 0; i < addresses.length; i++) + handleMemoryChange(addresses[i].getValue()); + } + } + + /** + * @param address + * @param length + */ + public void handleMemoryChange(BigInteger address) { + + // Check if the change affects this particular block (0 is universal) + BigInteger fEndAddress = fBlockAddress.add(BigInteger.valueOf(fLength)); + if (address.equals(BigInteger.ZERO) || + ((fBlockAddress.compareTo(address) != 1) && (fEndAddress.compareTo(address) == 1))) + { + // Notify the event listeners + DebugEvent debugEvent = new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.CONTENT); + DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[] { debugEvent }); + } + } + + public String[] getUpdatePolicies() { + return new String[] {UPDATE_POLICY_AUTOMATIC, UPDATE_POLICY_MANUAL, UPDATE_POLICY_BREAKPOINT}; + } + + public String getUpdatePolicy() + { + return fUpdatePolicy; + } + + public void setUpdatePolicy(String policy) + { + fUpdatePolicy = policy; + } + + public String getUpdatePolicyDescription(String id) { + return id; + } +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/model/DsfMemoryBlockRetrieval.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/model/DsfMemoryBlockRetrieval.java new file mode 100644 index 00000000000..6c224b78c11 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/model/DsfMemoryBlockRetrieval.java @@ -0,0 +1,505 @@ +/******************************************************************************* + * 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 Communication - upgrade IF to IMemoryBlockRetrievalExtension + * Ericsson Communication - added Expression evaluation + * Ericsson Communication - added support for 64 bit processors + * Ericsson Communication - added support for event handling + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.model; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +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.service.IExpressions; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues; +import org.eclipse.cdt.dsf.debug.service.IMemory; +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; +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.IMemoryDMContext; +import org.eclipse.cdt.dsf.internal.DsfPlugin; +import org.eclipse.cdt.dsf.service.DsfServices; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.service.IDsfService; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.PlatformObject; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; +import org.eclipse.debug.core.model.IDebugTarget; +import org.eclipse.debug.core.model.IMemoryBlock; +import org.eclipse.debug.core.model.IMemoryBlockExtension; +import org.eclipse.debug.core.model.IMemoryBlockRetrievalExtension; +import org.osgi.framework.BundleContext; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.util.tracker.ServiceTracker; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * Implementation of memory access API of the Eclipse standard debug model. + * + * The DsfMemoryBlockRetrieval is not an actual memory block but rather a + * reference to the memory address space for an execution context (e.g. a + * process) within a debug session. From this debug 'context', memory blocks + * can then be read/written. + * + * Note: For the reference application, The IMemoryBlockRetrievalExtension + * is implemented. This will result in getExtendedMemoryBlock() being called + * when a memory block is selected from the platform memory pane. + * + * However, if the 'simpler' IMemoryBlockRetrieval is to be implemented, the + * code will still be functional after some trivial adjustments. + * + */ +public class DsfMemoryBlockRetrieval extends PlatformObject implements IMemoryBlockRetrievalExtension +{ + private final String fModelId; + private final DsfSession fSession; + private final DsfExecutor fExecutor; + private final String fContextString; + private final ServiceTracker fMemoryServiceTracker; + private final ServiceTracker fExpressionServiceTracker; + + private final ILaunchConfiguration fLaunchConfig; + private final ILaunch fLaunch; + private final IDebugTarget fDebugTarget; + private final boolean fSupportsValueModification; + private final boolean fSupportBaseAddressModification; + private final int fAddressSize; + private final int fWordSize; // Number of bytes per address + + /** + * Constructor + * + * @param modelId + * @param dmc + * @throws DebugException + */ + public DsfMemoryBlockRetrieval(String modelId, ILaunchConfiguration config, DsfSession session) throws DebugException { + + // DSF stuff + fModelId = modelId; + + // FIXME: (Bug228573) Currently memory contexts are differentiated by + // sessionID so there is no way to guarantee the memory blocks will be + // reinstated in the correct memory space. + // Need a way to create deterministically the context ID from a unique + // target, ideally from the launch configuration (or derived from it). + // For the time being, just put some constant. This will work until we + // support multiple targets in the same launch. + // fContextString = fContext.toString(); + fContextString = "Context string"; //$NON-NLS-1$ + + fSession = session; + if (fSession == null) { + throw new IllegalArgumentException( + "Session " + session + " is not active"); //$NON-NLS-1$ //$NON-NLS-2$ + } + fExecutor = fSession.getExecutor(); + BundleContext bundle = DsfPlugin.getBundleContext(); + + // Here we chose to use 2 distinct service trackers instead of an + // amalgamated one because it is less error prone (and we are lazy). + + // Create a tracker for the MemoryService + String memoryServiceFilter = DsfServices.createServiceFilter(IMemory.class, session.getId()); + + try { + fMemoryServiceTracker = new ServiceTracker( + bundle, bundle.createFilter(memoryServiceFilter), null); + } catch (InvalidSyntaxException e) { + throw new DebugException(new Status(IStatus.ERROR, + DsfPlugin.PLUGIN_ID, DebugException.INTERNAL_ERROR, + "Error creating service filter.", e)); //$NON-NLS-1$ + } + fMemoryServiceTracker.open(); + + // Create a tracker for the ExpressionService + String expressionServiceFilter = "(&" + //$NON-NLS-1$ + "(OBJECTCLASS=" //$NON-NLS-1$ + + IExpressions.class.getName() + + ")" + //$NON-NLS-1$ + "(" + IDsfService.PROP_SESSION_ID //$NON-NLS-1$ + + "=" + session.getId() + ")" + //$NON-NLS-1$//$NON-NLS-2$ + ")"; //$NON-NLS-1$ + + try { + fExpressionServiceTracker = new ServiceTracker( + bundle, bundle.createFilter(expressionServiceFilter), null); + } catch (InvalidSyntaxException e) { + throw new DebugException(new Status(IStatus.ERROR, + DsfPlugin.PLUGIN_ID, DebugException.INTERNAL_ERROR, + "Error creating service filter.", e)); //$NON-NLS-1$ + } + fExpressionServiceTracker.open(); + + // Launch configuration information + fLaunchConfig = config; + fLaunch = null; + fDebugTarget = null; + fAddressSize = 4; // Get this from the launch configuration + fWordSize = 1; // Get this from the launch configuration + fSupportsValueModification = true; // Get this from the launch configuration + fSupportBaseAddressModification = false; // Get this from the launch configuration + } + + /////////////////////////////////////////////////////////////////////////// + // Memory monitors persistence + /////////////////////////////////////////////////////////////////////////// + + /* + * In the launch configuration file, the memory block entry is structured + * as follows (note: this differs from CDI): + * + * <stringAttribute + * key="org.eclipse.dsf.launch.MEMORY_BLOCKS" + * value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> + * <memoryBlockExpressionList context=[memory context ID]> + * <memoryBlockExpression label=[monitor label] address=[base address]/> + * <memoryBlockExpression ...> + * ... + * </memoryBlockExpressionList> + * ... + * <memoryBlockExpressionList context=...> + * ... + * </memoryBlockExpressionList>" + * /> + */ + + //------------------------------------------------------------------------- + // Memory blocks memento tags + //------------------------------------------------------------------------- + + // These 2 really belong in the DSF launch configuration class... + private static final String DSF_LAUNCH_ID = "org.eclipse.dsf.launch"; //$NON-NLS-1$ + private static final String ATTR_DEBUGGER_MEMORY_BLOCKS = DSF_LAUNCH_ID + ".MEMORY_BLOCKS"; //$NON-NLS-1$ + + private static final String MEMORY_BLOCK_EXPRESSION_LIST = "memoryBlockExpressionList"; //$NON-NLS-1$ + private static final String ATTR_EXPRESSION_LIST_CONTEXT = "context"; //$NON-NLS-1$ + private static final String MEMORY_BLOCK_EXPRESSION = "memoryBlockExpression"; //$NON-NLS-1$ + private static final String ATTR_MEMORY_BLOCK_EXPR_LABEL = "label"; //$NON-NLS-1$ + private static final String ATTR_MEMORY_BLOCK_EXPR_ADDRESS = "address"; //$NON-NLS-1$ + + //------------------------------------------------------------------------- + // Install persisted memory monitors + //------------------------------------------------------------------------- + + /** + * Restore the memory monitors from the memento in the launch configuration + */ + public void initialize(final IMemoryDMContext memoryCtx) { + try { + final String memento = fLaunchConfig.getAttribute(ATTR_DEBUGGER_MEMORY_BLOCKS, ""); //$NON-NLS-1$ + if (memento != null && memento.trim().length() != 0) { + // Submit the runnable to install the monitors on dispatch thread. + getExecutor().submit(new Runnable() { + public void run() { + try { + createBlocksFromConfiguration(memoryCtx, memento); + } catch (CoreException e) { + DsfPlugin.getDefault().getLog().log(e.getStatus()); + } + } + }); + } + } catch (CoreException e) { + DsfPlugin.getDefault().getLog().log(e.getStatus()); + } + } + + private void createBlocksFromConfiguration(IMemoryDMContext memoryCtx, String memento) throws CoreException { + + // Parse the memento and validate its type + Element root = DebugPlugin.parseDocument(memento); + if (!root.getNodeName().equalsIgnoreCase(MEMORY_BLOCK_EXPRESSION_LIST)) { + IStatus status = new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, DebugPlugin.INTERNAL_ERROR, + "Memory monitor initialization: invalid memento", null);//$NON-NLS-1$ + throw new CoreException(status); + } + + // Process the block list specific to this memory context + // FIXME: (Bug228573) We only process the first entry... + if (root.getAttribute(ATTR_EXPRESSION_LIST_CONTEXT).equals(fContextString)) { + List<IMemoryBlock> blocks = new ArrayList<IMemoryBlock>(); + NodeList expressionList = root.getChildNodes(); + int length = expressionList.getLength(); + for (int i = 0; i < length; ++i) { + Node node = expressionList.item(i); + if (node.getNodeType() == Node.ELEMENT_NODE) { + Element entry = (Element) node; + if (entry.getNodeName().equalsIgnoreCase(MEMORY_BLOCK_EXPRESSION)) { + String label = entry.getAttribute(ATTR_MEMORY_BLOCK_EXPR_LABEL); + String address = entry.getAttribute(ATTR_MEMORY_BLOCK_EXPR_ADDRESS); + BigInteger blockAddress = new BigInteger(address); + DsfMemoryBlock block = new DsfMemoryBlock(this, memoryCtx, fModelId, label, blockAddress, fWordSize, 0); + blocks.add(block); + } + } + } + DebugPlugin.getDefault().getMemoryBlockManager().addMemoryBlocks( blocks.toArray(new IMemoryBlock[blocks.size()])); + } + } + + // FIXME: (Bug228573) Each retrieval overwrites the previous one :-( + + // In theory, we should make this a Job since we are writing to the file system. + // However, this would cause the same racing condition as Bug228308. Finally, we + // don't care too much about the UI responsiveness since we are in the process of + // shutting down :-) + public void saveMemoryBlocks() { + try { + ILaunchConfigurationWorkingCopy wc = fLaunchConfig.getWorkingCopy(); + wc.setAttribute(ATTR_DEBUGGER_MEMORY_BLOCKS, getMemento()); + wc.doSave(); + } + catch( CoreException e ) { + DsfPlugin.getDefault().getLog().log(e.getStatus()); + } + } + + public String getMemento() throws CoreException { + IMemoryBlock[] blocks = DebugPlugin.getDefault().getMemoryBlockManager().getMemoryBlocks(this); + Document document = DebugPlugin.newDocument(); + Element expressionList = document.createElement(MEMORY_BLOCK_EXPRESSION_LIST); + expressionList.setAttribute(ATTR_EXPRESSION_LIST_CONTEXT, fContextString); + for (IMemoryBlock block : blocks) { + if (block instanceof IMemoryBlockExtension) { + IMemoryBlockExtension memoryBlock = (IMemoryBlockExtension) block; + Element expression = document.createElement(MEMORY_BLOCK_EXPRESSION); + expression.setAttribute(ATTR_MEMORY_BLOCK_EXPR_LABEL, memoryBlock.getExpression()); + expression.setAttribute(ATTR_MEMORY_BLOCK_EXPR_ADDRESS, memoryBlock.getBigBaseAddress().toString()); + expressionList.appendChild(expression); + } + } + document.appendChild(expressionList); + return DebugPlugin.serializeDocument(document); + } + + /////////////////////////////////////////////////////////////////////////// + // Accessors + /////////////////////////////////////////////////////////////////////////// + + public DsfSession getSession() { + return fSession; + } + + public DsfExecutor getExecutor() { + return fExecutor; + } + + public ServiceTracker getServiceTracker() { + return fMemoryServiceTracker; + } + + /////////////////////////////////////////////////////////////////////////// + // Launch/Target specific information + /////////////////////////////////////////////////////////////////////////// + + public ILaunch getLaunch() { + return fLaunch; + } + + public IDebugTarget getDebugTarget() { + return fDebugTarget; + } + + public int getAddressSize() { + return fAddressSize; + } + + public int getAddressableSize() { + return fWordSize; + } + + public boolean supportsValueModification() { + return fSupportsValueModification; + } + + public boolean supportBaseAddressModification() { + return fSupportBaseAddressModification; + } + + /////////////////////////////////////////////////////////////////////////// + // IMemoryBlockRetrieval - obsoleted by IMemoryBlockRetrievalExtension + /////////////////////////////////////////////////////////////////////////// + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#supportsStorageRetrieval() + */ + public boolean supportsStorageRetrieval() { + return true; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#getMemoryBlock(long, + * long) + */ + public IMemoryBlock getMemoryBlock(final long startAddress, final long length) throws DebugException { + throw new DebugException(new Status( + IStatus.ERROR, DsfPlugin.PLUGIN_ID, DebugException.NOT_SUPPORTED, + "getMemoryBlock() not supported, use getExtendedMemoryBlock()", null)); //$NON-NLS-1$ + } + + /////////////////////////////////////////////////////////////////////////// + // IMemoryBlockRetrievalExtension + /////////////////////////////////////////////////////////////////////////// + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.core.model.IMemoryBlockRetrievalExtension#getExtendedMemoryBlock(java.lang.String, + * java.lang.Object) + */ + public IMemoryBlockExtension getExtendedMemoryBlock(String expression, Object context) throws DebugException { + // Drill for the actual DMC + IMemoryDMContext memoryDmc = null; + IDMContext dmc = null; + if (context instanceof IAdaptable) { + dmc = (IDMContext)((IAdaptable)context).getAdapter(IDMContext.class); + if (dmc != null) { + memoryDmc = DMContexts.getAncestorOfType(dmc, IMemoryDMContext.class); + } + } + + if (memoryDmc == null) { + return null; + } + + // The block start address (supports 64-bit processors) + BigInteger blockAddress; + + /* + * See if the expression is a simple numeric value; if it is, we can + * avoid some costly processing (calling the back-end to resolve the + * expression and obtain an address) + */ + try { + // First, assume a decimal address + int base = 10; + int offset = 0; + + // Check for "hexadecimality" + if (expression.startsWith("0x") || expression.startsWith("0X")) { //$NON-NLS-1$//$NON-NLS-2$ + base = 16; + offset = 2; + } + // Check for "binarity" + else if (expression.startsWith("0b")) { //$NON-NLS-1$ + base = 2; + offset = 2; + } + // Check for "octality" + else if (expression.startsWith("0")) { //$NON-NLS-1$ + base = 8; + offset = 1; + } + // Now, try to parse the expression. If a NumberFormatException is + // thrown, then it wasn't a simple numerical expression and we go + // to plan B (attempt an expression evaluation) + blockAddress = new BigInteger(expression.substring(offset), base); + + } catch (NumberFormatException nfexc) { + // OK, expression is not a simple, absolute numeric value; + // try to resolve as an expression. + // In case of failure, simply return 'null' + + // Resolve the expression + blockAddress = resolveMemoryAddress(dmc, expression); + if (blockAddress == null) { + return null; + } + } + + /* + * At this point, we only resolved the requested memory block + * start address and we have no idea of the block's length. + * + * The renderer will provide this information when it calls + * getBytesFromAddress() i.e. after the memory block holder has + * been instantiated. + * + * The down side is that every time we switch renderer, for the + * same memory block, a trip to the target could result. However, + * the memory request cache should save the day. + */ + + return new DsfMemoryBlock(this, memoryDmc, fModelId, expression, blockAddress, fWordSize, 0); + } + + /////////////////////////////////////////////////////////////////////////// + // Helper functions + /////////////////////////////////////////////////////////////////////////// + + private BigInteger resolveMemoryAddress(final IDMContext dmc, final String expression) throws DebugException { + + // Use a Query to "synchronize" the downstream calls + Query<BigInteger> query = new Query<BigInteger>() { + @Override + protected void execute(final DataRequestMonitor<BigInteger> drm) { + // Lookup for the ExpressionService + final IExpressions expressionService = (IExpressions) fExpressionServiceTracker.getService(); + if (expressionService != null) { + // Create the expression + final IExpressionDMContext expressionDMC = expressionService.createExpression(dmc, expression); + String formatId = IFormattedValues.HEX_FORMAT; + FormattedValueDMContext valueDmc = expressionService.getFormattedValueContext(expressionDMC, formatId); + expressionService.getFormattedExpressionValue( + valueDmc, + new DataRequestMonitor<FormattedValueDMData>(getExecutor(), drm) { + @Override + protected void handleSuccess() { + // Store the result + FormattedValueDMData data = getData(); + String value = data.getFormattedValue().substring(2); // Strip the "0x" + drm.setData(new BigInteger(value, 16)); + drm.done(); + } + } + ); + } + } + }; + fExecutor.execute(query); + + try { + // The happy case + return query.get(); + } catch (InterruptedException e) { + throw new DebugException(new Status(IStatus.ERROR, + DsfPlugin.PLUGIN_ID, DebugException.INTERNAL_ERROR, + "Error evaluating memory address (InterruptedException).", e)); //$NON-NLS-1$ + + } catch (ExecutionException e) { + throw new DebugException(new Status(IStatus.ERROR, + DsfPlugin.PLUGIN_ID, DebugException.INTERNAL_ERROR, + "Error evaluating memory address (ExecutionException).", e)); //$NON-NLS-1$ + } + } + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/AbstractDsfDebugServicesFactory.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/AbstractDsfDebugServicesFactory.java new file mode 100644 index 00000000000..f93655ba9d2 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/AbstractDsfDebugServicesFactory.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (c) 2008 Ericsson 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 - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.service; + +import org.eclipse.cdt.dsf.debug.service.command.ICommandControl; +import org.eclipse.cdt.dsf.service.DsfSession; + +/** + * Convenience base class for {@link IDsfDebugServicesFactory} + * @since 1.1 + */ +public abstract class AbstractDsfDebugServicesFactory implements IDsfDebugServicesFactory { + + @SuppressWarnings("unchecked") + public <V> V createService(Class<V> clazz, DsfSession session, Object ... optionalArguments) { + if (IBreakpoints.class.isAssignableFrom(clazz)) { + return (V)createBreakpointService(session); + } else if (ICommandControl.class.isAssignableFrom(clazz)) { + return (V)createCommandControl(session); + } else if (IDisassembly.class.isAssignableFrom(clazz)) { + return (V)createDisassemblyService(session); + } else if (IExpressions.class.isAssignableFrom(clazz)) { + return (V)createExpressionService(session); + } else if (IMemory.class.isAssignableFrom(clazz)) { + return (V)createMemoryService(session); + } else if (IModules.class.isAssignableFrom(clazz)) { + return (V)createModulesService(session); + } else if (IProcesses.class.isAssignableFrom(clazz)) { + return (V)createProcessesService(session); + } else if (IRegisters.class.isAssignableFrom(clazz)) { + return (V)createRegistersService(session); + } else if (IRunControl.class.isAssignableFrom(clazz)) { + return (V)createRunControlService(session); + } else if (ISourceLookup.class.isAssignableFrom(clazz)) { + return (V)createSourceLookupService(session); + } else if (ISignals.class.isAssignableFrom(clazz)) { + return (V)createSignalsService(session); + } else if (IStack.class.isAssignableFrom(clazz)) { + return (V)createStackService(session); + } else if (ISymbols.class.isAssignableFrom(clazz)) { + return (V)createSymbolsService(session); + } + + return null; + } + + protected IBreakpoints createBreakpointService(DsfSession session) { return null; } + protected ICommandControl createCommandControl(DsfSession session) { return null; } + protected IDisassembly createDisassemblyService(DsfSession session) { return null; } + protected IExpressions createExpressionService(DsfSession session) { return null; } + protected IMemory createMemoryService(DsfSession session) { return null; } + protected IModules createModulesService(DsfSession session) { return null; } + protected IProcesses createProcessesService(DsfSession session) { return null; } + protected IRegisters createRegistersService(DsfSession session) { return null; } + protected IRunControl createRunControlService(DsfSession session) { return null; } + protected ISourceLookup createSourceLookupService(DsfSession session) { return null; } + protected ISignals createSignalsService(DsfSession session) { return null; } + protected IStack createStackService(DsfSession session) { return null; } + protected ISymbols createSymbolsService(DsfSession session) { return null; } + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/BreakpointsMediator.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/BreakpointsMediator.java new file mode 100644 index 00000000000..864c3400f20 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/BreakpointsMediator.java @@ -0,0 +1,886 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River 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 - Initial API and implementation + * Ericsson - Low-level breakpoints integration + *******************************************************************************/ + +package org.eclipse.cdt.dsf.debug.service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +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.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.debug.service.IBreakpoints.IBreakpointDMContext; +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; +import org.eclipse.cdt.dsf.internal.DsfPlugin; +import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.resources.IMarkerDelta; +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.DebugPlugin; +import org.eclipse.debug.core.IBreakpointListener; +import org.eclipse.debug.core.IBreakpointManager; +import org.eclipse.debug.core.IBreakpointManagerListener; +import org.eclipse.debug.core.model.IBreakpoint; +import org.osgi.framework.BundleContext; + +/** + * + */ +public class BreakpointsMediator extends AbstractDsfService implements IBreakpointManagerListener, IBreakpointListener +{ + + /** + * The attribute translator that this service will use to map the platform + * breakpiont attributes to the corresponding target attributes, and vice + * versa. + */ + private IBreakpointAttributeTranslator fAttributeTranslator; + + /** + * DSF Debug service for creating breakpoints. + */ + IBreakpoints fBreakpoints; + + /** + * Platform breakpoint manager + */ + IBreakpointManager fBreakpointManager; + + + /////////////////////////////////////////////////////////////////////////// + // Breakpoints tracking + /////////////////////////////////////////////////////////////////////////// + + /** + * Holds the set of platform breakpoints with their corresponding back-end + * breakpoint attributes, per context (i.e. each platform breakpoint is + * replicated for each execution context). + * - Context entry added/removed on start/stopTrackingBreakpoints() + * - Augmented on breakpointAdded() + * - Modified on breakpointChanged() + * - Diminished on breakpointRemoved() + */ + private Map<IBreakpointsTargetDMContext, Map<IBreakpoint, List<Map<String, Object>>>> fPlatformBPs = + new HashMap<IBreakpointsTargetDMContext, Map<IBreakpoint, List<Map<String, Object>>>>(); + + /** + * Holds the mapping from platform breakpoint to the corresponding target + * breakpoint(s), per context. There can be multiple back-end BPs for a + * single platform BP in the case of [1] multiple target contexts, and/or + * [2] thread filtering. + * Updated when: + * - We start/stop tracking an execution context + * - A platform breakpoint is added/removed + * - A thread filter is applied/removed + */ + private Map<IBreakpointsTargetDMContext, Map<IBreakpoint, List<IBreakpointDMContext>>> fBreakpointDMContexts = + new HashMap<IBreakpointsTargetDMContext, Map<IBreakpoint, List<IBreakpointDMContext>>>(); + + /** + * Due to the very asynchronous nature of DSF, a new breakpoint request can + * pop up at any time before an ongoing one is completed. The following set + * is used to store requests until the ongoing operation completes. + */ + private Set<IBreakpoint> fPendingRequests = new HashSet<IBreakpoint>(); + + /** + * @see fPendingRequests + */ + private Set<IBreakpoint> fPendingBreakpoints = new HashSet<IBreakpoint>(); + + /////////////////////////////////////////////////////////////////////////// + // AbstractDsfService + /////////////////////////////////////////////////////////////////////////// + + /** + * The service constructor + * + * @param session + * @param debugModelId + */ + public BreakpointsMediator(DsfSession session, IBreakpointAttributeTranslator attributeTranslator) { + super(session); + fAttributeTranslator = attributeTranslator; + } + + @Override + public void initialize(final RequestMonitor rm) { + // - Collect references for the services we interact with + // - Register to interesting events + // - Obtain the list of platform breakpoints + // - Register the service for interested parties + super.initialize( + new RequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + doInitialize(rm); + }}); + } + + /** + * Asynchronous service initialization + * + * @param requestMonitor + */ + private void doInitialize(RequestMonitor rm) { + + // Get the services references + fBreakpoints = getServicesTracker().getService(IBreakpoints.class); + fBreakpointManager = DebugPlugin.getDefault().getBreakpointManager(); + fAttributeTranslator.initialize(this); + + // Register to the useful events + fBreakpointManager.addBreakpointListener(this); + fBreakpointManager.addBreakpointManagerListener( this ); + + // Register this service + register(new String[] { BreakpointsMediator.class.getName() }, + new Hashtable<String, String>()); + + rm.done(); + } + + @Override + public void shutdown(final RequestMonitor rm) { + // - Un-register the service + // - Stop listening to events + // - Remove the breakpoints installed by this service + // + // Since we are shutting down, there is no overwhelming need + // to keep the maps coherent... + + // Stop accepting requests and events + unregister(); + fBreakpointManager.removeBreakpointListener(this); + fBreakpointManager.removeBreakpointManagerListener( this ); + fAttributeTranslator.dispose(); + + // Cleanup the breakpoints that are still installed by the service. + // Use a counting monitor which will call mom to complete the shutdown + // after the breakpoints are un-installed (successfully or not). + CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), rm) { + @Override + protected void handleCompleted() { + BreakpointsMediator.super.shutdown(rm); + } + }; + + // We have to make a copy of the fPlatformBPs keys because uninstallBreakpoints() + // modifies the map as it walks through it. + List<IBreakpointsTargetDMContext> platformBPKeysCopy = new ArrayList<IBreakpointsTargetDMContext>(fPlatformBPs.size()); + platformBPKeysCopy.addAll(0, fPlatformBPs.keySet()); + for (IBreakpointsTargetDMContext dmc : platformBPKeysCopy) { + stopTrackingBreakpoints(dmc, countingRm); + } + countingRm.setDoneCount(platformBPKeysCopy.size()); + } + + @Override + protected BundleContext getBundleContext() { + return DsfPlugin.getBundleContext(); + } + + /////////////////////////////////////////////////////////////////////////// + // IBreakpointsManager + /////////////////////////////////////////////////////////////////////////// + + + /** + * Install and begin tracking breakpoints for given context. The service + * will keep installing new breakpoints that appear in the IDE for this + * context until {@link #uninstallBreakpoints(IDMContext)} is called for that + * context. + * @param dmc Context to start tracking breakpoints for. + * @param rm Completion callback. + */ + public void startTrackingBreakpoints(IBreakpointsTargetDMContext dmc, final RequestMonitor rm) { + // - Augment the maps with the new execution context + // - Install the platform breakpoints on the selected target + + // Validate the context + final IBreakpointsTargetDMContext breakpointsDmc = DMContexts.getAncestorOfType(dmc, IBreakpointsTargetDMContext.class); + if (breakpointsDmc == null) { + rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid context type", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + // Make sure a mapping for this execution context does not already exist + Map<IBreakpoint, List<Map<String, Object>>> platformBPs = fPlatformBPs.get(dmc); + if (platformBPs != null) { + rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, INTERNAL_ERROR, "Context already initialized", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + // Create entries in the breakpoint tables for the new context. These entries should only + // be removed when this service stops tracking breakpoints for the given context. + fPlatformBPs.put(breakpointsDmc, new HashMap<IBreakpoint, List<Map<String, Object>>>()); + fBreakpointDMContexts.put(breakpointsDmc, new HashMap<IBreakpoint, List<IBreakpointDMContext>>()); + + // Install the platform breakpoints (stored in fPlatformBPs) on the target. + // We need to use a background thread for this operation because we are + // accessing the resources system to retrieve the breakpoint attributes. + // Accessing the resources system potentially requires using global locks. + // Also we will be calling IBreakpointAttributeTranslator which is prohibited + // from being called on the session executor thread. + new Job("MI Debugger: Install initial breakpoint list.") { //$NON-NLS-1$ + { setSystem(true); } + + // Get the stored breakpoints from the platform BreakpointManager + // and install them on the target + @Override + protected IStatus run(IProgressMonitor monitor) { + // Read initial breakpoints from platform. Copy the breakpoint attributes into a local map. + // Note that we cannot write data into fPlatformBPs table here directly because we are not + // executing on the dispatch thread. + final Map<IBreakpoint, List<Map<String, Object>>> initialPlatformBPs = + new HashMap<IBreakpoint, List<Map<String, Object>>>(); + try { + // Get the stored breakpoint list from the platform BreakpointManager + IBreakpoint[] bps = DebugPlugin.getDefault().getBreakpointManager().getBreakpoints(); + // Single out the installable breakpoints... + for (IBreakpoint bp : bps) { + if (fAttributeTranslator.supportsBreakpoint(bp)) { + // Retrieve the breakpoint attributes + List<Map<String, Object>> attrsArray = + fAttributeTranslator.getBreakpointAttributes(bp, fBreakpointManager.isEnabled()); + // Store it for now (will be installed on the dispatcher thread) + initialPlatformBPs.put(bp, attrsArray); + } + } + } catch (CoreException e) { + IStatus status = new Status( + IStatus.ERROR, DsfPlugin.PLUGIN_ID, REQUEST_FAILED, "Unable to read initial breakpoint attributes", e); //$NON-NLS-1$ + rm.setStatus(status); + rm.done(); + return status; + } + + // Submit the runnable to plant the breakpoints on dispatch thread. + getExecutor().submit(new Runnable() { + public void run() { + installInitialBreakpoints(breakpointsDmc, initialPlatformBPs, rm); + } + }); + + return Status.OK_STATUS; + } + }.schedule(); + } + + /** + * Installs the breakpoints that existed prior to the activation of this + * breakpoints context. + */ + private void installInitialBreakpoints(final IBreakpointsTargetDMContext dmc, + Map<IBreakpoint, List<Map<String, Object>>> initialPlatformBPs, + RequestMonitor rm) + { + // Retrieve the set of platform breakpoints for this context + Map<IBreakpoint, List<Map<String, Object>>> platformBPs = fPlatformBPs.get(dmc); + if (platformBPs == null) { + rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + // Install the individual breakpoints on the executor thread + // Requires a counting monitor to know when we're done + final CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), rm); + countingRm.setDoneCount(initialPlatformBPs.size()); + + for (final IBreakpoint bp : initialPlatformBPs.keySet()) { + final List<Map<String, Object>> attrs = initialPlatformBPs.get(bp); + // Upon determining the debuggerPath, the breakpoint is installed + installBreakpoint(dmc, bp, attrs, new RequestMonitor(getExecutor(), countingRm)); + } + } + + + public void stopTrackingBreakpoints(final IBreakpointsTargetDMContext dmc, final RequestMonitor rm) { + // - Remove the target breakpoints for the given execution context + // - Update the maps + + // Remove the breakpoints for given DMC from the internal maps. + Map<IBreakpoint, List<Map<String, Object>>> platformBPs = fPlatformBPs.get(dmc); + if (platformBPs == null) { + rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, INTERNAL_ERROR, "Breakpoints not installed for given context", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + // Uninstall the individual breakpoints on the executor thread + // Requires a counting monitor to know when we're done + final CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), rm); + countingRm.setDoneCount(platformBPs.size()); + + for (final IBreakpoint bp : platformBPs.keySet()) { + uninstallBreakpoint(dmc, bp, + new RequestMonitor(getExecutor(), countingRm) { + @Override + protected void handleCompleted() { + // After the breakpoint is removed from target. Call the attribute + // translator to refresh breakpoint status based on the new target + // breakpoint status. + new Job("Breakpoint status update") { //$NON-NLS-1$ + { setSystem(true); } + @Override + protected IStatus run(IProgressMonitor monitor) { + fAttributeTranslator.updateBreakpointStatus(bp); + return Status.OK_STATUS; + }; + }.schedule(); + + countingRm.done(); + } + }); + } + } + + /////////////////////////////////////////////////////////////////////////// + // Back-end interface functions + /////////////////////////////////////////////////////////////////////////// + + /** + * Install a new platform breakpoint on the back-end. A platform breakpoint + * can resolve into multiple back-end breakpoints when threads are taken + * into account. + * + * @param dmc + * @param breakpoint + * @param attrsList + * @param rm + */ + private void installBreakpoint(IBreakpointsTargetDMContext dmc, final IBreakpoint breakpoint, + final List<Map<String, Object>> attrsList, final RequestMonitor rm) + { + // Retrieve the set of breakpoints for this context + final Map<IBreakpoint, List<Map<String, Object>>> platformBPs = fPlatformBPs.get(dmc); + assert platformBPs != null; + + final Map<IBreakpoint, List<IBreakpointDMContext>> breakpointIDs = fBreakpointDMContexts.get(dmc); + assert breakpointIDs != null; // fBreakpointIds should be updated in parallel with fPlatformBPs + + // Ensure the breakpoint is not already installed + if (platformBPs.containsKey(breakpoint)) { + rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, INVALID_STATE, "Breakpoint already installed", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + // Update the breakpoint status when all back-end breakpoints have been installed + final CountingRequestMonitor installRM = new CountingRequestMonitor(getExecutor(), rm) { + @Override + protected void handleCompleted() { + // Store the platform breakpoint + platformBPs.put(breakpoint, attrsList); + new Job("Breakpoint status update") { //$NON-NLS-1$ + { setSystem(true); } + @Override + protected IStatus run(IProgressMonitor monitor) { + fAttributeTranslator.updateBreakpointStatus(breakpoint); + return Status.OK_STATUS; + }; + }.schedule(); + rm.done(); + } + }; + + // A back-end breakpoint needs to be installed for each specified attributes map. + installRM.setDoneCount(attrsList.size()); + + // Install the back-end breakpoint(s) + for (Map<String, Object> attrs : attrsList) { + fBreakpoints.insertBreakpoint( + dmc, attrs, + new DataRequestMonitor<IBreakpointDMContext>(getExecutor(), installRM) { + @Override + protected void handleCompleted() { + List<IBreakpointDMContext> list = breakpointIDs.get(breakpoint); + if (list == null) { + list = new LinkedList<IBreakpointDMContext>(); + breakpointIDs.put(breakpoint, list); + } + + if (isSuccess()) { + // Add the breakpoint back-end mapping + list.add(getData()); + } else { + // TODO (bug 219841): need to add breakpoint error status tracking + // in addition to fBreakpointDMContexts. + } + installRM.done(); + } + }); + } + } + + /** + * Un-install an individual breakpoint on the back-end. For one platform + * breakpoint, there could be multiple corresponding back-end breakpoints. + * + * @param dmc + * @param breakpoint + * @param rm + */ + private void uninstallBreakpoint(final IBreakpointsTargetDMContext dmc, final IBreakpoint breakpoint, + final RequestMonitor rm) + { + // Remove completion monitor + CountingRequestMonitor removeRM = new CountingRequestMonitor(getExecutor(), rm) { + @Override + protected void handleCompleted() { + // Remove the attributes mapping + Map<IBreakpoint, List<Map<String, Object>>> platformBPs = fPlatformBPs.get(dmc); + if (platformBPs == null) { + rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context", null)); //$NON-NLS-1$ + rm.done(); + return; + } + platformBPs.remove(breakpoint); + + // Remove the back-end mapping + Map<IBreakpoint, List<IBreakpointDMContext>> breakpointIDs = fBreakpointDMContexts.get(dmc); + if (breakpointIDs == null) { + rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid breakpoint", null)); //$NON-NLS-1$ + rm.done(); + return; + } + breakpointIDs.get(breakpoint).clear(); + breakpointIDs.remove(breakpoint); + + // Update breakpoint status + new Job("Breakpoint status update") { //$NON-NLS-1$ + { setSystem(true); } + @Override + protected IStatus run(IProgressMonitor monitor) { + fAttributeTranslator.updateBreakpointStatus(breakpoint); + return Status.OK_STATUS; + }; + }.schedule(); + + rm.done(); + } + }; + + // Remove the back-end breakpoints + Map<IBreakpoint, List<IBreakpointDMContext>> breakpointIDs = fBreakpointDMContexts.get(dmc); + if (breakpointIDs == null) { + rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid breakpoint", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + List<IBreakpointDMContext> list = breakpointIDs.get(breakpoint); + int count = 0; + if (list != null) { + for (IBreakpointDMContext bp : list) { + fBreakpoints.removeBreakpoint(bp, removeRM); + } + count = list.size(); + } + removeRM.setDoneCount(count); + } + + /** + * Modify an individual breakpoint + * + * @param context + * @param breakpoint + * @param attributes + * @param rm + * @throws CoreException + */ + private void modifyBreakpoint(final IBreakpointsTargetDMContext context, final IBreakpoint breakpoint, + final List<Map<String, Object>> newAttrsList0, final IMarkerDelta oldValues, final RequestMonitor rm) + { + // This method uses several lists to track the changed breakpoints: + // commonAttrsList - attributes which have not changed + // oldAttrsList - attributes for the breakpoint before the change + // newAttrsList - attributes for the breakpoint after the change + // oldBpContexts - target-side breakpoints from before the change + // newBpContexts - target-side breakpoints after the change + // attrDeltasList - changes in the attributes for each attribute map in + // oldAttrsList and newAttrsList + + // Get the maps + final Map<IBreakpoint, List<Map<String, Object>>> platformBPs = fPlatformBPs.get(context); + final Map<IBreakpoint, List<IBreakpointDMContext>> breakpointIDs = fBreakpointDMContexts.get(context); + if (platformBPs == null || breakpointIDs == null) { + rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + // Get the original breakpoint attributes + final List<Map<String, Object>> oldAttrsList0 = platformBPs.get(breakpoint); + if (oldAttrsList0 == null) { + rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid breakpoint", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + // Get the list of corresponding back-end breakpoints + final List<IBreakpointDMContext> oldBpContexts = new ArrayList<IBreakpointDMContext>(breakpointIDs.get(breakpoint)); + if (oldBpContexts == null) { + rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid breakpoint", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + // Calculate the list of attributes maps that have not changed. + // Immediately add these to the list of new breakpoint contexts, + // and remove them from further breakpoint attribute comparisons. + final List<Map<String, Object>> commonAttrsList = getCommonAttributeMaps(newAttrsList0, oldAttrsList0); + final List<IBreakpointDMContext> newBpContexts = new ArrayList<IBreakpointDMContext>(commonAttrsList.size()); + + final List<Map<String, Object>> newAttrsList = new ArrayList<Map<String, Object>>(newAttrsList0); + newAttrsList.removeAll(commonAttrsList); + + List<Map<String, Object>> oldAttrsList = new ArrayList<Map<String, Object>>(oldAttrsList0); + for (int i = 0; i < oldAttrsList.size(); i++) { + if (commonAttrsList.contains(oldAttrsList.get(i))) { + if (oldBpContexts.size() > i) { + newBpContexts.add(oldBpContexts.remove(i)); + } + } + } + oldAttrsList.removeAll(commonAttrsList); + + // Create a list of attribute changes. The lenghth of this list will + // always be max(oldAttrList.size(), newAttrsList.size()), padded with + // null's if oldAttrsList was longer. + final List<Map<String, Object>> attrDeltasList = getAttributesDeltas(oldAttrsList, newAttrsList); + + // Create the request monitor that will be called when all + // modifying/inserting/removing is complete. + final CountingRequestMonitor countingRM = new CountingRequestMonitor(getExecutor(), rm) { + @Override + protected void handleCompleted() { + // Save the new list of breakpoint contexts and attributes + breakpointIDs.put(breakpoint, newBpContexts); + newAttrsList.addAll(commonAttrsList); + platformBPs.put(breakpoint, newAttrsList); + + // Update breakpoint status. updateBreakpointStatus() cannot + // be called on the executor thread, so we need to + // use a Job. + new Job("Breakpoint status update") { //$NON-NLS-1$ + { setSystem(true); } + @Override + protected IStatus run(IProgressMonitor monitor) { + fAttributeTranslator.updateBreakpointStatus(breakpoint); + return Status.OK_STATUS; + }; + }.schedule(); + + super.handleCompleted(); + } + }; + + // Set the count, if could be zero if no breakpoints have actually changed. + countingRM.setDoneCount(attrDeltasList.size()); + + // Process the changed breakpoints. + for (int i = 0; i < attrDeltasList.size(); i++) { + if (attrDeltasList.get(i) == null) { + // The list of new attribute maps was shorter than the old. + // Remove the corresponding target-side bp. + fBreakpoints.removeBreakpoint(oldBpContexts.get(i), countingRM); + } else if ( i >= oldBpContexts.size()) { + // The list of new attribute maps was longer, just insert + // the new breakpoint + final Map<String, Object> attrs = newAttrsList.get(i); + fBreakpoints.insertBreakpoint( + context, attrs, + new DataRequestMonitor<IBreakpointDMContext>(getExecutor(), countingRM) { + @Override + protected void handleSuccess() { + newBpContexts.add(getData()); + countingRM.done(); + } + }); + } else if ( !fAttributeTranslator.canUpdateAttributes(oldBpContexts.get(i), attrDeltasList.get(i)) ) { + // The attribute translator tells us that the debugger cannot modify the + // breakpoint to change the given attributes. Remove the breakpoint + // and insert a new one. + final Map<String, Object> attrs = newAttrsList.get(i); + fBreakpoints.removeBreakpoint( + oldBpContexts.get(i), + new RequestMonitor(getExecutor(), countingRM) { + @Override + protected void handleCompleted() { + fBreakpoints.insertBreakpoint( + context, attrs, + new DataRequestMonitor<IBreakpointDMContext>(getExecutor(), countingRM) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + newBpContexts.add(getData()); + } else { + // TODO (bug 219841): need to add breakpoint error status tracking + // in addition to fBreakpointDMContexts. + } + countingRM.done(); + } + }); + } + }); + } else { + // The back end can modify the breakpoint. Update the breakpoint with the + // new attributes. + final IBreakpointDMContext bpCtx = oldBpContexts.get(i); + fBreakpoints.updateBreakpoint( + oldBpContexts.get(i), newAttrsList.get(i), + new RequestMonitor(getExecutor(), countingRM) { + @Override + protected void handleSuccess() { + newBpContexts.add(bpCtx); + countingRM.done(); + } + }); + } + } + } + + private List<Map<String, Object>> getCommonAttributeMaps(List<Map<String, Object>> array1, List<Map<String, Object>> array2) + { + List<Map<String, Object>> intersection = new LinkedList<Map<String, Object>>(); + List<Map<String, Object>> list2 = new ArrayList<Map<String, Object>>(array2); + for (Map<String, Object> array1Map : array1) { + if (list2.remove(array1Map)) { + intersection.add(array1Map); + } + } + return intersection; + } + + /** + * Determine the set of modified attributes + * + * @param oldAttributes + * @param newAttributes + * @return + */ + private List<Map<String, Object>> getAttributesDeltas(List<Map<String, Object>> oldAttributesList, List<Map<String, Object>> newAttributesList) { + List<Map<String, Object>> deltas = new ArrayList<Map<String, Object>>(oldAttributesList.size()); + + // Go through the bp attributes common to the old and the new lists and calculate + // their deltas. + for (int i = 0; i < oldAttributesList.size() && i < newAttributesList.size(); i++) { + Map<String, Object> oldAttributes = oldAttributesList.get(i); + Map<String, Object> newAttributes = newAttributesList.get(i); + + Map<String, Object> delta = new HashMap<String, Object>(); + + Set<String> oldKeySet = oldAttributes.keySet(); + Set<String> newKeySet = newAttributes.keySet(); + + Set<String> commonKeys = new HashSet<String>(newKeySet); commonKeys.retainAll(oldKeySet); + Set<String> addedKeys = new HashSet<String>(newKeySet); addedKeys.removeAll(oldKeySet); + Set<String> removedKeys = new HashSet<String>(oldKeySet); removedKeys.removeAll(newKeySet); + + // Add the modified attributes + for (String key : commonKeys) { + if (!(oldAttributes.get(key).equals(newAttributes.get(key)))) + delta.put(key, newAttributes.get(key)); + } + + // Add the new attributes + for (String key : addedKeys) { + delta.put(key, newAttributes.get(key)); + } + + // Remove the deleted attributes + for (String key : removedKeys) { + delta.put(key, null); + } + deltas.add(delta); + } + + // Add all the new attributes as deltas + for (int i = deltas.size(); i < newAttributesList.size(); i++) { + deltas.add(newAttributesList.get(i)); + } + + // For any old attribute Maps that were removed, insert a null value in the deltas list. + for (int i = deltas.size(); i < oldAttributesList.size(); i++) { + deltas.add(null); + } + + return deltas; + } + + /////////////////////////////////////////////////////////////////////////// + // IBreakpointManagerListener implementation + /////////////////////////////////////////////////////////////////////////// + + public void breakpointManagerEnablementChanged(boolean enabled) { + for (IBreakpoint breakpoint : fBreakpointManager.getBreakpoints()) { + breakpointChanged(breakpoint, null); + } + } + + @ThreadSafe + public void breakpointAdded(final IBreakpoint breakpoint) { + if (fAttributeTranslator.supportsBreakpoint(breakpoint)) { + try { + // Retrieve the breakpoint attributes + final List<Map<String, Object>> attrsArray = + fAttributeTranslator.getBreakpointAttributes(breakpoint, fBreakpointManager.isEnabled()); + + getExecutor().execute(new DsfRunnable() { + public void run() { + //TODO pp: need to track pending requests + + final CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), null) { + @Override + protected void handleError() { + if (getStatus().getSeverity() == IStatus.ERROR) { + DsfPlugin.getDefault().getLog().log(getStatus()); + } + } + }; + countingRm.setDoneCount(fPlatformBPs.size()); + + // Install the breakpoint in all the execution contexts + for (final IBreakpointsTargetDMContext dmc : fPlatformBPs.keySet()) { + installBreakpoint(dmc, breakpoint, attrsArray, new RequestMonitor(getExecutor(), countingRm)); + } + } + }); + } catch (CoreException e) { + DsfPlugin.getDefault().getLog().log(e.getStatus()); + } catch (RejectedExecutionException e) { + } + } + + } + + /////////////////////////////////////////////////////////////////////////// + // IBreakpointListener implementation + /////////////////////////////////////////////////////////////////////////// + + public void breakpointChanged(final IBreakpoint breakpoint, final IMarkerDelta delta) { + if (fAttributeTranslator.supportsBreakpoint(breakpoint)) { + try { + // Retrieve the breakpoint attributes + final List<Map<String, Object>> attrsArray = + fAttributeTranslator.getBreakpointAttributes(breakpoint, fBreakpointManager.isEnabled()); + + // Modify the breakpoint in all the target contexts + getExecutor().execute( new DsfRunnable() { + public void run() { + + // If the breakpoint is currently being updated, queue the request and exit + if (fPendingRequests.contains(breakpoint)) { + fPendingBreakpoints.add(breakpoint); + return; + } + + // Keep track of the updates + final CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), null) { + @Override + protected void handleCompleted() { + + if (!isSuccess()) { + if (getStatus().getSeverity() == IStatus.ERROR) { + DsfPlugin.getDefault().getLog().log(getStatus()); + } + } + + // Indicate that the pending request has completed + fPendingRequests.remove(breakpoint); + + // Process the next pending update for this breakpoint + if (fPendingBreakpoints.contains(breakpoint)) { + fPendingBreakpoints.remove(breakpoint); + new Job("Deferred breakpoint changed job") { //$NON-NLS-1$ + { setSystem(true); } + @Override + protected IStatus run(IProgressMonitor monitor) { + breakpointChanged(breakpoint, delta); + return Status.OK_STATUS; + }; + }.schedule(); + } + } + }; + countingRm.setDoneCount(fPlatformBPs.size()); + + // Mark the breakpoint as being updated and go + fPendingRequests.add(breakpoint); + + // Modify the breakpoint in all the execution contexts + for (final IBreakpointsTargetDMContext dmc : fPlatformBPs.keySet()) { + modifyBreakpoint(dmc, breakpoint, attrsArray, delta, new RequestMonitor(getExecutor(), countingRm)); + } + } + }); + } catch (CoreException e) { + DsfPlugin.getDefault().getLog().log(e.getStatus()); + } catch (RejectedExecutionException e) { + } + } + + } + + public void breakpointRemoved(final IBreakpoint breakpoint, IMarkerDelta delta) { + + if (fAttributeTranslator.supportsBreakpoint(breakpoint)) { + try { + getExecutor().execute(new DsfRunnable() { + public void run() { + //TODO pp: need to track pending requests + + CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), null) { + @Override + protected void handleError() { + if (getStatus().getSeverity() == IStatus.ERROR) { + DsfPlugin.getDefault().getLog().log(getStatus()); + } + } + }; + countingRm.setDoneCount(fPlatformBPs.size()); + + // Remove the breakpoint in all the execution contexts + for (IBreakpointsTargetDMContext dmc : fPlatformBPs.keySet()) { + if (fPlatformBPs.get(dmc).remove(breakpoint) != null) { + uninstallBreakpoint(dmc, breakpoint, countingRm); + } else { + // Breakpoint not installed for given context, do nothing. + } + } + } + }); + } catch (RejectedExecutionException e) { + } + } + + } +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IBreakpointAttributeTranslator.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IBreakpointAttributeTranslator.java new file mode 100644 index 00000000000..fe158fb3073 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IBreakpointAttributeTranslator.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * 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.service; + +import java.util.List; +import java.util.Map; + +import org.eclipse.cdt.dsf.concurrent.ThreadSafeAndProhibitedFromDsfExecutor; +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointDMContext; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.model.IBreakpoint; + + +@ThreadSafeAndProhibitedFromDsfExecutor("") +public interface IBreakpointAttributeTranslator { + + public void initialize(BreakpointsMediator mediator); + + public void dispose(); + + public List<Map<String, Object>> getBreakpointAttributes(IBreakpoint breakpoint, boolean bpManagerEnabled) throws CoreException; + + public boolean canUpdateAttributes(IBreakpointDMContext bp, Map<String, Object> delta); + + public boolean supportsBreakpoint(IBreakpoint bp); + + public void updateBreakpointStatus(IBreakpoint bp); +}
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IBreakpoints.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IBreakpoints.java new file mode 100644 index 00000000000..1fca358fa57 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IBreakpoints.java @@ -0,0 +1,140 @@ +/******************************************************************************* + * 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 - Revisited the API + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.service; + +import java.util.Map; + +import org.eclipse.cdt.core.IAddress; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMEvent; +import org.eclipse.cdt.dsf.service.IDsfService; + +/** + * Breakpoint service interface + */ +public interface IBreakpoints extends IDsfService { + + + /** + * Marker interface for a context for which breakpoints can be installed + */ + public interface IBreakpointsTargetDMContext extends IDMContext {} + + /** + * Specific breakpoint context + */ + @Immutable + public interface IBreakpointDMContext extends IDMContext {} + + /** + * Breakpoint events + */ + public interface IBreakpointsChangedEvent extends IDMEvent<IBreakpointsTargetDMContext> { + public IBreakpointDMContext[] getBreakpoints(); + } + + public interface IBreakpointsAddedEvent extends IBreakpointsChangedEvent {} + public interface IBreakpointsUpdatedEvent extends IBreakpointsChangedEvent {} + public interface IBreakpointsRemovedEvent extends IBreakpointsChangedEvent {} + + /** + * Effective breakpoint data as held by the back-end. + */ + public interface IBreakpointDMData { + + public String getBreakpointType(); + public String getFileName(); + public int getLineNumber(); + public String getFunctionName(); + public IAddress[] getAddresses(); + public String getCondition(); + public int getIgnoreCount(); + public boolean isEnabled(); + public String getExpression(); + } + + /** + * Retrieves the list of breakpoints installed in the context. + * + * Use getBreakpointDMData() to retrieve individual breakpoints. + * + * @param context the execution context of the breakpoint + * @param drm the list of breakpoints in the execution context + */ + public void getBreakpoints(IBreakpointsTargetDMContext context, + DataRequestMonitor<IBreakpointDMContext[]> drm); + + /** + * Retrieves a specific breakpoint from the service. + * + * @param dmc the breakpoint reference + * @param drm the DRM returning the breakpoint data + */ + public void getBreakpointDMData(IBreakpointDMContext dmc, + DataRequestMonitor<IBreakpointDMData> drm); + + /** + * Adds a breakpoint on the target. + * + * The breakpoint context is returned in the DRM. The actual breakpoint + * object can be later be retrieved using getBreakpoint(bp_context). + * + * E.g.: + * IBreakpointDMContext ref = insertBreakpoint(...); + * IBreakpointDMData bp = getBreakpointDMData(ref); + * + * If the breakpoint is a duplicate (already set previously), then it is up to + * the back-end to decide if it is an error or not. + * + * @param context the execution context of the breakpoint + * @param attributes the breakpoint attributes + * @param drm the DRM returning the breakpoint reference + */ + public void insertBreakpoint(IBreakpointsTargetDMContext context, + Map<String,Object> attributes, + DataRequestMonitor<IBreakpointDMContext> drm); + + /** + * Removes the breakpoint on the target. + * + * If the breakpoint doesn't exist, silently ignore it. + * + * @param dmc the context of the breakpoints to remove + * @param rm the asynchronous request monitor + */ + public void removeBreakpoint(IBreakpointDMContext dmc, + RequestMonitor rm); + + /** + * Updates the breakpoint properties on the target. + * + * To add/update/remove a property, simply create a map with + * the desired value(s) for the given key(s). + * + * Properties that affect the breakpoint nature or location + * should not be updated. Instead, the breakpoint should be + * removed then re-inserted. + * + * A null value is used for removal of a property e.g.: + * delta.set(some_key, null); + * + * @param delta the delta properties + * @param dmc the context of the breakpoints to modify + * @param rm the asynchronous request monitor + */ + public void updateBreakpoint(IBreakpointDMContext dmc, + Map<String,Object> delta, RequestMonitor drm); + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/ICachingService.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/ICachingService.java new file mode 100644 index 00000000000..35d61287299 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/ICachingService.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * 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.service; + +import org.eclipse.cdt.dsf.datamodel.IDMContext; + +/** + * Interface for services which use an internal cache for data. + * @since 1.1 + */ +public interface ICachingService { + + /** + * Clears the service cache entries which have the given context in their + * hierarchy. + * @param context Root context to flush. May be <code>null</code> to flush + * the entire cache. + */ + public void flushCache(IDMContext context); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IDisassembly.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IDisassembly.java new file mode 100644 index 00000000000..0f751e32aac --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IDisassembly.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (c) 2008 Ericsson 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 - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.debug.service; + +import java.math.BigInteger; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.service.IDsfService; + +/** + * Disassembly service interface + */ +public interface IDisassembly extends IDsfService { + + public interface IDisassemblyDMContext extends IDMContext {} + + /** + * Gets the disassembled code from an address range. + * If [startAddress] == null, disassemble from the instruction pointer. + * + * @param context Context of the disassembly code + * @param startAddress Beginning address + * @param endAddress End address + * @param drm Disassembled code + */ + public void getInstructions( + IDisassemblyDMContext context, + BigInteger startAddress, + BigInteger endAddress, + DataRequestMonitor<IInstruction[]> drm); + + /** + * Gets the disassembled code from a file location. + * If [lines] == -1, the whole function is disassembled. + * + * @param context Context of the disassembly code + * @param filename File to disassemble + * @param linenum Line number within the file + * @param lines Number of lines of disassembled code to produce + * @param drm Disassembled code + */ + public void getInstructions( + IDisassemblyDMContext context, + String filename, + int linenum, + int lines, + DataRequestMonitor<IInstruction[]> drm); + + /** + * Gets the mixed disassembled code from an address range. + * If [startAddress] == null, disassemble from the instruction pointer. + * + * @param context Context of the disassembly code + * @param startAddress Beginning address + * @param endAddress End address + * @param drm Disassembled code + */ + public void getMixedInstructions( + IDisassemblyDMContext context, + BigInteger startAddress, + BigInteger endAddress, + DataRequestMonitor<IMixedInstruction[]> drm); + + /** + * Gets the mixed disassembled code from a file location. + * If [lines] == -1, the whole function is disassembled. + * + * @param context Context of the disassembly code + * @param filename File to disassemble + * @param linenum Line number within the file + * @param lines Number of lines of disassembled code to produce + * @param drm Disassembled code + */ + public void getMixedInstructions( + IDisassemblyDMContext context, + String filename, + int linenum, + int lines, + DataRequestMonitor<IMixedInstruction[]> drm); + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IDsfBreakpointExtension.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IDsfBreakpointExtension.java new file mode 100644 index 00000000000..e71d8091b2c --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IDsfBreakpointExtension.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * 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.service; + +import org.eclipse.cdt.debug.core.model.ICBreakpointExtension; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.core.runtime.CoreException; + +/** + * An extension to {@link ICBreakpoint} with model-specific breakpoint + * attributes. Different debug models can use the standard C breakpoints that + * extend the basic <code>ICBreakpoint</code>. The can use this extension + * mechanism to edit and store model-specific data in the original breakpoint + * object. + * + * A breakpoint extension is defined by an extension of kind + * <code>"org.eclipse.cdt.debug.core.BreakpointExtension"</code></li>. + * The <code>ICBreakpoint</code> implementation instantiates breakpoint + * extensions registered for its specific marker type when a client requests + * extensions for a given debug model type. Thus the extension classes and + * plugins that declare them are not loaded unless requested by a client. + * + * @see ICBreakpoint#getExtension(String, Class) + */ +public interface IDsfBreakpointExtension extends ICBreakpointExtension { + + public void setTargetFilter( IContainerDMContext target ) throws CoreException; + public void removeTargetFilter( IContainerDMContext target ) throws CoreException; + public IContainerDMContext[] getTargetFilters() throws CoreException; + + public void setThreadFilters( IExecutionDMContext[] threads ) throws CoreException; + public void removeThreadFilters( IExecutionDMContext[] threads ) throws CoreException; + public IExecutionDMContext[] getThreadFilters( IContainerDMContext target ) throws CoreException; + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IDsfDebugServicesFactory.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IDsfDebugServicesFactory.java new file mode 100644 index 00000000000..5b3aab48852 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IDsfDebugServicesFactory.java @@ -0,0 +1,22 @@ +/******************************************************************************* + * Copyright (c) 2008 Ericsson 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 - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.service; + +import org.eclipse.cdt.dsf.service.DsfSession; + +/** + * A factory to create DSF services. Using this interface allows + * to easily have different service implementation for different backends. + * @since 1.1 + */ +public interface IDsfDebugServicesFactory { + <V> V createService(Class<V> clazz, DsfSession session, Object ... optionalArguments); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IExpressions.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IExpressions.java new file mode 100644 index 00000000000..adedef76125 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IExpressions.java @@ -0,0 +1,231 @@ +/******************************************************************************* + * 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 - Update for GDB/MI + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.service; + +import java.util.Map; + +import org.eclipse.cdt.core.IAddress; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMData; +import org.eclipse.cdt.dsf.datamodel.IDMEvent; + +/** + * Expressions service provides access to the debugger's expression evaluator. This service has + * dependencies on the Stack service, as it is be used to provide context for an + * expression to be evaluated. + */ +@SuppressWarnings("nls") +public interface IExpressions extends IFormattedValues { + + /** + * Expression context. + */ + public interface IExpressionDMContext extends IFormattedDataDMContext { + /** + * Returns a fully qualified expression string represented by this context. This + * expression string is the same as the string that is sent to the debug engine to be + * evaluated in context of a stack frame, thread, or a symbol context. + */ + String getExpression(); + } + + /** + * The address and size of an expression. + */ + public interface IExpressionDMAddress { + IAddress getAddress(); + int getSize(); + } + + /** + * This is the model data interface that corresponds to IExpressionDMContext. + */ + public interface IExpressionDMData extends IDMData { + // These static fields define the possible return values of method getTypeId(). + + final static String TYPEID_UNKNOWN = "TYPEID_UNKNOWN"; + final static String TYPEID_INTEGER = "TYPEID_INTEGER"; + final static String TYPEID_CHAR = "TYPEID_CHAR"; + final static String TYPEID_FLOAT = "TYPEID_FLOAT"; + final static String TYPEID_DOUBLE = "TYPEID_DOUBLE"; + final static String TYPEID_OPAQUE = "TYPEID_OPAQUE"; + + /** + * This enumerates the possible basic types that an expression can have. + * + * @see getBasicType(). + */ + enum BasicType { + unknown, // Unknown type. + basic, // Scalar type (e.g., int, short, float). + pointer, // Pointer to anything. + array, // Array of anything. + composite, // Struct, union, or class. + enumeration, // Enumeration. + function // Function. + } + + /** + * If this expression is a sub-expression of another expression, this method returns + * the expression relative to the parent of this expression. Otherwise this method + * will return the same string as {@link #getExpression()}. + */ + String getName(); + + /** + * @return A BasicType enumerator describing the basic type of an expression. + */ + BasicType getBasicType(); + + /** + * @return The source code type of this expression. This is a string such as "struct Foo", "short", + * "int *", "mytypedef", "(int *)[]", "enum Bar". If the debugger backend cannot supply + * this information, this method returns "<UNKNOWN>" (the angle brackets are there just in + * case there is a type named "UNKNOWN" in the application). + */ + String getTypeName(); + + /** + * This method needs to be defined. For now, this returns the empty string. + */ + String getEncoding(); + + /** + * @return One of the TYPEID_* static field values defined by this interface. + */ + String getTypeId(); + + /** + * @return A Map in which the keys are strings that are the names of enumerators in the enumeration + * that is the value of this expression and the values are the integer values of the + * enumerators. If the expression type is not an enumeration, this returns an empty Map. + */ + Map<String, Integer> getEnumerations(); + + /** + * This method needs to be defined. + */ + IRegisters.IRegisterDMContext getRegister(); + } + + /** + * Event indicating that a given expression is changed. If an expression is changed, it's implied that all + * the children of that expression are changed too. + */ + public interface IExpressionChangedDMEvent extends IDMEvent<IExpressionDMContext> {} + + /** + * Retrieves the expression DM data object for the given expression context(<tt>dmc</tt>). + * + * @param dmc + * The ExpressionDMC for the expression to be evaluated. + * @param rm + * The data request monitor that will contain the requested data + */ + void getExpressionData(IExpressionDMContext dmc, DataRequestMonitor<IExpressionDMData> rm); + + /** + * Retrieves the address and size of an expression given by the expression context(<tt>dmc</tt>). + * Non-lvalues do not have an addresses (e.g., "x + 5"). When the expression +- * has no address, the data request monitor will contain null. + * + * @param dmc + * The ExpressionDMC for the expression + * @param rm + * The data request monitor that will contain the requested data + */ + void getExpressionAddressData(IExpressionDMContext dmc, DataRequestMonitor<IExpressionDMAddress> rm); + + /** + * Returns the data model context object for the specified expression in the context + * specified by <b>ctx</b>. + * + * @param ctx: Context in which to evaluate the expression. This context could include the + * PC location, stack frame, thread, or just a symbol context. + * + * @param expression: The expression to evaluate. + * + * @return An expression data model context object that must be passed to + * getModelData() to obtain the value of the expression. + */ + IExpressionDMContext createExpression(IDMContext ctx, String expression); + + /** + * Retrieves the sub-expressions of the given expression. Sub-expressions are fields of a struct, union, + * or class, the enumerators of an enumeration, and the element of an array. + * + * @param exprCtx: The data model context representing an expression. + * + * @param rm: Request completion monitor containing an array of all sub-expressions + */ + void getSubExpressions(IExpressionDMContext exprCtx, DataRequestMonitor<IExpressionDMContext[]> rm); + + /** + * Retrieves a particular range of sub-expressions of the given expression. + * Sub-expressions are fields of a struct, union, or class, the enumerators + * of an enumeration, and the element of an array. + * + * @param exprCtx: The data model context representing an expression. + * startIndex: Index of the first sub-expression to retrieve + * length: Total number of sub-expressions to retrieve + * + * @param rm: Request completion monitor containing an array of the requested + * range of sub-expressions + */ + void getSubExpressions(IExpressionDMContext exprCtx, int startIndex, int length, + DataRequestMonitor<IExpressionDMContext[]> rm); + + /** + * Retrieves the number of sub-expressions of the given expression. Sub-expressions are fields of a struct, union, + * or class, the enumerators of an enumeration, and the element of an array. + * + * @param exprCtx: The data model context representing an expression. + * + * @param rm: Request completion monitor containing the number of sub-expressions + * of the specified expression + */ + void getSubExpressionCount(IExpressionDMContext exprCtx, DataRequestMonitor<Integer> rm); + + /** + * For object oriented languages, this method returns the expressions representing base types of + * the given expression type. + * + * @param exprContext: The data model context representing an expression. + * + * @param rm: Request completion monitor. + */ + void getBaseExpressions(IExpressionDMContext exprContext, DataRequestMonitor<IExpressionDMContext[]> rm); + + /** + * This method indicates if an expression can be written to. + * + * @param expressionContext: The data model context representing an expression. + * + * @param rm: Data Request monitor containing True if this expression's value can be edited. False otherwise. + */ + void canWriteExpression(IExpressionDMContext expressionContext, DataRequestMonitor<Boolean> rm); + + /** + * This method supports the writing/modifying the value of the expression. + * + * @param expressionContext: The data model context representing an expression. + * + * @param expressionValue: The new value of the expression as a String. + * + * @param formatId: The format ID specifying the format of parameter <b>expressionValue</b>. + * + * @param rm: Request completion monitor. + */ + void writeExpression(IExpressionDMContext expressionContext, String expressionValue, String formatId, RequestMonitor rm); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IFormattedValues.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IFormattedValues.java new file mode 100644 index 00000000000..5cc665aebb3 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IFormattedValues.java @@ -0,0 +1,118 @@ +/******************************************************************************* + * 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.service; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.datamodel.AbstractDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMData; +import org.eclipse.cdt.dsf.datamodel.IDMService; + +public interface IFormattedValues extends IDMService { + + /** Marker interface for a DMC that has a formatted value. */ + public interface IFormattedDataDMContext extends IDMContext {} + + /** + * These strings represent the standard known formats for any bit stream + * which needs to be formatted. These ID's as well as others which may be + * specifically available from the backend are what is returned from the + * getID() method. + */ + public final static String HEX_FORMAT = "HEX.Format" ; //$NON-NLS-1$ + public final static String OCTAL_FORMAT = "OCTAL.Format" ; //$NON-NLS-1$ + public final static String NATURAL_FORMAT = "NATURAL.Format" ; //$NON-NLS-1$ + public final static String BINARY_FORMAT = "BINARY.Format" ; //$NON-NLS-1$ + public final static String DECIMAL_FORMAT = "DECIMAL.Format" ; //$NON-NLS-1$ + public final static String STRING_FORMAT = "STRING.Format" ; //$NON-NLS-1$ + + /** + * Retrieves the formats that the given data is available in. + * This method is asynchronous because the service may need to retrieve + * information from the backend in order to determine what formats are + * available for the given data context. + * + * @param dmc Context for which to retrieve available formats. + * @param rm Completion monitor returns an array of support formatIds. + */ + public void getAvailableFormats(IFormattedDataDMContext dmc, DataRequestMonitor<String[]> rm); + + /** + * Creates a FormattedValueDMContext representing the given formatId. + * + * @param dmc Parent context for the context that is being created + * @param formatId Defines format to be used for the returned context. + */ + public FormattedValueDMContext getFormattedValueContext(IFormattedDataDMContext dmc, String formatId); + + /** + * Retrieves the DM data associated with given formatted value context. + * @param dmc Context to retrieve the value for. + * @param rm Completion monitor returns the formatted value. + */ + public void getFormattedExpressionValue(FormattedValueDMContext dmc, DataRequestMonitor<FormattedValueDMData> rm); + + + /** + * DMC that represents a value with specific format. The format ID can be + * persisted and used for comparison. + */ + + public static class FormattedValueDMContext extends AbstractDMContext + { + private final String fFormatID; + + public FormattedValueDMContext(IDMService service, IDMContext parent, String formatId) { + super(service, new IDMContext[] { parent }); + fFormatID = formatId; + } + + public FormattedValueDMContext(String sessionId, IDMContext parent, String formatId) { + super(sessionId, new IDMContext[] { parent }); + fFormatID = formatId; + } + + public String getFormatID() { + return fFormatID; + } + + @Override + public boolean equals(Object obj) { + return baseEquals(obj) && ((FormattedValueDMContext)obj).getFormatID().equals(getFormatID()); + } + + @Override + public int hashCode() { + return baseHashCode() + getFormatID().hashCode(); + } + + @Override + public String toString() { + return baseToString() + ".format(" + getFormatID() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + public static class FormattedValueDMData implements IDMData { + + private final String fValue; + + public FormattedValueDMData(String value) { + fValue = value; + } + + public String getFormattedValue() { + return fValue; + } + + + } +}
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IInstruction.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IInstruction.java new file mode 100644 index 00000000000..dd547ff5746 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IInstruction.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2008 Ericsson 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 - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.debug.service; + +import java.math.BigInteger; + +/** + * Represents an assembly instruction + */ +public interface IInstruction { + + /** + * @return the instruction address. + */ + BigInteger getAdress(); + + /** + * @return the function name. + */ + String getFuntionName(); + + /** + * @return the offset of this machine instruction + */ + long getOffset(); + + /** + * @return the instruction. + */ + String getInstruction(); + + /** + * @return the opcode + */ + String getOpcode(); + + /** + * @return any arguments to the instruction + */ + String getArgs(); + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IMemory.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IMemory.java new file mode 100644 index 00000000000..a8444301527 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IMemory.java @@ -0,0 +1,118 @@ +/******************************************************************************* + * Copyright (c) 2006, 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 + * Ericsson AB - extended the API for IMemoryBlockExtension + * Ericsson AB - added support for 64 bit processors + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.service; + +import org.eclipse.cdt.core.IAddress; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMEvent; +import org.eclipse.cdt.dsf.service.IDsfService; +import org.eclipse.debug.core.model.MemoryByte; + +/** + * Service for accessing memory. Memory contexts are not meant to be + * represented in tree or table views, so it doesn't need to implement + * IDMService interface. + */ +public interface IMemory extends IDsfService { + + public interface IMemoryDMContext extends IDMContext {} + + /** + * Event generated every time a range of bytes is modified. + * + * A client wishing to receive such events has to register as a service + * event listener and implement the corresponding eventDispatched method. + * + * E.g.: + * + * MyMemoryBlock(MIRunControl fRunControl) + * { + * ... + * fRunControl.getSession().addServiceEventListener(MyMemoryBlock.this, null); + * ... + * } + * + * @DsfServiceEventHandler + * public void eventDispatched(MemoryChangedEvent e) { + * IDMContext<?> context = e.getContext(); + * IAddress[] addresses = e.getAddresses(); + * // do whatever... + * } + */ + public interface IMemoryChangedEvent extends IDMEvent<IMemoryDMContext> { + IAddress[] getAddresses(); + } + + /** + * Reads a memory block from the target. + * + * An asynchronous memory read request at [address] + [offset] for + * [count] memory items, each of size [word_size] bytes, will be + * issued to the target. The result will be stored in [drm] upon + * completion of the call. + * + * The [drm] result buffer will be of size [word_size] * [count]. The + * successfully read bytes will have their MemoryByte.READABLE flag + * set while the bytes in error (unreachable/bad memory) will have their + * flag byte set to 0. The bytes will respect the target "endianness". + * + * @param context the context of the target memory block + * @param address the memory block address (on the target) + * @param offset the offset from the start address + * @param word_size the size, in bytes, of an addressable item + * @param count the number of data elements to read + * @param drm the asynchronous data request monitor + */ + public void getMemory(IMemoryDMContext context, IAddress address, long offset, + int word_size, int count, DataRequestMonitor<MemoryByte[]> drm); + + /** + * Writes a memory block on the target. + * + * An asynchronous memory write request at [address] + [offset] for + * [count] * [word_size] bytes will be issued to the target. + * + * The [buffer] must hold at least [count] * [word_size] bytes. + * + * A MemoryChangedEvent will be generated for the range of addresses. + * + * @param context the context of the target memory block + * @param address the memory block address (on the target) + * @param offset the offset from the start address + * @param word_size the size, in bytes, of an addressable item + * @param count the number of data elements to write + * @param buffer the source buffer + * @param rm the asynchronous data request monitor + */ + public void setMemory(IMemoryDMContext context, IAddress address, long offset, + int word_size, int count, byte[] buffer, RequestMonitor rm); + + /** + * Writes [pattern] at memory [address] + [offset], [count] times. + * + * A MemoryChangedEvent will be generated for the range of addresses. + * + * @param context the context of the target memory block + * @param address the memory block address (on the target) + * @param offset the offset from the start address + * @param word_size the size, in bytes, of an addressable item + * @param count the number of times [pattern] will be written + * @param pattern the source buffer + * @param rm the asynchronous data request monitor + */ + public void fillMemory(IMemoryDMContext context, IAddress address, long offset, + int word_size, int count, byte[] pattern, RequestMonitor rm); + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IMixedInstruction.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IMixedInstruction.java new file mode 100644 index 00000000000..511f81f1785 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IMixedInstruction.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2008 Ericsson 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 - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.debug.service; + +/** + * Represents the assembly instruction(s) corresponding to a source line + */ +public interface IMixedInstruction { + + /** + * @return the file name + */ + String getFileName(); + + /** + * @return the line Number. + */ + int getLineNumber(); + + /** + * @return the array of instruction. + */ + IInstruction[] getInstructions(); + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IModules.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IModules.java new file mode 100644 index 00000000000..c01ea5db89a --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IModules.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * 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.service; + +import org.eclipse.cdt.core.IAddress; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMEvent; +import org.eclipse.cdt.dsf.service.IDsfService; + +/** + * Debugger service representing module handling logic of a debugger. + */ +public interface IModules extends IDsfService { + + /** + * Symbol context represents the space into which module symbols are loaded. + * Traditionally symbols are loaded in context of a process, but for other + * types of debugging, like kernel or no-OS debugging, it's useful to + * separate the concept of a symbol context from a process. + */ + public interface ISymbolDMContext extends IDMContext {} + + /** + * Module context represents a single module that is loaded. + */ + public interface IModuleDMContext extends IDMContext {} + + /** + * Event indicating a change in the symbol information for given context. + */ + public interface ModulesChangedDMEvent extends IDMEvent<ISymbolDMContext> {} + + /** + * Specific event identifying that a new module was loaded into a + * symbol context. + */ + public interface ModuleLoadedDMEvent extends ModulesChangedDMEvent { + /** Returns context of the module that was loaded */ + IModuleDMContext getLoadedModuleContext(); + } + + public interface ModuleUnloadedDMEvent extends ModulesChangedDMEvent { + /** Returns context of the module that was un-loaded */ + IModuleDMContext getUnloadedModuleContext(); + } + + /** Module information. */ + public interface IModuleDMData { + String getName(); + String getFile(); + long getTimeStamp(); + String getBaseAddress(); + String getToAddress(); + boolean isSymbolsLoaded(); + long getSize(); + } + + /** Line information about a particular address */ + public interface LineInfo { + IAddress getAddress(); + String getSourceFile(); + int getStartLine(); + int getStartColumn(); + int getEndLine(); + int getEndColumn(); + } + + /** Address information about a particular file/line */ + public interface AddressRange { + IAddress getStartAddress(); + IAddress getEndAddress(); + } + + void getModuleData(IModuleDMContext dmc, DataRequestMonitor<IModuleDMData> rm); + + /** + * Retreives the list of modules loaded in given symbol context. + */ + void getModules(ISymbolDMContext symCtx, DataRequestMonitor<IModuleDMContext[]> rm); + + /** + * Calculates the line numbers corresponding to the given address. + */ + void calcLineInfo(ISymbolDMContext symCtx, IAddress address, DataRequestMonitor<LineInfo[]> rm); + + /** + * Calculates the addresses corresponding to the given source file location. + */ + void calcAddressInfo(ISymbolDMContext symCtx, String file, int line, int col, DataRequestMonitor<AddressRange[]> rm); + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IProcesses.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IProcesses.java new file mode 100644 index 00000000000..db65c2c89ed --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IProcesses.java @@ -0,0 +1,181 @@ +/******************************************************************************* + * 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 - Updated for latest DSF version + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.service; + +import java.util.Map; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMData; +import org.eclipse.cdt.dsf.datamodel.IDMEvent; +import org.eclipse.cdt.dsf.datamodel.IDMService; + +/** + * This interface provides access to the OS's process + * information, manipulation methods, and debugging methods. + * This service provides a relatively simple interface for + * manipulating processes as compared with a full-blown + * remote target debugger. + * @since 1.1 + */ +public interface IProcesses extends IDMService { + + /** + * A thread as known by the OS. + * This context is kept different than {@link IRunControl.IExecutionDMContext} + * because the OS id of a thread may not be the same as the thread id used by + * the debugger when doing run control operations. + */ + public interface IThreadDMContext extends IDMContext {} + + /** + * A process as known by the OS. + * This context is kept different than {@link IRunControl.IContainerDMContext} + * because the OS id of a process may not be the same as the process id used by + * the debugger when doing run control operations. + */ + public interface IProcessDMContext extends IThreadDMContext {} + + /** + * Interface for thread and process object data. This data provides a link + * to the lower level debugger services, in form of execution contexts. + */ + public interface IThreadDMData extends IDMData { + String getName(); + String getId(); + boolean isDebuggerAttached(); + } + + /** + * Event indicating that process data has changed. + */ + public interface ProcessChangedDMEvent extends IDMEvent<IProcessDMContext> {} + + /** + * Retrieves thread or process data for given context. + * @param dmc Context to retrieve data for. + * @param rm Request completion monitor. + */ + public void getExecutionData(IThreadDMContext dmc, DataRequestMonitor<IThreadDMData> rm); + + /** + * Retrieves the debugging context that characterizes the specified thread + * or process context. + * + * @param dmc The thread or process dmc for which we want the debugging context + * @param rm The request monitor that will contain the debugging context. + * null if no such context exists + */ + public void getDebuggingContext(IThreadDMContext dmc, DataRequestMonitor<IDMContext> rm); + + /** + * Retrieves the current list of processes running on target. + * @param dmc The processor or core for which to list all processes + * @param rm Request completion monitor, to be filled in with array of process contexts. + */ + void getRunningProcesses(IDMContext dmc, DataRequestMonitor<IProcessDMContext[]> rm); + + /** + * Checks whether it is possible to attach the debugger to a new process. + * @param dmc The processor or core on which we want to attach to a process. + * @param rm Return if it is possible to attach. + */ + void isDebuggerAttachSupported(IDMContext dmc, DataRequestMonitor<Boolean> rm); + + /** + * Attaches debugger to the given process. + * When attaching to a process, a debugging context can now be used to characterize the process. + * This method can optionally choose to return this IDMContext inside the DataRequestMonitor. + * This can be useful for backends that do not have the ability to obtain the different + * debugging IDMContexts through {@link #getProcessesBeingDebugged(IDMContext, DataRequestMonitor) + */ + void attachDebuggerToProcess(IProcessDMContext procCtx, DataRequestMonitor<IDMContext> rm); + + /** + * Checks whether it is possible to detach the debugger from the specified process. + * @param dmc The debugging context from which we want to detach. This context + * should have IProcessDMContext as an ancestor. + * @param rm Return if it is possible to detach. + */ + void canDetachDebuggerFromProcess(IDMContext dmc, DataRequestMonitor<Boolean> rm); + + /** + * Detaches debugger from the given process. + * @param dmc The debugging context from which we want to detach. This context + * should have IProcessDMContext as an ancestor. + */ + void detachDebuggerFromProcess(IDMContext dmc, RequestMonitor requestMonitor); + + /** + * Checks whether it is possible to run a new process. + * @param dmc The processor or core on which we want to run a new process. + * @param rm Return if it is possible to run a new process. + */ + void isRunNewProcessSupported(IDMContext dmc, DataRequestMonitor<Boolean> rm); + + /** + * Starts a new process. + * @param dmc The processor or core on which we want to run a new process. + * @param file Process image to use for the new process. + * @param attributes Attributes that give information on the process to be debugged + * @param rm Request completion monitor, to be filled in with the process context. + */ + void runNewProcess(IDMContext dmc, + String file, + Map<String, Object> attributes, + DataRequestMonitor<IProcessDMContext> rm); + + /** + * Checks whether it is possible to start a new process with the debugger attached + * @param dmc The processor or core on which we want to start and debug the new process. + * @param rm Return if it is possible to start a new process with the debugger attached. + */ + void isDebugNewProcessSupported(IDMContext dmc, DataRequestMonitor<Boolean> rm); + + /** + * Starts a new process with the debugger attached. + * @param dmc The processor or core on which we want to start and debug the new process. + * @param file Process image to use for the new process. + * @param attributes Attributes that give information on the process to be debugged + * @param rm Request completion monitor, to be filled in with the + * debugging context that can now be used to characterize the process + */ + void debugNewProcess(IDMContext dmc, + String file, + Map<String, Object> attributes, + DataRequestMonitor<IDMContext> rm); + + /** + * Retrieves the list of processes which are currently under debugger control. + * + * @param dmc The processor or core for which to list processes being debugged + * @param rm Request completion monitor which contains all debugging contexts representing + * the processes being debugged. Note that each of these contexts should also have + * IProcessDMContext as a parent. + */ + void getProcessesBeingDebugged(IDMContext dmc, DataRequestMonitor<IDMContext[]> rm); + + /** + * Checks whether the given process or thread can be terminated. + * @param thread Thread or process to terminate. + * @param rm Return token. + */ + void canTerminate(IThreadDMContext thread, DataRequestMonitor<Boolean> rm); + + /** + * Terminates the selected process or thread. + * @param thread Thread or process to terminate. + * @param rm Request completion monitor, indicates success or failure. + */ + void terminate(IThreadDMContext thread, RequestMonitor requestMonitor); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IRegisters.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IRegisters.java new file mode 100644 index 00000000000..7397ead772b --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IRegisters.java @@ -0,0 +1,208 @@ +/******************************************************************************* + * 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.service; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMData; +import org.eclipse.cdt.dsf.datamodel.IDMEvent; + +/** + * Service for accessing register data. + */ +public interface IRegisters extends IFormattedValues { + + /** + * Event indicating groups have changed. The type of context returned by this + * event is generic, because different implementations of the the register service + * could configure register groups using different contexts. Some implementations + * could configure different register groups for each execution context, other + * services may have a global list of groups. + */ + public interface IGroupsChangedDMEvent extends IDMEvent<IDMContext> {} + + /** Register group context */ + public interface IRegisterGroupDMContext extends IFormattedDataDMContext { + } + + /** Event indicating values for the group have changed. */ + public interface IGroupChangedDMEvent extends IDMEvent<IRegisterGroupDMContext> {} + + /** Event indicating registers in a group have changed. */ + public interface IRegistersChangedDMEvent extends IDMEvent<IRegisterGroupDMContext> {} + + /** + * Register groups only have a name and description. Sub groups and registers are + * retrieved through the service interface. + */ + public interface IRegisterGroupDMData extends IDMData { + public String getName(); + public String getDescription(); + } + + /** Register context */ + public interface IRegisterDMContext extends IFormattedDataDMContext { + } + + /** Event indicating register value changed. */ + public interface IRegisterChangedDMEvent extends IDMEvent<IRegisterDMContext> {} + + /** Register information */ + public interface IRegisterDMData extends IDMData { + String getName(); + String getDescription(); + boolean isReadable(); + boolean isReadOnce(); + boolean isWriteable(); + boolean isWriteOnce(); + boolean hasSideEffects(); + boolean isVolatile(); + boolean isFloat(); + } + + /** Bit field context */ + public interface IBitFieldDMContext extends IFormattedDataDMContext { + } + + /** Event indicating register value changed. */ + public interface IBitFieldChangedDMEvent extends IDMEvent<IBitFieldDMContext> {} + + /** + * Bitfield data, big groups and mnemonics are retrieved at the same + * time as rest of bit field data + */ + public interface IBitFieldDMData extends IDMData { + String getName(); + String getDescription(); + boolean isReadable(); + boolean isReadOnce(); + boolean isWriteable(); + boolean isWriteOnce(); + boolean hasSideEffects(); + boolean isZeroBasedNumbering(); + boolean isZeroBitLeftMost(); + IBitGroup[] getBitGroup(); + IMnemonic[] getMnemonics(); + IMnemonic getCurrentMnemonicValue(); + } + + /** Bit group definition */ + public interface IBitGroup { + int startBit(); + int bitCount(); + } + + /** Bit field mnemonic */ + public interface IMnemonic { + String getShortName(); + String getLongName(); + } + + /** + * Retrieves the list of register groups. + * @param ctx Context for the returned data. + * @param rm Request completion monitor. + */ + void getRegisterGroups(IDMContext ctx, DataRequestMonitor<IRegisterGroupDMContext[]> rm); + + /** + * Retrieves the list of registers for the given context. The given context could include + * a register group and an execution context or just an execution context, in which case all + * registers for all groups should be returned. + * @param ctx Context for the returned data. + * @param rm Request completion monitor. + */ + void getRegisters(IDMContext ctx, DataRequestMonitor<IRegisterDMContext[]> rm); + + /** + * Retrieves bit fields for given register + * @param ctx Context for the returned data. + * @param rm Request completion monitor. + */ + void getBitFields(IDMContext ctx, DataRequestMonitor<IBitFieldDMContext[]> rm); + + /** + * Retrieves a Register Group context. The given context could include a register + * group and an execution context or just an execution context. + * @param ctx Context for the returned data. + * @param name Name of group being requested + * @param rm Request completion monitor. + */ + void findRegisterGroup(IDMContext ctx, String name, DataRequestMonitor<IRegisterGroupDMContext> rm); + + /** + * Retrieves a Register context. The given context could include a register group and an execution + * context or just an execution context. + * @param ctx Context for the returned data. + * @param name Name of register being requested + * @param rm Request completion monitor. + */ + void findRegister(IDMContext ctx, String name, DataRequestMonitor<IRegisterDMContext> rm); + + /** + * Retrieves bit field context. The given context could include a register and an execution + * context or just an execution context. + * @param ctx Context for the returned data. + * @param name Name of bit field being requested + * @param rm Request completion monitor. + */ + void findBitField(IDMContext ctx, String name, DataRequestMonitor<IBitFieldDMContext> rm); + + /** + * Retrieves register group data for given context. + * @param dmc Context to retrieve data for. + * @param rm Request completion monitor. + */ + void getRegisterGroupData(IRegisterGroupDMContext dmc, DataRequestMonitor<IRegisterGroupDMData> rm); + + /** + * Retrieves register data for given context. + * @param dmc Context to retrieve data for. + * @param rm Request completion monitor. + */ + void getRegisterData(IRegisterDMContext dmc , DataRequestMonitor<IRegisterDMData> rm); + + /** + * Retrieves bit field data for given context. + * @param dmc Context to retrieve data for. + * @param rm Request completion monitor. + */ + void getBitFieldData(IBitFieldDMContext dmc , DataRequestMonitor<IBitFieldDMData> rm); + + + + /** + * Writes a register value for a given register to the target + * @param regCtx Context containing the register. + * @param regValue Value of the register to be written. + * @param formatId Format of the value to be written. + * @param rm Request completion monitor. + */ + void writeRegister(IRegisterDMContext regCtx, String regValue, String formatId, RequestMonitor rm); + + /** + * Writes a bit field value for a given bit field to the target + * @param bitFieldCtx Context containing the bit field. + * @param bitFieldValue Value of the bit field to be written. + * @param formatId Format of the value to be written. + * @param rm Request completion monitor. + */ + void writeBitField(IBitFieldDMContext bitFieldCtx, String bitFieldValue, String formatId, RequestMonitor rm); + + /** + * Writes a bit field value for a given bit field to the target + * @param bitFieldCtx Context containing the bit field. + * @param mnemonic Mnemonic which represents the value to be written. + * @param rm Request completion monitor. + */ + void writeBitField(IBitFieldDMContext bitFieldCtx, IMnemonic mnemonic, RequestMonitor rm); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IRunControl.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IRunControl.java new file mode 100644 index 00000000000..181d2f44418 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IRunControl.java @@ -0,0 +1,127 @@ +/******************************************************************************* + * 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 additional functionality + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.service; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMData; +import org.eclipse.cdt.dsf.datamodel.IDMEvent; +import org.eclipse.cdt.dsf.datamodel.IDMService; + +/** + * This interface provides access to controlling and monitoring the execution + * state of a process being debugged. This interface does not actually + * provide methods for creating or destroying execution contexts, it doesn't + * even have methods for getting labels. That's because it is expected that + * higher level services, ones that deal with processes, kernels, or target + * features will provide that functionality. + */ +public interface IRunControl extends IDMService +{ + /** + * Execution context is the object on which run control operations can be + * performed. A lot of higher-level services reference this context to build + * functionality on top of it, e.g. stack, expression evaluation, registers, etc. + */ + public interface IExecutionDMContext extends IDMContext {} + + /** + * Context representing a process, kernel, or some other logical container + * for execution contexts, which by itself can perform run-control + * operations. + */ + + public interface IContainerDMContext extends IExecutionDMContext {} + + /** Flag indicating reason context state change. */ + public enum StateChangeReason { UNKNOWN, USER_REQUEST, STEP, BREAKPOINT, EXCEPTION, CONTAINER, WATCHPOINT, SIGNAL, SHAREDLIB, ERROR, EVALUATION }; + + /** + * Indicates that the given thread has suspended. + */ + public interface ISuspendedDMEvent extends IDMEvent<IExecutionDMContext> { + StateChangeReason getReason(); + } + + /** + * Indicates that the given thread has resumed. + */ + public interface IResumedDMEvent extends IDMEvent<IExecutionDMContext> { + StateChangeReason getReason(); + } + + /** + * Indicates that the given container has suspended. + */ + public interface IContainerSuspendedDMEvent extends ISuspendedDMEvent { + /** + * Returns the contexts which triggered the resume, which could be + * an empty array if not known. + */ + IExecutionDMContext[] getTriggeringContexts(); + } + + /** + * Indicates that the given container has resumed. + */ + public interface IContainerResumedDMEvent extends IResumedDMEvent { + /** + * Returns the contexts which triggered the resume, which could be an + * empty array if not known. + */ + IExecutionDMContext[] getTriggeringContexts(); + } + + /** + * Indicates that a new execution context was started. + */ + public interface IStartedDMEvent extends IDMEvent<IExecutionDMContext> {} + + /** + * Indicates that an execution context has exited. + */ + public interface IExitedDMEvent extends IDMEvent<IExecutionDMContext> {} + + /** + * Display information for an execution context. + */ + public interface IExecutionDMData extends IDMData { + StateChangeReason getStateChangeReason(); + } + + /** + * Retrieves execution data for given context. + * @param dmc Context to retrieve data for. + * @param rm Request completion monitor. + */ + public void getExecutionData(IExecutionDMContext dmc, DataRequestMonitor<IExecutionDMData> rm); + + /** + * Returns execution contexts belonging to the given container context. + */ + public void getExecutionContexts(IContainerDMContext c, DataRequestMonitor<IExecutionDMContext[]> rm); + + /* + * Run control commands. They all require the IExecutionContext object on + * which they perform the operations. + */ + void canResume(IExecutionDMContext context, DataRequestMonitor<Boolean> rm); + void canSuspend(IExecutionDMContext context, DataRequestMonitor<Boolean> rm); + boolean isSuspended(IExecutionDMContext context); + void resume(IExecutionDMContext context, RequestMonitor requestMonitor); + void suspend(IExecutionDMContext context, RequestMonitor requestMonitor); + public enum StepType { STEP_OVER, STEP_INTO, STEP_RETURN, INSTRUCTION_STEP_OVER, INSTRUCTION_STEP_INTO, INSTRUCTION_STEP_RETUTRN }; + boolean isStepping(IExecutionDMContext context); + void canStep(IExecutionDMContext context, StepType stepType, DataRequestMonitor<Boolean> rm); + void step(IExecutionDMContext context, StepType stepType, RequestMonitor requestMonitor); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/ISignals.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/ISignals.java new file mode 100644 index 00000000000..3fba2d7da39 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/ISignals.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * 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.service; + +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.service.IDsfService; + +/** + * + */ +public interface ISignals extends IDsfService { + /** + * Marker interface for a context for which signals can be set. + */ + public interface ISignalsDMContext extends IDMContext {}; + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/ISourceLookup.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/ISourceLookup.java new file mode 100644 index 00000000000..b92dd3c83b1 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/ISourceLookup.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2006, 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.service; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMEvent; +import org.eclipse.cdt.dsf.service.IDsfService; + +/** + * Service for mapping debugger paths to host paths. This service is needed + * primarily by other services that need to access source-path mappings, such + * as the breakpoints service. For UI components, the platform source lookup + * interfaces could be sufficient. + */ +public interface ISourceLookup extends IDsfService { + + public interface ISourceLookupDMContext extends IDMContext {} + + public interface ISourceLookupChangedDMEvent extends IDMEvent<ISourceLookupDMContext> {} + + /** + * Retrieves the host source object for given debugger path string. + */ + void getSource(ISourceLookupDMContext ctx, String debuggerPath, DataRequestMonitor<Object> rm); + + /** + * Retrieves the debugger path string for given host source object. + */ + void getDebuggerPath(ISourceLookupDMContext ctx, Object source, DataRequestMonitor<String> rm); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IStack.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IStack.java new file mode 100644 index 00000000000..d6003483f94 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IStack.java @@ -0,0 +1,111 @@ +/******************************************************************************* + * 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.service; + +import org.eclipse.cdt.core.IAddress; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMData; +import org.eclipse.cdt.dsf.datamodel.IDMService; + +/** + * Stack service provides access to stack information for a + * given execution context. + */ +public interface IStack extends IDMService { + + /** + * Context for a specific stack frame. Besides allowing access to stack + * frame data, this context is used by other services that require a stack + * frame for evaluation. + */ + public interface IFrameDMContext extends IDMContext { + int getLevel(); + } + + /** + * Stack frame information. + */ + public interface IFrameDMData extends IDMData { + IAddress getAddress(); + String getFile(); + String getFunction(); + int getLine(); + int getColumn(); + } + + /** + * Variable context. This context only provides access to limited + * expression information. For displaying complete information, + * Expressions service should be used. + */ + public interface IVariableDMContext extends IDMContext {} + + /** + * Stack frame variable information. + */ + public interface IVariableDMData extends IDMData { + String getName(); + String getValue(); + } + + /** + * Retrieves stack frame data for given context. + * @param frameDmc Context to retrieve data for. + * @param rm Request completion monitor. + */ + public void getFrameData(final IFrameDMContext frameDmc, DataRequestMonitor<IFrameDMData> rm); + + /** + * Retrieves stack frame variable data for given context. + * @param variableDmc Context to retrieve data for. + * @param rm Request completion monitor. + */ + public void getVariableData(IVariableDMContext variableDmc, DataRequestMonitor<IVariableDMData> rm); + + /** + * Retrieves list of stack frames for the given execution context. Request + * will fail if the stack frame data is not available. + */ + void getFrames(IDMContext execContext, DataRequestMonitor<IFrameDMContext[]> rm); + + /** + * Retrieves the top stack frame for the given execution context. + * Retrieving just the top frame DMC and corresponding data can be much + * more efficient than just retrieving the whole stack, before the data + * is often included in the stopped event. Also for some UI functionality, + * such as setpping, only top stack frame is often needed. + * @param execContext + * @param rm + */ + void getTopFrame(IDMContext execContext, DataRequestMonitor<IFrameDMContext> rm); + + /** + * Retrieves variables which were arguments to the stack frame's function. + */ + void getArguments(IFrameDMContext frameCtx, DataRequestMonitor<IVariableDMContext[]> rm); + + /** + * Retrieves variables local to the stack frame, including arguments. + */ + void getLocals(IFrameDMContext frameCtx, DataRequestMonitor<IVariableDMContext[]> rm); + + /** + * Retrieves the number of stack frames available for the given context.. + * @param dmc Context to retrieve data for. + * @param The maximum depth of stack to calculate. Should be 0 to calculate + * depth with no limit. + * @param rm Callback + */ + void getStackDepth(IDMContext dmc, int maxDepth, DataRequestMonitor<Integer> rm); + + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IStack2.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IStack2.java new file mode 100644 index 00000000000..ae2f507839f --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IStack2.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * 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.service; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; + +/** + * Stack service extension. + * <p> + * Adds the capability to retrieve a limited number of stack frames. + * </p> + * + * @since DSF 1.1 + */ +public interface IStack2 extends IStack { + + /** + * Convenience constant for use with {@link #getFrames(IDMContext, int, int, DataRequestMonitor)} + * to retrieve all stack frames. + */ + public final static int ALL_FRAMES = -1; + + /** + * Retrieves list of stack frames for the given execution context. Request + * will fail if the stack frame data is not available. + * <p>The range of stack frames can be limited by the <code>startIndex</code> and <code>endIndex</code> arguments. + * It is no error to specify an <code>endIndex</code> exceeding the number of available stack frames. + * A negative value for <code>endIndex</code> means to retrieve all stack frames. <code>startIndex</code> must be a non-negative value. + * </p> + * + * @param execContext the execution context to retrieve stack frames for + * @param startIndex the index of the first frame to retrieve + * @param endIndex the index of the last frame to retrieve (inclusive) or {@link #ALL_FRAMES} + * @param rm the request monitor + * + * @see #getFrames(IDMContext, DataRequestMonitor) + */ + public abstract void getFrames(IDMContext execContext, int startIndex, int endIndex, DataRequestMonitor<IFrameDMContext[]> rm); + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IStepQueueManager.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IStepQueueManager.java new file mode 100644 index 00000000000..16167692125 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IStepQueueManager.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * 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.service; + +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.StepType; +import org.eclipse.cdt.dsf.service.DsfSession; + +/** + * @since 1.1 + */ +@ConfinedToDsfExecutor("getSession().getExecutor()") +public interface IStepQueueManager { + + /** + * Returns the session for which this step queue manager is used. + */ + public DsfSession getSession(); + + /** + * Checks whether a step command can be queued up for given context. + */ + public abstract void canEnqueueStep(IExecutionDMContext execCtx, StepType stepType, DataRequestMonitor<Boolean> rm); + + /** + * 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 abstract void enqueueStep(final IExecutionDMContext execCtx, final StepType stepType); + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/ISymbols.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/ISymbols.java new file mode 100644 index 00000000000..bf92dd13caa --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/ISymbols.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * 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.service; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMData; +import org.eclipse.cdt.dsf.datamodel.IDMEvent; +import org.eclipse.cdt.dsf.datamodel.IDMService; + +/** + * Service for accessing debugger symbols. This service builds on the Modules + * service, but not all debuggers provide access for parsing symbols so this + * service is separated. + * @see IModules + */ +public interface ISymbols extends IDMService { + public interface ISymbolObjectDMContext extends IDMContext {} + + /** + * Data about a debug symbol. + */ + public interface ISymbolObjectDMData extends IDMData { + String getName(); + String getTypeName(); + String getFilepath(); + } + + /** + * Indicates that the list of symbol objects is changed. Parsing debug + * symbols can be a long running operation (order of 10's of seconds or + * minues), so it is useful for the service to provide access to the data + * even while it's still parsing. This event may be issued periodically + * by the service to indicate that a section of debug symbols has been + * parsed. + */ + public interface ISymbolDataChangedDMEvent extends IDMEvent<IModules.ISymbolDMContext> {} + + /** + * Retrieves the list of symbols. + * @param symCtx Symbols context to retrieve symbols for. + * @param rm Request completion monitor. The return value is an iterator (rather than + * array) since there could be a very large number of symbols returned. + */ + public void getSymbols(IModules.ISymbolDMContext symCtx, DataRequestMonitor<Iterable<ISymbolObjectDMContext>> rm); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/StepQueueManager.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/StepQueueManager.java new file mode 100644 index 00000000000..3054e2af136 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/StepQueueManager.java @@ -0,0 +1,248 @@ +/******************************************************************************* + * 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.service; + +import java.util.HashMap; +import java.util.Hashtable; +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.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMEvent; +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.internal.DsfPlugin; +import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.osgi.framework.BundleContext; + +/** + * This service builds on top of standard run control service to provide + * step queuing functionality. Step queuing essentially allows user to press + * and hold the step key and achieve maximum stepping speed. If this service + * 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. + * + * @deprecated The functionality has been integrated in the UI layer. + */ +@Deprecated +public class StepQueueManager extends AbstractDsfService implements IStepQueueManager +{ + /** + * Amount of time in milliseconds, that it takes the ISteppingTimedOutEvent + * event to be issued after a step is started. + * @see ISteppingTimedOutEvent + */ + 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 throwing them away. + */ + public final static int STEP_QUEUE_DEPTH = 3; + + /** + * Indicates that the given context has been stepping for some time, + * and the UI (views and actions) may need to be updated accordingly. + */ + public interface ISteppingTimedOutEvent extends IDMEvent<IExecutionDMContext> {} + + + private static class StepRequest { + StepType fStepType; + StepRequest(StepType type) { + fStepType = type; + } + } + + private IRunControl fRunControl; + private int fQueueDepth = STEP_QUEUE_DEPTH; + private Map<IExecutionDMContext,List<StepRequest>> fStepQueues = new HashMap<IExecutionDMContext,List<StepRequest>>(); + private Map<IExecutionDMContext,Boolean> fTimedOutFlags = new HashMap<IExecutionDMContext,Boolean>(); + private Map<IExecutionDMContext,ScheduledFuture<?>> fTimedOutFutures = new HashMap<IExecutionDMContext,ScheduledFuture<?>>(); + + public StepQueueManager(DsfSession session) { + super(session); + } + + /////////////////////////////////////////////////////////////////////////// + // IDsfService + @Override + public void initialize(final RequestMonitor requestMonitor) { + super.initialize( + new RequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleSuccess() { + doInitialize(requestMonitor); + }}); + } + + private void doInitialize(final RequestMonitor requestMonitor) { + fRunControl = getServicesTracker().getService(IRunControl.class); + + getSession().addServiceEventListener(this, null); + register(new String[]{ StepQueueManager.class.getName()}, new Hashtable<String,String>()); + requestMonitor.done(); + } + + @Override + public void shutdown(final RequestMonitor requestMonitor) { + unregister(); + getSession().removeServiceEventListener(this); + super.shutdown(requestMonitor); + } + + @Override + protected BundleContext getBundleContext() { + return DsfPlugin.getBundleContext(); + } + + /* + * @see org.eclipse.cdt.dsf.debug.service.IStepQueueManager#canEnqueueStep(org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext, org.eclipse.cdt.dsf.debug.service.IRunControl.StepType, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor) + */ + public void canEnqueueStep(IExecutionDMContext execCtx, StepType stepType, DataRequestMonitor<Boolean> rm) { + if (doCanEnqueueStep(execCtx, stepType)) { + rm.setData(true); + rm.done(); + } else { + fRunControl.canStep(execCtx, stepType, rm); + } + } + + private boolean doCanEnqueueStep(IExecutionDMContext execCtx, StepType stepType) { + return fRunControl.isStepping(execCtx) && !isSteppingTimedOut(execCtx); + } + + /** + * Returns the number of step commands that are queued for given execution + * context. + */ + public int getPendingStepCount(IExecutionDMContext execCtx) { + List<StepRequest> stepQueue = fStepQueues.get(execCtx); + if (stepQueue == null) return 0; + return stepQueue.size(); + } + + /* + * @see org.eclipse.cdt.dsf.debug.service.IStepQueueManager#enqueueStep(org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext, org.eclipse.cdt.dsf.debug.service.IRunControl.StepType) + */ + public void enqueueStep(final IExecutionDMContext execCtx, final StepType stepType) { + fRunControl.canStep( + execCtx, stepType, new DataRequestMonitor<Boolean>(getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess() && getData()) { + fRunControl.step(execCtx, stepType, new RequestMonitor(getExecutor(), null)); + } else if (doCanEnqueueStep(execCtx, 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(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; + } + + + /////////////////////////////////////////////////////////////////////////// + + @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 + if (fStepQueues.containsKey(e.getDMContext())) { + List<StepRequest> queue = fStepQueues.get(e.getDMContext()); + final StepRequest request = queue.remove(queue.size() - 1); + if (queue.isEmpty()) fStepQueues.remove(e.getDMContext()); + fRunControl.canStep( + e.getDMContext(), request.fStepType, + new DataRequestMonitor<Boolean>(getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess() && getData()) { + fRunControl.step( + e.getDMContext(), request.fStepType, new RequestMonitor(getExecutor(), null)); + } else { + // For whatever reason we can't step anymore, so clear out + // the step queue. + fStepQueues.remove(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 ISteppingTimedOutEvent() { + public IExecutionDMContext getDMContext() { return e.getDMContext(); } + }, + getProperties()); + } + }}, + STEPPING_TIMEOUT, TimeUnit.MILLISECONDS) + ); + + } + } + + @DsfServiceEventHandler + public void eventDispatched(ISteppingTimedOutEvent e) { + fTimedOutFlags.put(e.getDMContext(), Boolean.TRUE); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/CommandCache.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/CommandCache.java new file mode 100644 index 00000000000..0c0f1360da2 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/CommandCache.java @@ -0,0 +1,549 @@ +/******************************************************************************* + * 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 - Modified for caching commands corresponding to multiple execution contexts + *******************************************************************************/ + +package org.eclipse.cdt.dsf.debug.service.command; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.internal.DsfPlugin; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +/** + * This is a utility class for caching results of MI Commands. Return MIInfo + * data is retrieved from the cache if command was previously executed, and + * it is executed with MICommand service if it was not previously seen. + * + * Resetting the cache has to be performed by the object owning the cache when + * when an event indicates that the data is obsolete (which is specific to the + * types of commands being cached). + */ + +public class CommandCache implements ICommandListener +{ + static enum CommandStyle { COALESCED, NONCOALESCED } + + /** + * Holds cache information for a given command. + * @param <V> Type matches the result type associated with the command. + */ + class CommandInfo { + + /* + * Control variables. + */ + + /** List of the request monitors associated with this command */ + private final List<DataRequestMonitor<ICommandResult>> fCurrentRequestMonitors ; + + /** Original command. Need for reference from Queue completion notification */ + private final ICommand<ICommandResult> fCommand; + + /** Style of this command ( internal coalesced or not) */ + private final CommandStyle fCmdStyle; + + /** Command being processed for this command */ + private CommandInfo fCoalescedCmd; + + private ICommandToken fToken; + + public CommandInfo(CommandStyle cmdstyle, ICommand<ICommandResult> cmd, DataRequestMonitor<ICommandResult> rm ) { + fCmdStyle = cmdstyle; + fCommand = cmd; + fCurrentRequestMonitors = new LinkedList<DataRequestMonitor<ICommandResult>>(); + fCurrentRequestMonitors.add(rm); + fCoalescedCmd = null; + } + + public CommandStyle getCommandstyle() { return fCmdStyle; } + public List<DataRequestMonitor<ICommandResult>> getRequestMonitorList() { return fCurrentRequestMonitors; } + public ICommand<ICommandResult> getCommand() { return fCommand; } + public CommandInfo getCoalescedCmd() { return fCoalescedCmd; } + public void setCoalescedCmd( CommandInfo cmd ) { fCoalescedCmd = cmd; } + + @Override + public boolean equals(Object other) { + if (!(other instanceof CommandInfo)) return false; + CommandInfo otherCmd = (CommandInfo)other; + + return otherCmd.fCommand.equals(fCommand); + } + + @Override + public int hashCode() { + return fCommand.hashCode(); + } + } + + class CommandResultInfo { + private final ICommandResult fData; + private final IStatus fStatus; + + public CommandResultInfo(ICommandResult data, IStatus status) { + fData = data; + fStatus = status; + } + + public ICommandResult getData() { return fData; } + public IStatus getStatus() { return fStatus; } + } + + private DsfSession fSession; + + private ICommandControl fCommandControl; + + /* + * This class contains 5 significant lists. + * + * Cached Results : + * + * Contains a mapping of commands and their completed results. Until the cached + * results are cleared by the owner of the cache. + * + * Pending Commands Not Queued : + * + * The Control object has not yet indicated that it has recognized the command + * yet. The user is not allowed to interrogate these objects until the Control + * object indicates they have been queued ( commandQueued notification ). + * + * Pending Commands Unsent : + * + * This is the list of commands which have been issued to the Control object but + * have not been actually issued to the backend. These commands represent coalesce + * options. They may be compared against the Queued list being maintained by the + * Control object until told otherwise - commandSent notification ). + * + * Pending Commands Sent : + * + * This is a list of commands which have been issued to the Control object and + * have also been sent to the backend. It is not possible use these objects for + * coalescents. + * + * Coalesced Pending Q : + * + * These represent original commands for which a new coalesced command has been + * created. When the coalesced commands completes the results will be decomposed + * when back into individual results from this command. + */ + private Set<IDMContext> fAvailableContexts = new HashSet<IDMContext>(); + + private Map<IDMContext, HashMap<CommandInfo, CommandResultInfo>> fCachedContexts = new HashMap<IDMContext, HashMap<CommandInfo, CommandResultInfo>>(); + + private ArrayList<CommandInfo> fPendingQCommandsSent = new ArrayList<CommandInfo>(); + + private ArrayList<CommandInfo> fPendingQCommandsNotYetSent = new ArrayList<CommandInfo>(); + + private ArrayList<CommandInfo> fPendingQWaitingForCoalescedCompletion = new ArrayList<CommandInfo>(); + + public CommandCache(DsfSession session, ICommandControl control) { + fSession = session; + fCommandControl = control; + + /* + * We listen for the notifications that the commands have been sent to the + * backend from the GDB/MI Communications engine. + */ + fCommandControl.addCommandListener(this); + } + + /* + * Constructs a coalesced command if possible. + */ + private CommandInfo getCoalescedCommand(CommandInfo cmd) { + + for ( CommandInfo currentUnsentEntry : new ArrayList<CommandInfo>(fPendingQCommandsNotYetSent) ) { + /* + * Get the current unsent entry to determine if we can coalesced with it. + */ + ICommand<?> unsentCommand = currentUnsentEntry.getCommand(); + + /* + * Check if we can so construct a new COALESCED command from scratch. + */ + + // For sanity's sake, cast the generic ?'s to concrete types in the cache implementation. + @SuppressWarnings("unchecked") + ICommand<ICommandResult> coalescedCmd = + (ICommand<ICommandResult>)unsentCommand.coalesceWith( cmd.getCommand() ); + + if ( coalescedCmd != null ) { + CommandInfo coalescedCmdInfo = new CommandInfo( CommandStyle.COALESCED, coalescedCmd, null) ; + + if ( currentUnsentEntry.getCommandstyle() == CommandStyle.COALESCED ) { + /* + * We matched a command which is itself already a COALESCED command. So + * we need to run through the reference list and point all the current + * command which are referencing the command we just subsumed and change + * them to point to the new super command. + */ + + for ( CommandInfo waitingEntry : new ArrayList<CommandInfo>(fPendingQWaitingForCoalescedCompletion) ) { + + if ( waitingEntry.getCoalescedCmd() == currentUnsentEntry ) { + /* + * This referenced the old command change it to point to the new one. + */ + waitingEntry.setCoalescedCmd(coalescedCmdInfo); + } + } + } else { + /* + * This currently unsent entry needs to go into the coalescing list. To + * be completed when the coalesced command comes back with a result. + */ + fPendingQWaitingForCoalescedCompletion.add(currentUnsentEntry); + currentUnsentEntry.setCoalescedCmd(coalescedCmdInfo); + } + + /* + * Either way we want to take the command back from the Control object so it + * does not continue to process it. + */ + fPendingQCommandsNotYetSent.remove(currentUnsentEntry); + fCommandControl.removeCommand(currentUnsentEntry.fToken); + + return( coalescedCmdInfo ); + } + } + + return null; + } + + /** + * Executes given ICommand, or retrieves the cached result if known. + * @param command Command to execute. + * @param rm Return token, contains the retrieved MIInfo object as + * well as its cache status. + */ + public <V extends ICommandResult> void execute(ICommand<V> command, DataRequestMonitor<V> rm) { + assert fSession.getExecutor().isInExecutorThread(); + + // Cast the generic ?'s to concrete types in the cache implementation. + @SuppressWarnings("unchecked") + final ICommand<ICommandResult> genericCommand = (ICommand<ICommandResult>)command; + @SuppressWarnings("unchecked") + final DataRequestMonitor<ICommandResult> genericDone = (DataRequestMonitor<ICommandResult>) rm; + + CommandInfo cachedCmd = new CommandInfo( CommandStyle.NONCOALESCED, genericCommand, genericDone) ; + + final IDMContext context = genericCommand.getContext(); + + /* + * If command is already cached, just return the cached data. + */ + if(fCachedContexts.get(context) != null && fCachedContexts.get(context).containsKey(cachedCmd)){ + CommandResultInfo result = fCachedContexts.get(context).get(cachedCmd); + if (result.getStatus().getSeverity() <= IStatus.INFO) { + @SuppressWarnings("unchecked") + V v = (V)result.getData(); + rm.setData(v); + } else { + rm.setStatus(result.getStatus()); + } + rm.done(); + return; + } + + /* + * Return an error if the target is available anymore. + */ + if (!isTargetAvailable(command.getContext())) { + rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, "Target not available.", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + /* + * If we are already waiting for this command to complete, + * add this request monitor to list of waiting monitors. + */ + for ( CommandInfo sentCommand : fPendingQCommandsSent ) { + if ( sentCommand.equals( cachedCmd )) { + sentCommand.getRequestMonitorList().add(genericDone); + return; + } + } + for ( CommandInfo notYetSentCommand : fPendingQCommandsNotYetSent ) { + if ( notYetSentCommand.equals( cachedCmd )) { + notYetSentCommand.getRequestMonitorList().add(genericDone); + return; + } + } + + + /* + * We see if this command can be combined into a coalesced one. The + * coalesce routine will take care of the already enqueued one which + * this command is being coalesced with. + */ + + CommandInfo coalescedCmd = getCoalescedCommand(cachedCmd); + + if ( coalescedCmd != null ) { + /* + * The original command we were handed needs to go into the waiting QUEUE. + * We also need to point it it to the coalesced command. + */ + fPendingQWaitingForCoalescedCompletion.add(cachedCmd); + cachedCmd.setCoalescedCmd(coalescedCmd); + cachedCmd = coalescedCmd; + } + + /* + * Now we have a command to send ( coalesced or not ). Put it in the cannot touch + * it list and give it to the Control object. Our state handlers will move it into + * the proper list as the Control object deals with it. + */ + final CommandInfo finalCachedCmd = cachedCmd; + fPendingQCommandsNotYetSent.add(finalCachedCmd); + + finalCachedCmd.fToken = fCommandControl.queueCommand( + finalCachedCmd.getCommand(), + new DataRequestMonitor<ICommandResult>(fSession.getExecutor(), null) { + @Override + public void handleCompleted() { + + /* + * Match this up with a command set we know about. + */ + if ( ! fPendingQCommandsSent.remove(finalCachedCmd) ) { + /* + * It should not be the case that this is possible. It would mean we + * have mismanaged the queues or completions are lost at the lower + * levels. When the removal and cancellation is completed this code + * will probably not be here. But for now just return. + */ + return ; + } + + ICommandResult result = getData(); + IStatus status = getStatus(); + + if ( finalCachedCmd.getCommandstyle() == CommandStyle.COALESCED ) { + /* + * We matched a command which is itself already a COALESCED command. So + * we need to go through the list of unsent commands which were not sent + * because the coalesced command represented it. For each match we find + * we create a new result from the coalesced command for it. + */ + + for ( CommandInfo waitingEntry : new ArrayList<CommandInfo>(fPendingQWaitingForCoalescedCompletion) ) { + + if ( waitingEntry.getCoalescedCmd() == finalCachedCmd ) { + + /* + * Remove this entry from the list since we can complete it. + */ + fPendingQWaitingForCoalescedCompletion.remove(waitingEntry); + + // Cast the calculated result back to the requested type. + @SuppressWarnings("unchecked") + V subResult = (V)result.getSubsetResult(waitingEntry.getCommand()); + CommandResultInfo subResultInfo = new CommandResultInfo(subResult, status); + + if(fCachedContexts.get(context) != null){ + fCachedContexts.get(context).put(waitingEntry, subResultInfo); + } else { + HashMap<CommandInfo, CommandResultInfo> map = new HashMap<CommandInfo, CommandResultInfo>(); + map.put(waitingEntry, subResultInfo); + fCachedContexts.put(context, map); + } + + if (!isSuccess()) { + + /* + * We had some form of error with the original command. So notify the + * original requesters of the issues. + */ + for (DataRequestMonitor<?> pendingRM : waitingEntry.getRequestMonitorList()) { + pendingRM.setStatus(status); + pendingRM.done(); + } + } else { + assert subResult != null; + + /* + * Notify the original requesters of the positive results. + */ + for (DataRequestMonitor<? extends ICommandResult> pendingRM : waitingEntry.getRequestMonitorList()) { + // Cast the pending return token to match the requested type. + @SuppressWarnings("unchecked") + DataRequestMonitor<V> vPendingRM = (DataRequestMonitor<V>) pendingRM; + + vPendingRM.setData(subResult); + vPendingRM.done(); + } + } + } + } + } else { + /* + * This is an original request which completed. Indicate success or + * failure to the original requesters. + */ + CommandResultInfo resultInfo = new CommandResultInfo(result, status); + + if (fCachedContexts.get(context) != null){ + fCachedContexts.get(context).put(finalCachedCmd, resultInfo); + } else { + HashMap<CommandInfo, CommandResultInfo> map = new HashMap<CommandInfo, CommandResultInfo>(); + map.put(finalCachedCmd, resultInfo); + fCachedContexts.put(context, map); + } + + if (!isSuccess()) { + /* + * We had some form of error with the original command. So notify the + * original requesters of the issues. + */ + for (DataRequestMonitor<?> pendingRM : finalCachedCmd.getRequestMonitorList()) { + pendingRM.setStatus(status); + pendingRM.done(); + } + } else { + // Cast the calculated result back to the requested type. + @SuppressWarnings("unchecked") + V vResult = (V)result; + + for (DataRequestMonitor<? extends ICommandResult> pendingRM : finalCachedCmd.getRequestMonitorList()) { + // Cast the pending return token to match the requested type. + @SuppressWarnings("unchecked") + DataRequestMonitor<V> vPendingRM = (DataRequestMonitor<V>) pendingRM; + + vPendingRM.setData(vResult); + vPendingRM.done(); + } + } + } + } + }); + } + + /** + * TODO + */ + public void setContextAvailable(IDMContext context, boolean isAvailable) { + if (isAvailable) { + fAvailableContexts.add(context); + } else { + fAvailableContexts.remove(context); + for (Iterator<IDMContext> itr = fAvailableContexts.iterator(); itr.hasNext();) { + if (DMContexts.isAncestorOf(context, itr.next())) { + itr.remove(); + } + } + } + } + + /** + * TODO + * @see #setContextAvailable(IDMContext, boolean) + */ + public boolean isTargetAvailable(IDMContext context) { + for (IDMContext availableContext : fAvailableContexts) { + if (context.equals(availableContext) || DMContexts.isAncestorOf(context, availableContext)) { + return true; + } + } + return false; + } + + + + /** + * Clears the cache data. + */ + public void reset() { + fCachedContexts.clear(); + } + + public void commandRemoved(ICommandToken token) { + /* + * Do nothing. + */ + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.mi.service.control.IDebuggerControl.ICommandListener#commandQueued(org.eclipse.cdt.dsf.mi.core.command.ICommand) + */ + public void commandQueued(ICommandToken token) { + /* + * Do nothing. + */ + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.mi.service.control.IDebuggerControl.ICommandListener#commandDone(org.eclipse.cdt.dsf.mi.core.command.ICommand, org.eclipse.cdt.dsf.mi.core.command.ICommandResult) + */ + public void commandDone(ICommandToken token, ICommandResult result) { + /* + * We handle the done with a runnable where we initiated the command + * so there is nothing to do here. + */ + } + + /* + * Move the command into our internal sent list. This means we can no longer look at + * this command for possible coalescence since it has been given to the debug engine + * and is currently being processed. + * + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.mi.service.control.IDebuggerControl.ICommandListener#commandSent(org.eclipse.cdt.dsf.mi.core.command.ICommand) + */ + public void commandSent(ICommandToken token) { + + // Cast the generic ?'s to concrete types in the cache implementation. + @SuppressWarnings("unchecked") + ICommand<ICommandResult> genericCommand = (ICommand<ICommandResult>)token.getCommand(); + + CommandInfo cachedCmd = new CommandInfo( CommandStyle.NONCOALESCED, genericCommand, null) ; + + for ( CommandInfo unqueuedCommand : new ArrayList<CommandInfo>(fPendingQCommandsNotYetSent) ) { + if ( unqueuedCommand.equals( cachedCmd )) { + fPendingQCommandsNotYetSent.remove(unqueuedCommand); + fPendingQCommandsSent.add(unqueuedCommand); + break; + } + } + } + + /** + * Clears the cache entries for given context. Clears the whole cache if + * context parameter is null. + */ + public void reset(IDMContext dmc) { + if (dmc == null) { + fCachedContexts.clear(); + return; + } + for (Iterator<IDMContext> itr = fCachedContexts.keySet().iterator(); itr.hasNext();) { + IDMContext keyDmc = itr.next(); + if (keyDmc != null && (dmc.equals(keyDmc) || DMContexts.isAncestorOf(keyDmc, dmc))) { + itr.remove(); + } + } + } +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommand.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommand.java new file mode 100644 index 00000000000..6c63ae8fb35 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommand.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * 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 + * Ericsson - Modified for additional features in DSF Reference implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.debug.service.command; + +import org.eclipse.cdt.dsf.datamodel.IDMContext; + + +/** + * Command interface for creating and manipulating GDB/MI commands + * for the DSF GDB reference implemenation. The command represents + * the GDB/MI request which will be put on the wire to the GDB + * backend. + */ + +public interface ICommand<V extends ICommandResult> { + /** + * Takes the supplied command and coalesces it with this one. + * The result is a new third command which represent the two + * original commands. + * <br>Note: the result type associated with the resurned command may be + * different than the result type associated with either of the commands + * being coalesced. + * + * @return newly created command, or null if command cannot be coalesced + */ + public ICommand<? extends ICommandResult> coalesceWith( ICommand<? extends ICommandResult> command ); + + /** + * Returns the context that this command is to be evaluated in. May be null + * if the command does not need to be evaluated in a specific context. + */ + public IDMContext getContext(); +} + + diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommandControl.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommandControl.java new file mode 100644 index 00000000000..dd174d3bd4a --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommandControl.java @@ -0,0 +1,72 @@ +/******************************************************************************* + * 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.service.command; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; + +/** + * API for sending commands to the debugger and for receiving command results + * and asynchronous events. The command control may be implemented by a service + * or a non-service object. + * + * @see ICommandControlService + */ +public interface ICommandControl { + + /** + * Adds the specified command to the queue of commands to be processed. + * + * @param command Specific command to be processed + * @param rm Request completion monitor + * @return None + */ + <V extends ICommandResult> ICommandToken queueCommand(ICommand<V> command, DataRequestMonitor<V> rm); + + /** + * Removes the specified command from the processor queue. + * + * @param command Specific command to be removed + * @return None + */ + void removeCommand(ICommandToken token); + + /** + * Adds a notification handler for the Command processor. + * + * @param command listener to be added + * @return None + */ + void addCommandListener(ICommandListener listener); + + /** + * Removes a notification handler for the Command processor. + * + * @param command listener to be removed + * @return None + */ + void removeCommandListener(ICommandListener listener); + + /** + * Adds a notification handler for the Event processor. + * + * @param event listener to be added + * @return None + */ + void addEventListener(IEventListener listener); + + /** + * Removes a notification handler for the Event processor. + * + * @param event listener to be removed + * @return None + */ + void removeEventListener(IEventListener listener); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommandControlService.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommandControlService.java new file mode 100644 index 00000000000..0672d5ec986 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommandControlService.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * 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.service.command; + +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMEvent; +import org.eclipse.cdt.dsf.service.IDsfService; + +/** + * Service which acts as a command control. + * + * @since 1.1 + */ +public interface ICommandControlService extends ICommandControl, IDsfService { + + /** + * Context representing a command control service. All contexts which + * originate from a given command control service, should have that + * control's context in their hierarchy. + * + * @see ICommandControlService#getContext() + */ + public interface ICommandControlDMContext extends IDMContext { + /** + * Returns the ID of the command control that this context + * represents. + */ + public String getCommandControlId(); + } + + /** + * Event indicating that the back end process has started. + */ + public interface ICommandControlInitializedDMEvent extends IDMEvent<ICommandControlDMContext> {}; + + /** + * Event indicating that the back end process has terminated. + */ + public interface ICommandControlShutdownDMEvent extends IDMEvent<ICommandControlDMContext> {}; + + /** + * Returns the identifier of this command control service. It can be used + * to distinguish between multiple instances of command control services. + */ + public String getId(); + + /** + * returns the context representing this command control. + */ + public ICommandControlDMContext getContext(); + + /** + * Returns whether this command control is currently active. A command + * control service is active if it has been initialized and has not yet + * shut down. + * @return + */ + public boolean isActive(); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommandListener.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommandListener.java new file mode 100644 index 00000000000..f7a0f80ae79 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommandListener.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * 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.service.command; + +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; + +/** + * Synchronous listener to commands being sent and received. + * All the registered listeners will be called in the same + * dispatch cycle as when the result of the command is submitted. + */ + +@ConfinedToDsfExecutor("") +public interface ICommandListener { + /** + * Notifies that the specified command has been added to the Command Queue. + * It has not yet been sent. In this state the command can be examined and + * possibly withdrawn because it has been coalesced with another command. + * + * @return None + * @param command Command which has been added to the Queue + */ + public void commandQueued(ICommandToken token); + + /** + * Notification that the given command was sent to the debugger. At this + * point the command is no longer in the Command Queue and should not be + * examined. The only thing which can be done is to try and cancel the + * command. + * + * @return None + * @param command + */ + public void commandSent(ICommandToken token); + + /** + * Notifies that the specified command has been removed from the + * Command Queue. This notification means that the command has + * been removed from the queue and not sent to the backend. The + * user has specifically removed it, perhaps because it has been + * combined with another. Or some state change has occured and + * there is no longer a need to get this particular set of data. + * + * @return None + * @param Command which has been sent to the backend + */ + public void commandRemoved(ICommandToken token); + + /** + * Notifies that the specified command has been completed. + * + * @return None + * @param Command which has been sent to the backend + */ + public void commandDone(ICommandToken token, ICommandResult result); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommandResult.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommandResult.java new file mode 100644 index 00000000000..09e53154c43 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommandResult.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * 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.service.command; + +public interface ICommandResult { + /** + * Returns an ICommandResult which is a subset command result. The command + * result which is being passed in is from a coalesced command. The result + * which is desired is contained within those results. In this instance we + * are processing the command result from the coalesced command to get our + * command result. + * <i>Note:</i> The type of returned command result must match the type + * associated with the subset command that is passed in the argument. + * + * @return result for this particular command. + */ + public <V extends ICommandResult> V getSubsetResult( ICommand<V> command ); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommandToken.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommandToken.java new file mode 100644 index 00000000000..ccd80d234d2 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommandToken.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * 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.service.command; + +/** + * Token returned by ICommandControl.queueCommand(). This token can be used + * to uniquely identify a command when calling ICommandControl.removeCommand() + * or when implementing the ICommandListener listener methods. + */ +public interface ICommandToken { + /** + * Returns the command that this was created for. + */ + public ICommand<? extends ICommandResult> getCommand(); +}
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/IEventListener.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/IEventListener.java new file mode 100644 index 00000000000..49fa20f3eee --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/IEventListener.java @@ -0,0 +1,19 @@ +package org.eclipse.cdt.dsf.debug.service.command; + +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; + +/** + * Synchronous listener for events issued from the debugger. All + * registered listeners will be called in the same dispatch cycle. + */ + +@ConfinedToDsfExecutor("") +public interface IEventListener { + /** + * Notifies that the given asynchronous output was received from the + * debugger. + * @param output output that was received from the debugger. Format + * of the output data is debugger specific. + */ + public void eventReceived(Object output); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/sourcelookup/DsfSourceLookupDirector.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/sourcelookup/DsfSourceLookupDirector.java new file mode 100644 index 00000000000..2e881b18c6c --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/sourcelookup/DsfSourceLookupDirector.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * 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 + * Nokia - Added support for AbsoluteSourceContainer( 159833 ) + * Wind River Systems - Adapted for use with DSF +*******************************************************************************/ +package org.eclipse.cdt.dsf.debug.sourcelookup; + +import org.eclipse.cdt.debug.internal.core.sourcelookup.CSourceLookupDirector; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.debug.core.sourcelookup.ISourceLookupParticipant; + +/** + * DSF source lookup director. + */ +public class DsfSourceLookupDirector extends CSourceLookupDirector { + + private final DsfSession fSession; + + public DsfSourceLookupDirector(DsfSession session) { + fSession = session; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.sourcelookup.ISourceLookupDirector#initializeParticipants() + */ + @Override + public void initializeParticipants() { + super.initializeParticipants(); + addParticipants( new ISourceLookupParticipant[]{ new DsfSourceLookupParticipant(fSession) } ); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/sourcelookup/DsfSourceLookupParticipant.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/sourcelookup/DsfSourceLookupParticipant.java new file mode 100644 index 00000000000..11a105e6f2e --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/sourcelookup/DsfSourceLookupParticipant.java @@ -0,0 +1,229 @@ +/******************************************************************************* + * Copyright (c) 2006 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.sourcelookup; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +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.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IStack; +import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; +import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMData; +import org.eclipse.cdt.dsf.internal.DsfPlugin; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.sourcelookup.ISourceContainer; +import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector; +import org.eclipse.debug.core.sourcelookup.ISourceLookupParticipant; + +/** + * Source lookup participant that should be used with DSF-based debuggers. + */ +@ThreadSafe +public class DsfSourceLookupParticipant implements ISourceLookupParticipant { + protected static final Object[] EMPTY = new Object[0]; + + private DsfExecutor fExecutor; + private String fSessionId; + private DsfServicesTracker fServicesTracker; + private ISourceLookupDirector fDirector; + private Map<String, List<Object>> fLookupCache = Collections.synchronizedMap(new HashMap<String, List<Object>>()); + + public DsfSourceLookupParticipant(DsfSession session) { + fSessionId = session.getId(); + fExecutor = session.getExecutor(); + fServicesTracker = new DsfServicesTracker(DsfPlugin.getBundleContext(), fSessionId); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.sourcelookup.ISourceLookupParticipant#init(org.eclipse.debug.core.sourcelookup.ISourceLookupDirector) + */ + public void init(ISourceLookupDirector director) { + fDirector = director; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.sourcelookup.ISourceLookupParticipant#dispose() + */ + public void dispose() { + fServicesTracker.dispose(); + fDirector = null; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.sourcelookup.ISourceLookupParticipant#findSourceElements(java.lang.Object) + */ + public Object[] findSourceElements(Object object) throws CoreException { + CoreException single = null; + MultiStatus multiStatus = null; + List<Object> results = null; + + String name = getSourceName(object); + if (name != null) { + results = fLookupCache.get(name); + if (results != null) { + return results.toArray(); + } else { + results = new ArrayList<Object>(); + } + ISourceContainer[] containers = getSourceContainers(); + for (int i = 0; i < containers.length; i++) { + try { + ISourceContainer container = containers[i]; + if (container != null) { + Object[] objects = container.findSourceElements(name); + if (objects.length > 0) { + if (isFindDuplicates()) { + results.addAll(Arrays.asList(objects)); + } else { + results.add(objects[0]); + break; + } + } + } + } catch (CoreException e) { + if (single == null) { + single = e; + } else if (multiStatus == null) { + multiStatus = new MultiStatus(DebugPlugin.getUniqueIdentifier(), DebugPlugin.INTERNAL_ERROR, new IStatus[]{single.getStatus()}, "Source Lookup error", null); //$NON-NLS-1$ + multiStatus.add(e.getStatus()); + } else { + multiStatus.add(e.getStatus()); + } + } + } + + if (!results.isEmpty()) { + synchronized(fLookupCache) { + if (!fLookupCache.containsKey(name)) { + fLookupCache.put(name, results); + } + } + } + } + if (results == null || results.isEmpty()) { + if (multiStatus != null) { + throw new CoreException(multiStatus); + } else if (single != null) { + throw single; + } + return EMPTY; + } + return results.toArray(); + } + + /** + * Returns whether this participant's source lookup director is configured + * to search for duplicate source elements. + * + * @return whether this participant's source lookup director is configured + * to search for duplicate source elements + */ + protected boolean isFindDuplicates() { + ISourceLookupDirector director = fDirector; + if (director != null) { + return director.isFindDuplicates(); + } + return false; + } + + /** + * Returns the source containers currently registered with this participant's + * source lookup director. + * + * @return the source containers currently registered with this participant's + * source lookup director + */ + protected ISourceContainer[] getSourceContainers() { + ISourceLookupDirector director = fDirector; + if (director != null) { + return director.getSourceContainers(); + } + return new ISourceContainer[0]; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.sourcelookup.ISourceLookupParticipant#sourceContainersChanged(org.eclipse.debug.core.sourcelookup.ISourceLookupDirector) + */ + public void sourceContainersChanged(ISourceLookupDirector director) { + fLookupCache.clear(); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.core.sourcelookup.ISourceLookupParticipant#getSourceName(java.lang.Object) + */ + public String getSourceName(Object object) throws CoreException { + if ( !(object instanceof IDMContext) || + !((IDMContext)object).getSessionId().equals(fSessionId) ) + { + throw new CoreException(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, -1, "Invalid object", null)); //$NON-NLS-1$ + } + + final IDMContext dmc = (IDMContext)object; + Query<String> query = new Query<String>() { + @Override + protected void execute(final DataRequestMonitor<String> rm) { + getSourceNameOnDispatchThread(dmc, rm); + }}; + fExecutor.execute(query); + try { + return query.get(); + } catch (InterruptedException e) { assert false : "Interrupted exception in DSF executor"; //$NON-NLS-1$ + } catch (ExecutionException e) { + if (e.getCause() instanceof CoreException) { + throw (CoreException)e.getCause(); + } + assert false : "Unexptected exception"; //$NON-NLS-1$ + } + return null; // Should never get here. + } + + @ConfinedToDsfExecutor("fExecutor") + private void getSourceNameOnDispatchThread(IDMContext dmc, final DataRequestMonitor<String> rm) { + if (!(dmc instanceof IStack.IFrameDMContext)) { + rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_HANDLE, "No source for this object", null)); //$NON-NLS-1$ + rm.done(); + return; + } + IFrameDMContext frameDmc = (IFrameDMContext)dmc; + + IStack stackService = fServicesTracker.getService(IStack.class); + if (stackService == null) { + rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_HANDLE, "Stack data not available", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + stackService.getFrameData( + frameDmc, + new DataRequestMonitor<IFrameDMData>(fExecutor, rm) { @Override + public void handleSuccess() { + rm.setData(getData().getFile()); + rm.done(); + }}); + } +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/internal/DsfPlugin.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/internal/DsfPlugin.java new file mode 100644 index 00000000000..11ccf06857b --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/internal/DsfPlugin.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * 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.internal; + +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Plugin; +import org.osgi.framework.BundleContext; + +/** + * The activator class controls the plug-in life cycle + */ +public class DsfPlugin extends Plugin { + + // The plug-in ID + public static final String PLUGIN_ID = "org.eclipse.cdt.dsf"; //$NON-NLS-1$ + + // The shared instance + private static DsfPlugin fgPlugin; + + // BundleContext of this plugin + private static BundleContext fgBundleContext; + + // Debugging flag + public static boolean DEBUG = false; + + /** + * The constructor + */ + public DsfPlugin() { + fgPlugin = this; + } + + /* + * (non-Javadoc) + * @see org.eclipse.core.runtime.Plugins#start(org.osgi.framework.BundleContext) + */ + @Override + public void start(BundleContext context) throws Exception { + fgBundleContext = context; + super.start(context); + DEBUG = "true".equals(Platform.getDebugOption("org.eclipse.cdt.dsf/debug")); //$NON-NLS-1$//$NON-NLS-2$ + } + + /* + * (non-Javadoc) + * @see org.eclipse.core.runtime.Plugin#stop(org.osgi.framework.BundleContext) + */ + @Override + public void stop(BundleContext context) throws Exception { + fgBundleContext = null; + super.stop(context); + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static DsfPlugin getDefault() { + return fgPlugin; + } + + public static BundleContext getBundleContext() { + return fgBundleContext; + } + + public static void debug(String message) { + if (DEBUG) { + System.out.println(message); + } + } + + public static String getDebugTime() { + StringBuilder traceBuilder = new StringBuilder(); + + // Record the time + long time = System.currentTimeMillis(); + long seconds = (time / 1000) % 1000; + if (seconds < 100) traceBuilder.append('0'); + if (seconds < 10) traceBuilder.append('0'); + traceBuilder.append(seconds); + traceBuilder.append(','); + long millis = time % 1000; + if (millis < 100) traceBuilder.append('0'); + if (millis < 10) traceBuilder.append('0'); + traceBuilder.append(millis); + return traceBuilder.toString(); + } + + + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/AbstractDsfService.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/AbstractDsfService.java new file mode 100644 index 00000000000..8d085eb6c27 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/AbstractDsfService.java @@ -0,0 +1,222 @@ +/******************************************************************************* + * 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.service; + +import java.util.Arrays; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.framework.ServiceRegistration; + + +/** + * Standard base implementation of the DSF service. This is a convenience + * class that provides the basic functionality that all DSF services have + * to implement. + */ +abstract public class AbstractDsfService + implements IDsfService, IDsfStatusConstants +{ + /** Reference to the session that this service belongs to. */ + private DsfSession fSession; + + /** Startup order number of this service. */ + private int fStartupNumber; + + /** Registration object for this service. */ + private ServiceRegistration fRegistration; + + /** Tracker for services that this service depends on. */ + private DsfServicesTracker fTracker; + + /** Properties that this service was registered with */ + @SuppressWarnings("unchecked") + private Dictionary fProperties; + + /** Properties that this service was registered with */ + private String fFilter; + + /** + * Only constructor, requires a reference to the session that this + * service belongs to. + * @param session + */ + public AbstractDsfService(DsfSession session) { + fSession = session; + } + + public DsfExecutor getExecutor() { return fSession.getExecutor(); } + + @SuppressWarnings("unchecked") + public Dictionary getProperties() { return fProperties; } + + public String getServiceFilter() { return fFilter; } + + public int getStartupNumber() { return fStartupNumber; } + + public void initialize(RequestMonitor rm) { + fTracker = new DsfServicesTracker(getBundleContext(), fSession.getId()); + fStartupNumber = fSession.getAndIncrementServiceStartupCounter(); + rm.done(); + } + + public void shutdown(RequestMonitor rm) { + fTracker.dispose(); + fTracker = null; + rm.done(); + } + + /** Returns the session object for this service */ + public DsfSession getSession() { return fSession; } + + /** + * Sub-classes should return the bundle context of the plugin, which the + * service belongs to. + */ + abstract protected BundleContext getBundleContext(); + + /** Returns the tracker for the services that this service depends on. */ + protected DsfServicesTracker getServicesTracker() { return fTracker; } + + /** + * Registers this service. + */ + @SuppressWarnings("unchecked") + protected void register(String[] classes, Dictionary properties) { + + /* + * If this service has already been registered, make sure we + * keep the names it has been registered with. However, we + * must trigger a new registration or else OSGI will keep the two + * registration separate. + */ + if (fRegistration != null) { + String[] previousClasses = (String[])fRegistration.getReference().getProperty(Constants.OBJECTCLASS); + + // Use a HashSet to avoid duplicates + Set<String> newClasses = new HashSet<String>(); + newClasses.addAll(Arrays.asList(previousClasses)); + newClasses.addAll(Arrays.asList(classes)); + classes = newClasses.toArray(new String[0]); + + /* + * Also keep all previous properties. + */ + if (fProperties != null) { + for (Enumeration e = fProperties.keys() ; e.hasMoreElements();) { + Object key = e.nextElement(); + Object value = fProperties.get(key); + properties.put(key, value); + } + } + + // Now, cancel the previous registration + unregister(); + } + /* + * Ensure that the list of classes contains the base DSF service + * interface, as well as the actual class type of this object. + */ + if (!Arrays.asList(classes).contains(IDsfService.class.getName())) { + String[] newClasses = new String[classes.length + 1]; + System.arraycopy(classes, 0, newClasses, 1, classes.length); + newClasses[0] = IDsfService.class.getName(); + classes = newClasses; + } + if (!Arrays.asList(classes).contains(getClass().getName())) { + String[] newClasses = new String[classes.length + 1]; + System.arraycopy(classes, 0, newClasses, 1, classes.length); + newClasses[0] = getClass().getName(); + classes = newClasses; + } + /* + * Make sure that the session ID is set in the service properties. + * The session ID distinguishes this service instance from instances + * of the same service in other sessions. + */ + properties.put(PROP_SESSION_ID, getSession().getId()); + fProperties = properties; + fRegistration = getBundleContext().registerService(classes, this, properties); + + /* + * Retrieve the OBJECTCLASS property directly from the service + * registration info. This is the best bet for getting an accurate + * value. + */ + fRegistration.getReference().getProperty(Constants.OBJECTCLASS); + fProperties.put(Constants.OBJECTCLASS, fRegistration.getReference().getProperty(Constants.OBJECTCLASS)); + + /* + * Create the filter for this service based on all the properties. If + * there is a single service instance per session, or if the properties + * parameter uniquely identifies this service instance among other + * instances in this session. Then this filter will fetch this service + * and only this service from OSGi. + */ + fFilter = generateFilter(fProperties); + } + + /** + * Generates an LDAP filter to uniquely identify this service. + */ + @SuppressWarnings("unchecked") + private String generateFilter(Dictionary properties) { + StringBuffer filter = new StringBuffer(); + filter.append("(&"); //$NON-NLS-1$ + + for (Enumeration keys = properties.keys(); keys.hasMoreElements();) { + Object key = keys.nextElement(); + Object value = properties.get(key); + if (value instanceof Object[]) { + /* + * For arrays, add a test to check that every element in array + * is present. This is here mainly to handle OBJECTCLASS property. + */ + for (Object arrayValue : (Object[])value) { + filter.append('('); + filter.append(key.toString()); + filter.append("=*"); //$NON-NLS-1$ + filter.append(arrayValue.toString()); + filter.append(')'); + } + } else { + filter.append('('); + filter.append(key.toString()); + filter.append('='); + filter.append(value.toString()); + filter.append(')'); + } + } + filter.append(')'); + return filter.toString(); + } + + /** + * De-registers this service. + * + */ + protected void unregister() { + if (fRegistration != null) { + fRegistration.unregister(); + } + fRegistration = null; + } + + /** Returns the registration object that was obtained when this service was registered */ + protected ServiceRegistration getServiceRegistration() { return fRegistration; } +}
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/DsfServiceEventHandler.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/DsfServiceEventHandler.java new file mode 100644 index 00000000000..9f2e5525dcb --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/DsfServiceEventHandler.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2006 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.service; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation for service event handler methods. The name of the event + * handler method is irrelevant, only the annotation is checked. + * <p> + * Each service event handler method should have one or two parameters: + * <li> + * <br> First argument is required and it should be the event object, with + * type with the event class desired. + * <br> Second argument is optional, and it has to be of type Dictionary<String,String>. + * If this parameter is declared, the handler will be passed the properties + * dictionary of the service that submitted the event. + * </li> + * <p> + * It is expected that service event classes are hierarchical. So that if a + * handler is registered for a super-class of another event, this handler + * will be called every time one of the sub-class events is invoked. + * If a listener declares a handler for an event AND a superclass of that event, + * both handlers will be invoked when the event is dispatched. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@Documented +public @interface DsfServiceEventHandler { + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/DsfServices.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/DsfServices.java new file mode 100644 index 00000000000..02b83b94b1d --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/DsfServices.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * 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.service; + +/** + * Utility class containing status methods to use with DSF services. + */ +public class DsfServices { + + /** + * Creates a properly formatted OSGi service filter for a DSF service based + * on service class and session ID. + * @param serviceClass Class of the service to create the filter for. + * @param sessionId Session ID of the session that the service belongs to. + * @return Filter string to identify the given service. + */ + public static String createServiceFilter(Class<?> serviceClass, String sessionId) { + String serviceId = + "(&" + //$NON-NLS-1$ + "(OBJECTCLASS=" + //$NON-NLS-1$ + serviceClass.getName() + + ")" + //$NON-NLS-1$ + "(" + //$NON-NLS-1$ + IDsfService.PROP_SESSION_ID + + "=" + //$NON-NLS-1$ + sessionId + + ")" + //$NON-NLS-1$ + ")" ; //$NON-NLS-1$ + + return serviceId; + } +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/DsfServicesTracker.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/DsfServicesTracker.java new file mode 100644 index 00000000000..9a98805798c --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/DsfServicesTracker.java @@ -0,0 +1,169 @@ +/******************************************************************************* + * 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.service; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.osgi.framework.BundleContext; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; + +/** + * Convenience class to help track DSF services that a given + * client needs to use. This class is similar to the standard OSGI + * org.osgi.util.tracker.ServiceTracker class, with a few differences: + * <br>1. This class is assumed to be accessed by a single thread hence it + * has no synchronization built in, while OSGI ServiceTracker synchronized + * access to its data. + * <br>2. This class is primarily designed to track multiple services of + * different type (class), while OSGI ServiceTracker is designed to work with + * single class type, with optional filtering options. + * <br>3. This class uses knowledge of DSF sessions to help narrow down + * service references. + * <br>4. OSGI Service tracker explicitly listens to OSGI service + * startup/shutdown events and it will clear a reference to a service as + * soon as it's shut down. This class leaves it up to the client to make + * sure that it doesn't access a service once that service has been shut down. + * <p> + * That said, it might be more convenient for certain types of clients to use + * OSGI Service tracker for the additional features it provides. + * + * @see org.osgi.util.tracker.ServiceTracker + */ +public class DsfServicesTracker { + + private static String getServiceFilter(String sessionId) { + return ("(" + IDsfService.PROP_SESSION_ID + "=" + sessionId + ")").intern(); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ + } + + private static class ServiceKey + { + String fClassName; + String fFilter; + public ServiceKey(Class<?> clazz, String filter) { + fClassName = clazz != null ? clazz.getName() : null; + fFilter = filter; + } + + @Override + public boolean equals(Object other) { + // I guess this doesn't have to assume fFilter can be null, but oh well. + return other instanceof ServiceKey && + ((fClassName == null && ((ServiceKey)other).fClassName == null) || + (fClassName != null && fClassName.equals(((ServiceKey)other).fClassName))) && + ((fFilter == null && ((ServiceKey)other).fFilter == null) || + (fFilter != null && fFilter.equals(((ServiceKey)other).fFilter))); + } + + @Override + public int hashCode() { + return (fClassName == null ? 0 : fClassName.hashCode()) + (fFilter == null ? 0 : fFilter.hashCode()); + } + } + + private BundleContext fBundleContext; + private Map<ServiceKey,ServiceReference> fServiceReferences = new HashMap<ServiceKey,ServiceReference>(); + private Map<ServiceReference,Object> fServices = new HashMap<ServiceReference,Object>(); + private String fServiceFilter; + + /** + * Only constructor. + * @param bundleContext Context of the plugin that the client lives in. + * @param sessionId The DSF session that this tracker will be used for. + */ + public DsfServicesTracker(BundleContext bundleContext, String sessionId) { + fBundleContext = bundleContext; + fServiceFilter = getServiceFilter(sessionId); + } + + /** + * Retrieves a service reference for given service class and optional filter. + * Filter should be used if there are multiple instances of the desired service + * running within the same session. + * @param serviceClass class of the desired service + * @param custom filter to use when searching for the service, this filter will + * be used instead of the standard filter so it should also specify the desired + * session-ID + * @return OSGI service reference object to the desired service, null if not found + */ + @SuppressWarnings("unchecked") + public ServiceReference getServiceReference(Class serviceClass, String filter) { + ServiceKey key = new ServiceKey(serviceClass, filter != null ? filter : fServiceFilter); + if (fServiceReferences.containsKey(key)) { + return fServiceReferences.get(key); + } + + try { + ServiceReference[] references = fBundleContext.getServiceReferences(key.fClassName, key.fFilter); + assert references == null || references.length <= 1; + if (references == null || references.length == 0) { + return null; + } else { + fServiceReferences.put(key, references[0]); + return references[0]; + } + } catch(InvalidSyntaxException e) { + assert false : "Invalid session ID syntax"; //$NON-NLS-1$ + } catch(IllegalStateException e) { + // Can occur when plugin is shutting down. + } + return null; + } + + /** + * Convenience class to retrieve a service based on class name only. + * @param serviceClass class of the desired service + * @return instance of the desired service, null if not found + */ + public <V> V getService(Class<V> serviceClass) { + return getService(serviceClass, null); + } + + /** + * Retrieves the service given service class and optional filter. + * Filter should be used if there are multiple instances of the desired service + * running within the same session. + * @param serviceClass class of the desired service + * @param custom filter to use when searching for the service, this filter will + * be used instead of the standard filter so it should also specify the desired + * session-ID + * @return instance of the desired service, null if not found + */ + @SuppressWarnings("unchecked") + public <V> V getService(Class<V> serviceClass, String filter) { + ServiceReference serviceRef = getServiceReference(serviceClass, filter); + if (serviceRef == null) { + return null; + } else { + if (fServices.containsKey(serviceRef)) { + return (V)fServices.get(serviceRef); + } else { + V service = (V)fBundleContext.getService(serviceRef); + fServices.put(serviceRef, service); + return service; + } + } + } + + /** + * Un-gets all the serferences held by this tracker. Must be called + * to avoid leaking OSGI service references. + */ + public void dispose() { + for (Iterator<ServiceReference> itr = fServices.keySet().iterator(); itr.hasNext();) { + fBundleContext.ungetService(itr.next()); + itr.remove(); + } + } + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/DsfSession.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/DsfSession.java new file mode 100644 index 00000000000..349ae16447e --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/DsfSession.java @@ -0,0 +1,431 @@ +/******************************************************************************* + * 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.service; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Dictionary; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; + +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +import org.eclipse.cdt.dsf.internal.DsfPlugin; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.osgi.framework.Filter; + +/** + * Class to manage DSF sessions. A DSF session is a way to + * associate a set of DSF services that are running simultaneously and + * are interacting with each other to provide a complete set of functionality. + * <p> + * Properties of a session are following: + * <br>1. Each session is associated with a single DSF executor, although there + * could be multiple sessions using the same executor. + * <br>2. Each session has a unique String identifier, which has to be used by + * the services belonging to this session when registering with OSGI services. + * <br>3. Each session has its set of service event listeners. + * <br>4. Start and end of each session is announced by events, which are always + * sent on that session's executor dispatch thread. + * + * @see org.eclipse.cdt.dsf.concurrent.DsfExecutor + */ +@ConfinedToDsfExecutor("getExecutor") +public class DsfSession +{ + /** + * Listener for session started events. This listener is always going to be + * called in the dispatch thread of the session's executor. + */ + public static interface SessionStartedListener { + /** + * Called when a new session is started. It is always called in the + * dispatch thread of the new session. + */ + public void sessionStarted(DsfSession session); + } + + /** + * Listener for session ended events. This listener is always going to be + * called in the dispatch thread of the session's executor. + */ + public static interface SessionEndedListener { + /** + * Called when a session is ended. It is always called in the + * dispatch thread of the session. + */ + public void sessionEnded(DsfSession session); + } + + private static int fgSessionIdCounter = 0; + private static Set<DsfSession> fgActiveSessions = Collections.synchronizedSet(new HashSet<DsfSession>()); + private static List<SessionStartedListener> fSessionStartedListeners = Collections.synchronizedList(new ArrayList<SessionStartedListener>()); + private static List<SessionEndedListener> fSessionEndedListeners = Collections.synchronizedList(new ArrayList<SessionEndedListener>()); + + /** Returns true if given session is currently active */ + public static boolean isSessionActive(String sessionId) { + return getSession(sessionId) != null; + } + + /** Returns a session instance for given session identifier */ + @ThreadSafe + public static DsfSession getSession(String sessionId) { + synchronized(fgActiveSessions) { + for (DsfSession session : fgActiveSessions) { + if (session.getId().equals(sessionId)) { + return session; + } + } + } + return null; + } + + /** + * Registers a listener for session started events. + * Can be called on any thread. + */ + @ThreadSafe + public static void addSessionStartedListener(SessionStartedListener listener) { + assert !fSessionStartedListeners.contains(listener); + fSessionStartedListeners.add(listener); + } + + /** + * Un-registers a listener for session started events. + * Can be called on any thread. + */ + @ThreadSafe + public static void removeSessionStartedListener(SessionStartedListener listener) { + assert fSessionStartedListeners.contains(listener); + fSessionStartedListeners.remove(listener); + } + + /** + * Registers a listener for session ended events. + * Can be called on any thread. + */ + @ThreadSafe + public static void addSessionEndedListener(SessionEndedListener listener) { + assert !fSessionEndedListeners.contains(listener); + fSessionEndedListeners.add(listener); + } + + /** + * Un-registers a listener for session ended events. + * Can be called on any thread. + */ + @ThreadSafe + public static void removeSessionEndedListener(SessionEndedListener listener) { + assert fSessionEndedListeners.contains(listener); + fSessionEndedListeners.remove(listener); + } + + /** + * Starts and returns a new session instance. This method can be called on any + * thread, but the session-started listeners will be called using the session's + * executor. + * @param executor The DSF executor to use for this session. + * @param ownerId ID (plugin ID preferably) of the owner of this session + * @return instance object of the new session + */ + @ThreadSafe + public static DsfSession startSession(DsfExecutor executor, String ownerId) { + synchronized(fgActiveSessions) { + final DsfSession newSession = new DsfSession(executor, ownerId, Integer.toString(fgSessionIdCounter++)); + fgActiveSessions.add(newSession); + executor.submit( new DsfRunnable() { public void run() { + SessionStartedListener[] listeners = fSessionStartedListeners.toArray( + new SessionStartedListener[fSessionStartedListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].sessionStarted(newSession); + } + }}); + return newSession; + } + } + + /** + * Terminates the given session. This method can be also called on any + * thread, but the session-ended listeners will be called using the session's + * executor. + * @param session session to terminate + */ + @ThreadSafe + public static void endSession(final DsfSession session) { + synchronized(fgActiveSessions) { + if (!fgActiveSessions.contains(session)) { + throw new IllegalArgumentException(); + } + fgActiveSessions.remove(session); + session.getExecutor().submit( new DsfRunnable() { public void run() { + SessionEndedListener[] listeners = fSessionEndedListeners.toArray( + new SessionEndedListener[fSessionEndedListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].sessionEnded(session); + } + }}); + } + } + + private static class ListenerEntry { + Object fListener; + Filter fFilter; + + ListenerEntry(Object listener, Filter filter) { + fListener = listener; + fFilter = filter; + } + + @Override + public boolean equals(Object other) { + return other instanceof ListenerEntry && fListener.equals(((ListenerEntry)other).fListener); + } + + @Override + public int hashCode() { return fListener.hashCode(); } + } + + /** ID (plugin ID preferably) of the owner of this session */ + private String fOwnerId; + + /** Session ID of this session. */ + private String fId; + + /** Dispatch-thread executor for this session */ + private DsfExecutor fExecutor; + + /** Service start-up counter for this session */ + private int fServiceInstanceCounter; + + /** Map of registered event listeners. */ + private Map<ListenerEntry,Method[]> fListeners = new HashMap<ListenerEntry,Method[]>(); + + /** + * Map of registered adapters, for implementing the + * IModelContext.getAdapter() method. + * @see org.eclipse.cdt.dsf.datamodel.AbstractDMContext#getAdapter + */ + @SuppressWarnings("unchecked") + private Map<Class,Object> fAdapters = Collections.synchronizedMap(new HashMap<Class,Object>()); + + /** Returns the owner ID of this session */ + public String getOwnerId() { return fOwnerId; } + + public boolean isActive() { return DsfSession.isSessionActive(fId); } + + /** Returns the ID of this session */ + public String getId() { return fId; } + + /** Returns the DSF executor of this session */ + public DsfExecutor getExecutor() { return fExecutor; } + + /** + * Adds a new listener for service events in this session. + * @param listener the listener that will receive service events + * @param filter optional filter to restrict the services that the + * listener will receive events from + */ + public void addServiceEventListener(Object listener, Filter filter) { + ListenerEntry entry = new ListenerEntry(listener, filter); + assert !fListeners.containsKey(entry); + fListeners.put(entry, getEventHandlerMethods(listener)); + } + + /** + * Removes the given listener. + * @param listener listener to remove + */ + public void removeServiceEventListener(Object listener) { + ListenerEntry entry = new ListenerEntry(listener, null); + assert fListeners.containsKey(entry); + fListeners.remove(entry); + } + + /** + * Retrieves and increments the startup counter for services in this session. + * DSF services should retrieve this counter when they are initialized, + * and should return it through IService.getStartupNumber(). This number is then + * used to prioritize service events. + * @return current startup counter value + */ + public int getAndIncrementServiceStartupCounter() { return fServiceInstanceCounter++; } + + /** + * Dispatches the given event to service event listeners. The event is submitted to + * the executor to be dispatched. + * @param event to be sent out + * @param serviceProperties properties of the service requesting the event to be dispatched + */ + @ThreadSafe + @SuppressWarnings("unchecked") + public void dispatchEvent(final Object event, final Dictionary serviceProperties) { + getExecutor().submit(new DsfRunnable() { + public void run() { doDispatchEvent(event, serviceProperties);} + @Override + public String toString() { return "Event: " + event + ", from service " + serviceProperties; } //$NON-NLS-1$ //$NON-NLS-2$ + }); + } + + /** + * Registers a IModelContext adapter of given type. + * @param adapterType class type to register the adapter for + * @param adapter adapter instance to register + * @see org.eclipse.dsdp.model.AbstractDMContext#getAdapter + */ + @ThreadSafe + @SuppressWarnings("unchecked") + public void registerModelAdapter(Class adapterType, Object adapter) { + fAdapters.put(adapterType, adapter); + } + + /** + * Un-registers a IModelContext adapter of given type. + * @param adapterType adapter type to unregister + * @see org.eclipse.dsdp.model.AbstractDMContext#getAdapter + */ + @ThreadSafe + @SuppressWarnings("unchecked") + public void unregisterModelAdapter(Class adapterType) { + fAdapters.remove(adapterType); + } + + /** + * Retrieves an adapter for given type for IModelContext. + * @param adapterType adapter type to look fors + * @return adapter object for given type, null if none is registered with the session + * @see org.eclipse.dsdp.model.AbstractDMContext#getAdapter + */ + @ThreadSafe + @SuppressWarnings("unchecked") + public Object getModelAdapter(Class adapterType) { + return fAdapters.get(adapterType); + } + + @Override + @ThreadSafe + public boolean equals(Object other) { + return other instanceof DsfSession && fId.equals(((DsfSession)other).fId); + } + + @Override + @ThreadSafe + public int hashCode() { return fId.hashCode(); } + + @SuppressWarnings("unchecked") + private void doDispatchEvent(Object event, Dictionary serviceProperties) { + // Build a list of listeners; + SortedMap<ListenerEntry,List<Method>> listeners = new TreeMap<ListenerEntry,List<Method>>(new Comparator<ListenerEntry>() { + public int compare(ListenerEntry o1, ListenerEntry o2) { + if (o1.fListener == o2.fListener) { + return 0; + } if (o1.fListener instanceof IDsfService && !(o2.fListener instanceof IDsfService)) { + return Integer.MIN_VALUE; + } else if (o2.fListener instanceof IDsfService && !(o1.fListener instanceof IDsfService)) { + return Integer.MAX_VALUE; + } else if ( (o1.fListener instanceof IDsfService) && (o2.fListener instanceof IDsfService) ) { + return ((IDsfService)o1.fListener).getStartupNumber() - ((IDsfService)o2.fListener).getStartupNumber(); + } + return 1; + } + + @Override + public boolean equals(Object obj) { + return obj == this; + } + }); + + // Build a list of listeners and methods that are registered for this event class. + Class<?> eventClass = event.getClass(); + for (Map.Entry<ListenerEntry,Method[]> entry : fListeners.entrySet()) { + if (entry.getKey().fFilter != null && !entry.getKey().fFilter.match(serviceProperties)) { + // Dispatching service doesn't match the listener's filter, skip it. + continue; + } + Method[] allMethods = entry.getValue(); + List<Method> matchingMethods = new ArrayList<Method>(); + for (Method method : allMethods) { + assert method.getParameterTypes().length > 0 : eventClass.getName() + "." + method.getName() //$NON-NLS-1$ + + " signature contains zero parameters"; //$NON-NLS-1$ + if ( method.getParameterTypes()[0].isAssignableFrom(eventClass) ) { + matchingMethods.add(method); + } + } + if (!matchingMethods.isEmpty()) { + listeners.put(entry.getKey(), matchingMethods); + } + } + + // Call the listeners + for (Map.Entry<ListenerEntry,List<Method>> entry : listeners.entrySet()) { + for (Method method : entry.getValue()) { + try { + method.invoke(entry.getKey().fListener, new Object[] { event } ); + } + catch (IllegalAccessException e) { + DsfPlugin.getDefault().getLog().log(new Status( + IStatus.ERROR, DsfPlugin.PLUGIN_ID, -1, "Security exception when calling a service event handler method", e)); //$NON-NLS-1$ + assert false : "IServiceEventListener.ServiceHandlerMethod method not accessible, is listener declared public?"; //$NON-NLS-1$ + } + catch (InvocationTargetException e) { + DsfPlugin.getDefault().getLog().log(new Status( + IStatus.ERROR, DsfPlugin.PLUGIN_ID, -1, "Invocation exception when calling a service event handler method", e)); //$NON-NLS-1$ + assert false : "Exception thrown by a IServiceEventListener.ServiceHandlerMethod method"; //$NON-NLS-1$ + } + } + } + } + + private Method[] getEventHandlerMethods(Object listener) + { + List<Method> retVal = new ArrayList<Method>(); + try { + Method[] methods = listener.getClass().getMethods(); + for (Method method : methods) { + if (method.isAnnotationPresent(DsfServiceEventHandler.class)) { + Class<?>[] paramTypes = method.getParameterTypes(); + if (paramTypes.length > 2) { + throw new IllegalArgumentException("ServiceEventHandler method has incorrect number of parameters"); //$NON-NLS-1$ + } + retVal.add(method); + } + } + } catch(SecurityException e) { + throw new IllegalArgumentException("No permission to access ServiceEventHandler method"); //$NON-NLS-1$ + } + + if (retVal.isEmpty()) { + throw new IllegalArgumentException("No methods marked with @ServiceEventHandler in listener, is listener declared public?"); //$NON-NLS-1$ + } + return retVal.toArray(new Method[retVal.size()]); + } + + /** + * Class to be instanciated only using startSession() + */ + @ThreadSafe + private DsfSession(DsfExecutor executor, String ownerId, String id) { + fId = id; + fOwnerId = ownerId; + fExecutor = executor; + } + +}
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/IDsfService.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/IDsfService.java new file mode 100644 index 00000000000..f0181ea115b --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/IDsfService.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * 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.service; + +import java.util.Dictionary; + +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; + +/** + * The inteface that all DSF services must implement. It only privides a + * few features to help manage and identify the servies using the OSGI services + * framework. + * <p> + * Each service should register itself with OSGI services framework using + * the BundleContext.registerService() method. And each service should use the + * session ID that it is registering with as one of the service properties. If there + * is more than one instance of the service to be instanciated for a given session, + * additional properties should be used when registering the service to allow clients + * to uniquely identify the services. + * <p> + * By convention, all methods of DSF services can be called only on the dispatch + * thread of the DSF executor that is associated with the service. If a + * service exposes a method that is to be called on non-dispatch thread, it should + * be documented so. + * + * @see org.osgi.framework.BundleContext#registerService(String[], Object, Dictionary) + */ +@ConfinedToDsfExecutor("getExecutor") +public interface IDsfService { + + /** + * Property name for the session-id of this service. This property should be set by + * all DSF services when they are registered with OSGI service framework. + */ + final static String PROP_SESSION_ID = "org.eclipse.cdt.dsf.service.IService.session_id"; //$NON-NLS-1$ + + /** + * Returns the DSF Session that this service belongs to. + */ + DsfSession getSession(); + + /** + * Returns the executor that should be used to call methods of this service. + * This method is equivalent to calling getSession().getExecutor() + */ + DsfExecutor getExecutor(); + + /** + * Returns the map of properties that this service was registered with. + */ + @SuppressWarnings("unchecked") + Dictionary getProperties(); + + /** + * Returns a filter string that can be used to uniquely identify this + * service. This filter string should be based on the properties and class + * name, which were used to register this service. + * @see org.osgi.framework.BundleContext#getServiceReferences + */ + String getServiceFilter(); + + /** + * Performs initialization and registration of the given service. Implementation + * should initialize the service, so that all methods and events belonging to this + * service can be used following the initialization. + * <br>Note: Since service initializaiton should be performed by an external + * logic, if this service depends on other services, the implementaion should + * assume that these services are already present, and if they are not, the + * initializaiton should fail. + * @param requestMonitor callback to be submitted when the initialization is complete + */ + void initialize(RequestMonitor requestMonitor); + + /** + * Performs shutdown and de-registration of the given service. + * @param requestMonitor callback to be submitted when shutdown is complete + */ + void shutdown(RequestMonitor requestMonitor); + + /** + * Returns the startup order number of this service among services in the same session. + * Implementations should get this number during initialization by calling + * Session.getAndIncrementServiceStartupCounter(). This counter is used to Session + * objects to prioritize the listeners of service events. + * @return startup order number of this service + * @see org.eclipse.cdt.dsf.service.DsfSession#getAndIncrementServiceStartupCounter() + */ + int getStartupNumber(); +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda.ui/.classpath b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/.classpath new file mode 100644 index 00000000000..304e86186aa --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/.classpath @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/> + <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda.ui/.cvsignore b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/.cvsignore new file mode 100644 index 00000000000..ba077a4031a --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/.cvsignore @@ -0,0 +1 @@ +bin diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda.ui/.project b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/.project new file mode 100644 index 00000000000..2d088321537 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/.project @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>org.eclipse.cdt.examples.dsf.pda.ui</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.ManifestBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.SchemaBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.pde.PluginNature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda.ui/.settings/org.eclipse.jdt.core.prefs b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..d777d691415 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,65 @@ +#Tue Jun 24 11:03:46 PDT 2008 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 +org.eclipse.jdt.core.compiler.compliance=1.5 +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.autoboxing=ignore +org.eclipse.jdt.core.compiler.problem.deprecation=warning +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning +org.eclipse.jdt.core.compiler.problem.emptyStatement=warning +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore +org.eclipse.jdt.core.compiler.problem.finalParameterBound=ignore +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning +org.eclipse.jdt.core.compiler.problem.forbiddenReference=error +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=warning +org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore +org.eclipse.jdt.core.compiler.problem.nullReference=error +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore +org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled +org.eclipse.jdt.core.compiler.problem.unusedImport=warning +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=warning +org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.source=1.5 diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda.ui/META-INF/MANIFEST.MF b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..a453d5834ef --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/META-INF/MANIFEST.MF @@ -0,0 +1,24 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: PDA Example Device Debugging UI Plug-in +Bundle-SymbolicName: org.eclipse.cdt.examples.dsf.pda.ui;singleton:=true +Bundle-Version: 2.0.0.qualifier +Bundle-Activator: org.eclipse.cdt.examples.dsf.pda.ui.PDAUIPlugin +Require-Bundle: org.eclipse.core.runtime, + org.eclipse.core.resources, + org.eclipse.debug.core, + org.eclipse.ui, + org.eclipse.debug.ui, + org.eclipse.jface.text, + org.eclipse.ui.editors, + org.eclipse.ui.workbench.texteditor, + org.eclipse.ui.ide, + org.eclipse.cdt.examples.dsf.pda, + org.eclipse.cdt.dsf, + org.eclipse.cdt.dsf.ui +Bundle-ActivationPolicy: lazy +Export-Package: org.eclipse.cdt.examples.dsf.pda.ui, + org.eclipse.cdt.examples.dsf.pda.ui.breakpoints, + org.eclipse.cdt.examples.dsf.pda.ui.editor, + org.eclipse.cdt.examples.dsf.pda.ui.launcher +Bundle-RequiredExecutionEnvironment: J2SE-1.5 diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda.ui/about.html b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/about.html new file mode 100644 index 00000000000..c1cb2e12f40 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/about.html @@ -0,0 +1,28 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/> +<title>About</title> +</head> +<body lang="EN-US"> +<h2>About This Content</h2> + +<p>May 14, 2008</p> +<h3>License</h3> + +<p>The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>. +For purposes of the EPL, "Program" will mean the Content.</p> + +<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at <a href="http://www.eclipse.org">http://www.eclipse.org</a>.</p> + +</body> +</html>
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda.ui/build.properties b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/build.properties new file mode 100644 index 00000000000..e5e3b48cf3f --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/build.properties @@ -0,0 +1,22 @@ +############################################################################### +# 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 +############################################################################### +source.. = src/ +output.. = bin/ +bin.includes = plugin.xml,\ + bin/,\ + about.html,\ + META-INF/,\ + .,\ + icons/ +src.includes = src/,\ + about.html,\ + icons/,\ + plugin.xml diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda.ui/icons/full/obj16/pda.gif b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/icons/full/obj16/pda.gif Binary files differnew file mode 100644 index 00000000000..9a6adbfc87e --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/icons/full/obj16/pda.gif diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda.ui/plugin.xml b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/plugin.xml new file mode 100644 index 00000000000..da6e4f6b6ee --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/plugin.xml @@ -0,0 +1,106 @@ +<?xml version="1.0" encoding="UTF-8"?> +<?eclipse version="3.0"?> +<plugin> + <extension + point="org.eclipse.debug.ui.launchConfigurationTabGroups"> + <launchConfigurationTabGroup + class="org.eclipse.cdt.examples.dsf.pda.ui.launcher.PDATabGroup" + description="Specify and launch a PDA(DSF) program" + id="org.eclipse.cdt.examples.dsf.pda.tabGroup" + type="org.eclipse.cdt.examples.dsf.pda.launchType"/> + </extension> + <extension + point="org.eclipse.debug.ui.launchConfigurationTypeImages"> + <launchConfigurationTypeImage + icon="icons/full/obj16/pda.gif" + configTypeID="org.eclipse.cdt.examples.dsf.pda.launchType" + id="org.eclipse.cdt.examples.dsf.pda.typeImage"/> + </extension> + + <extension + point="org.eclipse.core.runtime.adapters"> + <factory + class="org.eclipse.cdt.examples.dsf.pda.ui.PDAAdapterFactory" + adaptableType="org.eclipse.cdt.examples.dsf.pda.launch.PDALaunch"> + <adapter type="org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider"/> + <adapter type="org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxyFactory"/> + </factory> + </extension> + + <extension + point="org.eclipse.ui.editors"> + <editor + filenames="*.pda" + class="org.eclipse.cdt.examples.dsf.pda.ui.editor.PDAEditor" + icon="icons/full/obj16/pda.gif" + default="true" + name="PDA(DSF) Editor" + id="org.eclipse.cdt.examples.dsf.pda.editor" + extensions="pda"/> + </extension> + <extension + point="org.eclipse.ui.editorActions"> + <editorContribution + targetID="org.eclipse.cdt.examples.dsf.pda.editor" + id="org.eclipse.cdt.examples.dsf.pda.rulerActions"> + <action + label="Not Used" + class="org.eclipse.debug.ui.actions.RulerToggleBreakpointActionDelegate" + style="push" + actionID="RulerDoubleClick" + id="org.eclipse.cdt.examples.dsf.pda.doubleClickBreakpointAction"/> + </editorContribution> + </extension> + <extension + point="org.eclipse.ui.contexts"> + <context + parentId="org.eclipse.debug.ui.debugging" + description="Debugging PDA(DSF) Programs" + name="Debugging PDA(DSF) Programs" + id="org.eclipse.cdt.examples.dsf.pda.debugging"/> + </extension> + <extension + point="org.eclipse.debug.ui.debugModelContextBindings"> + <modelContextBinding + contextId="org.eclipse.cdt.examples.dsf.pda.debugging" + debugModelId="org.eclipse.cdt.examples.dsf.pda.debugModel"/> + </extension> + <extension + point="org.eclipse.debug.ui.contextViewBindings"> + <contextViewBinding + contextId="org.eclipse.cdt.examples.dsf.pda.debugging" + viewId="org.eclipse.cdt.examples.dsf.pda.dataStackView"/> + </extension> + + <extension + point="org.eclipse.ui.popupMenus"> + <viewerContribution + targetID="org.eclipse.cdt.examples.dsf.pda.editor.rulerMenu" + id="org.eclipse.cdt.examples.dsf.pda.editor.rulerActions"> + <action + label="Toggle Breakpoint" + class="org.eclipse.debug.ui.actions.RulerToggleBreakpointActionDelegate" + menubarPath="debug" + id="org.eclipse.cdt.examples.dsf.pda.editor.ruler.toggleBreakpointAction"/> + </viewerContribution> + <viewerContribution + targetID="org.eclipse.cdt.examples.dsf.pda.editor.contextMenu" + id="org.eclipse.cdt.examples.dsf.pda.editor.menuActions"> + <action + label="Run to Line" + definitionId="org.eclipse.debug.ui.commands.RunToLine" + class="org.eclipse.debug.ui.actions.RunToLineActionDelegate" + menubarPath="additions" + id="org.eclipse.cdt.examples.dsf.pda.editor.context.runToLineAction"/> + </viewerContribution> + </extension> + + <extension + point="org.eclipse.core.runtime.adapters"> + <factory + class="org.eclipse.cdt.examples.dsf.pda.ui.breakpoints.PDAEditorAdapterFactory" + adaptableType="org.eclipse.cdt.examples.dsf.pda.ui.editor.PDAEditor"> + <adapter type="org.eclipse.debug.ui.actions.IToggleBreakpointsTarget"/> + </factory> + </extension> --> +</plugin> diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/PDAAdapterFactory.java b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/PDAAdapterFactory.java new file mode 100644 index 00000000000..ce756d66613 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/PDAAdapterFactory.java @@ -0,0 +1,266 @@ +/******************************************************************************* + * 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.examples.dsf.pda.ui; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.WeakHashMap; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +import org.eclipse.cdt.dsf.debug.ui.actions.DsfResumeCommand; +import org.eclipse.cdt.dsf.debug.ui.actions.DsfStepIntoCommand; +import org.eclipse.cdt.dsf.debug.ui.actions.DsfStepOverCommand; +import org.eclipse.cdt.dsf.debug.ui.actions.DsfStepReturnCommand; +import org.eclipse.cdt.dsf.debug.ui.actions.DsfSuspendCommand; +import org.eclipse.cdt.dsf.debug.ui.sourcelookup.DsfSourceDisplayAdapter; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.SteppingController; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.launch.DefaultDsfModelSelectionPolicyFactory; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.examples.dsf.pda.PDAPlugin; +import org.eclipse.cdt.examples.dsf.pda.launch.PDALaunch; +import org.eclipse.cdt.examples.dsf.pda.ui.actions.PDATerminateCommand; +import org.eclipse.cdt.examples.dsf.pda.ui.viewmodel.PDAVMAdapter; +import org.eclipse.core.runtime.IAdapterFactory; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchesListener2; +import org.eclipse.debug.core.commands.IResumeHandler; +import org.eclipse.debug.core.commands.IStepIntoHandler; +import org.eclipse.debug.core.commands.IStepOverHandler; +import org.eclipse.debug.core.commands.IStepReturnHandler; +import org.eclipse.debug.core.commands.ISuspendHandler; +import org.eclipse.debug.core.commands.ITerminateHandler; +import org.eclipse.debug.core.model.IDebugModelProvider; +import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentationFactory; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxyFactory; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicyFactory; +import org.eclipse.debug.ui.sourcelookup.ISourceDisplay; + +/** + * The adapter factory is the central point of control of view model and other + * UI adapters of a DSF-based debugger. As new launches are created and + * old ones removed, this factory manages the life cycle of the associated + * UI adapters. + * <p> + * As a platform adapter factory, this factory only provides adapters for + * the launch object. Adapters for all other objects in the DSF model hierarchy + * are registered with the DSF session. + * </p> + */ +@ThreadSafe +@SuppressWarnings({"restriction"}) +public class PDAAdapterFactory implements IAdapterFactory, ILaunchesListener2 +{ + /** + * Contains the set of adapters that are created for each launch instance. + */ + @Immutable + private static class LaunchAdapterSet { + // View Model adapter + final PDAVMAdapter fViewModelAdapter; + + // Source lookup and positioning adapter + final DsfSourceDisplayAdapter fSourceDisplayAdapter; + + // Command adapters + final DsfStepIntoCommand fStepIntoCommand; + final DsfStepOverCommand fStepOverCommand; + final DsfStepReturnCommand fStepReturnCommand; + final DsfSuspendCommand fSuspendCommand; + final DsfResumeCommand fResumeCommand; + final PDATerminateCommand fTerminateCommand; + + // Adapters for integration with other UI actions + final IDebugModelProvider fDebugModelProvider; + final PDALaunch fLaunch; + + final SteppingController fSteppingController; + + private IModelSelectionPolicyFactory fModelSelectionPolicyFactory; + + LaunchAdapterSet(PDALaunch launch) { + // Initialize launch and session. + fLaunch = launch; + DsfSession session = launch.getSession(); + + // register stepping controller + fSteppingController = new SteppingController(session); + session.registerModelAdapter(SteppingController.class, fSteppingController); + + // Initialize VM + fViewModelAdapter = new PDAVMAdapter(session, fSteppingController); + + // Initialize source lookup + fSourceDisplayAdapter = new DsfSourceDisplayAdapter(session, (ISourceLookupDirector)launch.getSourceLocator(), fSteppingController); + session.registerModelAdapter(ISourceDisplay.class, fSourceDisplayAdapter); + + // Default selection policy + fModelSelectionPolicyFactory = new DefaultDsfModelSelectionPolicyFactory(); + session.registerModelAdapter(IModelSelectionPolicyFactory.class, fModelSelectionPolicyFactory); + + // Initialize retargetable command handler. + fStepIntoCommand = new DsfStepIntoCommand(session, null); + fStepOverCommand = new DsfStepOverCommand(session, null); + fStepReturnCommand = new DsfStepReturnCommand(session); + fSuspendCommand = new DsfSuspendCommand(session); + fResumeCommand = new DsfResumeCommand(session); + fTerminateCommand = new PDATerminateCommand(session); + session.registerModelAdapter(IStepIntoHandler.class, fStepIntoCommand); + session.registerModelAdapter(IStepOverHandler.class, fStepOverCommand); + session.registerModelAdapter(IStepReturnHandler.class, fStepReturnCommand); + session.registerModelAdapter(ISuspendHandler.class, fSuspendCommand); + session.registerModelAdapter(IResumeHandler.class, fResumeCommand); + session.registerModelAdapter(ITerminateHandler.class, fTerminateCommand); + + // Initialize debug model provider + fDebugModelProvider = new IDebugModelProvider() { + public String[] getModelIdentifiers() { + return new String[] { PDAPlugin.ID_PDA_DEBUG_MODEL }; + } + }; + session.registerModelAdapter(IDebugModelProvider.class, fDebugModelProvider); + + // Register the launch as an adapter This ensures that the launch, + // and debug model ID will be associated with all DMContexts from this + // session. + session.registerModelAdapter(ILaunch.class, fLaunch); + } + + void dispose() { + DsfSession session = fLaunch.getSession(); + + fViewModelAdapter.dispose(); + + session.unregisterModelAdapter(ISourceDisplay.class); + if (fSourceDisplayAdapter != null) fSourceDisplayAdapter.dispose(); + + session.unregisterModelAdapter(SteppingController.class); + fSteppingController.dispose(); + + session.unregisterModelAdapter(IModelSelectionPolicyFactory.class); + + session.unregisterModelAdapter(IStepIntoHandler.class); + session.unregisterModelAdapter(IStepOverHandler.class); + session.unregisterModelAdapter(IStepReturnHandler.class); + session.unregisterModelAdapter(ISuspendHandler.class); + session.unregisterModelAdapter(IResumeHandler.class); + session.unregisterModelAdapter(ITerminateHandler.class); + fStepIntoCommand.dispose(); + fStepOverCommand.dispose(); + fStepReturnCommand.dispose(); + fSuspendCommand.dispose(); + fResumeCommand.dispose(); + fTerminateCommand.dispose(); + } + } + + /** + * Active adapter sets. They are accessed using the launch instance + * which owns the debug services session. + */ + private static Map<PDALaunch, LaunchAdapterSet> fgLaunchAdapterSets = + Collections.synchronizedMap(new HashMap<PDALaunch, LaunchAdapterSet>()); + + /** + * Map of launches for which adapter sets have already been disposed. + * This map (used as a set) is maintained in order to avoid re-creating an + * adapter set after the launch was removed from the launch manager, but + * while the launch is still being held by other classes which may + * request its adapters. A weak map is used to avoid leaking + * memory once the launches are no longer referenced. + * <p> + * Access to this map is synchronized using the fgLaunchAdapterSets + * instance. + * </p> + */ + private static Map<ILaunch, Object> fgDisposedLaunchAdapterSets = + new WeakHashMap<ILaunch, Object>(); + + static void disposeAdapterSet(ILaunch launch) { + synchronized(fgLaunchAdapterSets) { + if ( fgLaunchAdapterSets.containsKey(launch) ) { + fgLaunchAdapterSets.remove(launch).dispose(); + fgDisposedLaunchAdapterSets.put(launch, null); + } + } + } + + public PDAAdapterFactory() { + DebugPlugin.getDefault().getLaunchManager().addLaunchListener(this); + } + + // This IAdapterFactory method returns adapters for the PDA launch object only. + @SuppressWarnings("unchecked") // IAdapterFactory is Java 1.3 + public Object getAdapter(Object adaptableObject, Class adapterType) { + if (!(adaptableObject instanceof PDALaunch)) return null; + + PDALaunch launch = (PDALaunch)adaptableObject; + + // Check for valid session. + // Note: even if the session is no longer active, the adapter set + // should still be returned. This is because the view model may still + // need to show elements representing a terminated process/thread/etc. + DsfSession session = launch.getSession(); + if (session == null) return null; + + // Find the correct set of adapters based on the launch. If not found + // it means that we have a new launch, and we have to create a + // new set of adapters. + LaunchAdapterSet adapterSet; + synchronized(fgLaunchAdapterSets) { + // The adapter set for the given launch was already disposed. + // Return a null adapter. + if (fgDisposedLaunchAdapterSets.containsKey(launch)) { + return null; + } + adapterSet = fgLaunchAdapterSets.get(launch); + if (adapterSet == null) { + adapterSet = new LaunchAdapterSet(launch); + fgLaunchAdapterSets.put(launch, adapterSet); + } + } + + // Returns the adapter type for the launch object. + if (adapterType.equals(IElementContentProvider.class)) return adapterSet.fViewModelAdapter; + else if (adapterType.equals(IModelProxyFactory.class)) return adapterSet.fViewModelAdapter; + else return null; + } + + @SuppressWarnings("unchecked") // IAdapterFactory is Java 1.3 + public Class[] getAdapterList() { + return new Class[] { IElementContentProvider.class, IModelProxyFactory.class, IColumnPresentationFactory.class }; + } + + public void launchesRemoved(ILaunch[] launches) { + // Dispose the set of adapters for a launch only after the launch is + // removed from the view. If the launch is terminated, the adapters + // are still needed to populate the contents of the view. + for (ILaunch launch : launches) { + if (launch instanceof PDALaunch) { + disposeAdapterSet(launch); + } + } + } + + public void launchesTerminated(ILaunch[] launches) { + } + + public void launchesAdded(ILaunch[] launches) { + } + + public void launchesChanged(ILaunch[] launches) { + } + +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/PDAUIPlugin.java b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/PDAUIPlugin.java new file mode 100644 index 00000000000..e45c6727647 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/PDAUIPlugin.java @@ -0,0 +1,155 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 + * Bjorn Freeman-Benson - initial API and implementation + * Wind River Systems - adopted to use with DSF + *******************************************************************************/ +package org.eclipse.cdt.examples.dsf.pda.ui; + +import java.net.URL; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.cdt.examples.dsf.pda.launch.PDALaunch; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.resource.ImageRegistry; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.internal.util.BundleUtility; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +/** + * The main plugin class to be used in the desktop. + */ +public class PDAUIPlugin extends AbstractUIPlugin { + + public static String PLUGIN_ID = "org.eclipse.cdt.examples.dsf.pda.ui "; + + //The shared instance. + private static PDAUIPlugin plugin; + + private static BundleContext fContext; + + private final static String ICONS_PATH = "icons/full/";//$NON-NLS-1$ + private final static String PATH_OBJECT = ICONS_PATH + "obj16/"; //Model object icons //$NON-NLS-1$ + + /** + * PDA program image + */ + public final static String IMG_OBJ_PDA = "IMB_OBJ_PDA"; + + /** + * Keyword color + */ + public final static RGB KEYWORD = new RGB(0,0,255); + public final static RGB LABEL = new RGB(128, 128, 0); + + /** + * Managed colors + */ + private Map<RGB, Color> fColors = new HashMap<RGB, Color>(); + + /** + * The constructor. + */ + public PDAUIPlugin() { + super(); + plugin = this; + } + + /** + * This method is called upon plug-in activation + */ + @Override + public void start(BundleContext context) throws Exception { + fContext = context; + super.start(context); +// Toggles single threaded adapter example +// IAdapterManager adapterManager = Platform.getAdapterManager(); +// IAdapterFactory factory = new AdapterFactory(); +// adapterManager.registerAdapters(factory, PDADebugTarget.class); + } + + /** + * This method is called when the plug-in is stopped + */ + @Override + public void stop(BundleContext context) throws Exception { + disposeAdapterSets(); + super.stop(context); + plugin = null; + fContext = null; + for (Map.Entry<RGB, Color> entry : fColors.entrySet()) { + entry.getValue().dispose(); + } + } + + /** + * Returns the shared instance. + */ + public static PDAUIPlugin getDefault() { + return plugin; + } + + public static BundleContext getBundleContext() { + return fContext; + } + + @Override + protected void initializeImageRegistry(ImageRegistry reg) { + declareImage(IMG_OBJ_PDA, PATH_OBJECT + "pda.gif"); + } + + /** + * Declares a workbench image given the path of the image file (relative to + * the workbench plug-in). This is a helper method that creates the image + * descriptor and passes it to the main <code>declareImage</code> method. + * + * @param symbolicName the symbolic name of the image + * @param path the path of the image file relative to the base of the workbench + * plug-ins install directory + * <code>false</code> if this is not a shared image + */ + private void declareImage(String key, String path) { + URL url = BundleUtility.find("org.eclipse.cdt.examples.dsf.pda.ui", path); + ImageDescriptor desc = ImageDescriptor.createFromURL(url); + getImageRegistry().put(key, desc); + } + + /** + * Returns the color described by the given RGB. + * + * @param rgb + * @return color + */ + public Color getColor(RGB rgb) { + Color color = fColors.get(rgb); + if (color == null) { + color= new Color(Display.getCurrent(), rgb); + fColors.put(rgb, color); + } + return color; + } + + /** + * Dispose adapter sets for all launches. + */ + private void disposeAdapterSets() { + for (ILaunch launch : DebugPlugin.getDefault().getLaunchManager().getLaunches()) { + if (launch instanceof PDALaunch) { + PDAAdapterFactory.disposeAdapterSet(launch); + } + } + } + + } diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/actions/PDATerminateCommand.java b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/actions/PDATerminateCommand.java new file mode 100644 index 00000000000..923be68441f --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/actions/PDATerminateCommand.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.examples.dsf.pda.ui.actions; + +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +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.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; +import org.eclipse.cdt.examples.dsf.pda.service.PDACommandControl; +import org.eclipse.cdt.examples.dsf.pda.service.PDAVirtualMachineDMContext; +import org.eclipse.cdt.examples.dsf.pda.ui.PDAUIPlugin; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.commands.IDebugCommandRequest; +import org.eclipse.debug.core.commands.IEnabledStateRequest; +import org.eclipse.debug.core.commands.ITerminateHandler; + +/** + * The terminate command is specialized for the PDA debugger. Currently there + * is no standard interface for terminating a debug session in DSF, because the + * details of initiating and shutting down a debug session vary greatly in + * different debuggers. + */ +public class PDATerminateCommand implements ITerminateHandler { + // The executor and the services tracker, both initialized from a DSF session. + private final DsfSession fSession; + private final DsfServicesTracker fTracker; + + public PDATerminateCommand(DsfSession session) { + fSession = session; + fTracker = new DsfServicesTracker(PDAUIPlugin.getBundleContext(), session.getId()); + } + + public void dispose() { + // DSF services tracker always has to be disposed, because the OSGi services + // use reference counting. + fTracker.dispose(); + } + + // Run control may not be available after a connection is terminated and shut down. + public void canExecute(final IEnabledStateRequest request) { + // Terminate can only operate on a single element. + if (request.getElements().length != 1 || + !(request.getElements()[0] instanceof IDMVMContext) ) + { + request.setEnabled(false); + request.done(); + return; + } + + // Find the PDA program context in the selected element. If one is not found, + // the action should be disabled. + IDMVMContext vmc = (IDMVMContext)request.getElements()[0]; + final PDAVirtualMachineDMContext pdaProgramCtx = DMContexts.getAncestorOfType(vmc.getDMContext(), PDAVirtualMachineDMContext.class); + if (pdaProgramCtx == null) { + request.setEnabled(false); + request.done(); + return; + } + + try { + fSession.getExecutor().execute( + new DsfRunnable() { + public void run() { + // Get the processes service and the exec context. + PDACommandControl commandControl = fTracker.getService(PDACommandControl.class); + if (commandControl == null || pdaProgramCtx == null) { + // Context or service already invalid. + request.setEnabled(false); + request.done(); + } else { + // Check whether the control is terminated. + request.setEnabled(!commandControl.isTerminated()); + request.done(); + } + } + }); + } catch (RejectedExecutionException e) { + // The DSF session for this context is no longer active. It's possible to check + // for this condition before calling fSession.getExecutor().execute(), but + // since this method is executing in a different thread than the session control, + // there would still be a chance for a race condition leading to this exception. + request.setEnabled(false); + request.done(); + } + } + + public boolean execute(final IDebugCommandRequest request) { + // Skip the checks and assume that this method is called only if the action + // was enabled. + + try { + fSession.getExecutor().submit(new DsfRunnable() { + public void run() { + // If the command control service is available, attempt to terminate the program. + PDACommandControl commandControl = fTracker.getService(PDACommandControl.class); + if (commandControl != null) { + + commandControl.terminate( + new RequestMonitor(ImmediateExecutor.getInstance(), null) { + @Override + protected void handleCompleted() { + request.setStatus(getStatus()); + request.done(); + } + }); + } + } + }); + } catch (RejectedExecutionException e) { + request.setStatus(new Status(IStatus.ERROR, PDAUIPlugin.PLUGIN_ID, "PDA debug session is shut down.")); + request.done(); + } + return false; + } + +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/breakpoints/PDABreakpointAdapter.java b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/breakpoints/PDABreakpointAdapter.java new file mode 100644 index 00000000000..806e87bd88b --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/breakpoints/PDABreakpointAdapter.java @@ -0,0 +1,225 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 + * Bjorn Freeman-Benson - initial API and implementation + * Wind River Systems - adopted to use with DSF + *******************************************************************************/ +package org.eclipse.cdt.examples.dsf.pda.ui.breakpoints; + +import org.eclipse.cdt.examples.dsf.pda.PDAPlugin; +import org.eclipse.cdt.examples.dsf.pda.breakpoints.PDALineBreakpoint; +import org.eclipse.cdt.examples.dsf.pda.breakpoints.PDAWatchpoint; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.model.IBreakpoint; +import org.eclipse.debug.core.model.ILineBreakpoint; +import org.eclipse.debug.ui.actions.IToggleBreakpointsTargetExtension; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.texteditor.IDocumentProvider; +import org.eclipse.ui.texteditor.ITextEditor; + + +/** + * Adapter to create breakpoints in PDA files. + * <p> + * This class is identical to the corresponding in PDA debugger implemented in + * org.eclipse.debug.examples.ui. + * </p> + */ +public class PDABreakpointAdapter implements IToggleBreakpointsTargetExtension { + /* (non-Javadoc) + * @see org.eclipse.debug.ui.actions.IToggleBreakpointsTarget#toggleLineBreakpoints(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection) + */ + public void toggleLineBreakpoints(IWorkbenchPart part, ISelection selection) throws CoreException { + ITextEditor textEditor = getEditor(part); + if (textEditor != null) { + IResource resource = (IResource) textEditor.getEditorInput().getAdapter(IResource.class); + ITextSelection textSelection = (ITextSelection) selection; + int lineNumber = textSelection.getStartLine(); + IBreakpoint[] breakpoints = DebugPlugin.getDefault().getBreakpointManager().getBreakpoints(PDAPlugin.ID_PDA_DEBUG_MODEL); + for (int i = 0; i < breakpoints.length; i++) { + IBreakpoint breakpoint = breakpoints[i]; + if (breakpoint instanceof ILineBreakpoint && resource.equals(breakpoint.getMarker().getResource())) { + if (((ILineBreakpoint)breakpoint).getLineNumber() == (lineNumber + 1)) { + // remove + breakpoint.delete(); + return; + } + } + } + // create line breakpoint (doc line numbers start at 0) + PDALineBreakpoint lineBreakpoint = new PDALineBreakpoint(resource, lineNumber + 1); + DebugPlugin.getDefault().getBreakpointManager().addBreakpoint(lineBreakpoint); + } + } + /* (non-Javadoc) + * @see org.eclipse.debug.ui.actions.IToggleBreakpointsTarget#canToggleLineBreakpoints(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection) + */ + public boolean canToggleLineBreakpoints(IWorkbenchPart part, ISelection selection) { + return getEditor(part) != null; + } + + /** + * Returns the editor being used to edit a PDA file, associated with the + * given part, or <code>null</code> if none. + * + * @param part workbench part + * @return the editor being used to edit a PDA file, associated with the + * given part, or <code>null</code> if none + */ + private ITextEditor getEditor(IWorkbenchPart part) { + if (part instanceof ITextEditor) { + ITextEditor editorPart = (ITextEditor) part; + IResource resource = (IResource) editorPart.getEditorInput().getAdapter(IResource.class); + if (resource != null) { + String extension = resource.getFileExtension(); + if (extension != null && extension.equals("pda")) { + return editorPart; + } + } + } + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.actions.IToggleBreakpointsTarget#toggleMethodBreakpoints(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection) + */ + public void toggleMethodBreakpoints(IWorkbenchPart part, ISelection selection) throws CoreException { + } + /* (non-Javadoc) + * @see org.eclipse.debug.ui.actions.IToggleBreakpointsTarget#canToggleMethodBreakpoints(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection) + */ + public boolean canToggleMethodBreakpoints(IWorkbenchPart part, ISelection selection) { + return false; + } + /* (non-Javadoc) + * @see org.eclipse.debug.ui.actions.IToggleBreakpointsTarget#toggleWatchpoints(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection) + */ + public void toggleWatchpoints(IWorkbenchPart part, ISelection selection) throws CoreException { + String[] variableAndFunctionName = getVariableAndFunctionName(part, selection); + if (variableAndFunctionName != null && part instanceof ITextEditor && selection instanceof ITextSelection) { + ITextEditor editorPart = (ITextEditor)part; + int lineNumber = ((ITextSelection)selection).getStartLine(); + IResource resource = (IResource) editorPart.getEditorInput().getAdapter(IResource.class); + String var = variableAndFunctionName[0]; + String fcn = variableAndFunctionName[1]; + // look for existing watchpoint to delete + IBreakpoint[] breakpoints = DebugPlugin.getDefault().getBreakpointManager().getBreakpoints(PDAPlugin.ID_PDA_DEBUG_MODEL); + for (int i = 0; i < breakpoints.length; i++) { + IBreakpoint breakpoint = breakpoints[i]; + if (breakpoint instanceof PDAWatchpoint && resource.equals(breakpoint.getMarker().getResource())) { + PDAWatchpoint watchpoint = (PDAWatchpoint)breakpoint; + String otherVar = watchpoint.getVariableName(); + String otherFcn = watchpoint.getFunctionName(); + if (otherVar.equals(var) && otherFcn.equals(fcn)) { + breakpoint.delete(); + return; + } + } + } + // create watchpoint + PDAWatchpoint watchpoint = new PDAWatchpoint(resource, lineNumber + 1, fcn, var, true, true); + DebugPlugin.getDefault().getBreakpointManager().addBreakpoint(watchpoint); + } + } + /* (non-Javadoc) + * @see org.eclipse.debug.ui.actions.IToggleBreakpointsTarget#canToggleWatchpoints(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection) + */ + public boolean canToggleWatchpoints(IWorkbenchPart part, ISelection selection) { + return getVariableAndFunctionName(part, selection) != null; + } + + /** + * Returns the variable and function names at the current line, or <code>null</code> if none. + * + * @param part text editor + * @param selection text selection + * @return the variable and function names at the current line, or <code>null</code> if none. + * The array has two elements, the first is the variable name, the second is the function name. + */ + private String[] getVariableAndFunctionName(IWorkbenchPart part, ISelection selection) { + ITextEditor editor = getEditor(part); + if (editor != null && selection instanceof ITextSelection) { + ITextSelection textSelection = (ITextSelection) selection; + IDocumentProvider documentProvider = editor.getDocumentProvider(); + try { + documentProvider.connect(this); + IDocument document = documentProvider.getDocument(editor.getEditorInput()); + IRegion region = document.getLineInformationOfOffset(textSelection.getOffset()); + String string = document.get(region.getOffset(), region.getLength()).trim(); + if (string.startsWith("var ")) { + String varName = string.substring(4).trim(); + String fcnName = getFunctionName(document, varName, document.getLineOfOffset(textSelection.getOffset())); + return new String[] {varName, fcnName}; + } + } catch (CoreException e) { + } catch (BadLocationException e) { + } finally { + documentProvider.disconnect(this); + } + } + return null; + } + + /** + * Returns the name of the function containing the given variable defined at the given + * line number in the specified document. + * + * @param document PDA source file + * @param varName variable name + * @param line line numbner at which the variable is defined + * @return name of function defining the variable + */ + private String getFunctionName(IDocument document, String varName, int line) { + // This is a simple guess at the function name - look for the labels preceeding + // the variable definition, and then see if there are any 'calls' to that + // label. If none, assumet the variable is in the "_main_" function + String source = document.get(); + int lineIndex = line - 1; + while (lineIndex >= 0) { + try { + IRegion information = document.getLineInformation(lineIndex); + String lineText = document.get(information.getOffset(), information.getLength()); + if (lineText.startsWith(":")) { + String label = lineText.substring(1); + if (source.indexOf("call " + label) >= 0) { + return label; + } + } + lineIndex--; + } catch (BadLocationException e) { + } + } + return "_main_"; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.actions.IToggleBreakpointsTargetExtension#toggleBreakpoints(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection) + */ + public void toggleBreakpoints(IWorkbenchPart part, ISelection selection) throws CoreException { + if (canToggleWatchpoints(part, selection)) { + toggleWatchpoints(part, selection); + } else { + toggleLineBreakpoints(part, selection); + } + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.actions.IToggleBreakpointsTargetExtension#canToggleBreakpoints(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection) + */ + public boolean canToggleBreakpoints(IWorkbenchPart part, ISelection selection) { + return canToggleLineBreakpoints(part, selection) || canToggleWatchpoints(part, selection); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/breakpoints/PDAEditorAdapterFactory.java b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/breakpoints/PDAEditorAdapterFactory.java new file mode 100644 index 00000000000..0fdfb769b78 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/breakpoints/PDAEditorAdapterFactory.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 + * Bjorn Freeman-Benson - initial API and implementation + * Wind River Systems - adopted to use with DSF + *******************************************************************************/ +package org.eclipse.cdt.examples.dsf.pda.ui.breakpoints; +import org.eclipse.cdt.examples.dsf.pda.ui.editor.PDAEditor; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.IAdapterFactory; +import org.eclipse.debug.ui.actions.IToggleBreakpointsTarget; +import org.eclipse.ui.texteditor.ITextEditor; + + + +/** + * Creates a toggle breakpoint adapter + * <p> + * This class is identical to the corresponding in PDA debugger implemented in + * org.eclipse.debug.examples.ui. + * </p> + */ +public class PDAEditorAdapterFactory implements IAdapterFactory { + + @SuppressWarnings("unchecked") // IAdapterFactory is Java 1.3 + public Object getAdapter(Object adaptableObject, Class adapterType) { + if (adaptableObject instanceof PDAEditor) { + ITextEditor editorPart = (ITextEditor) adaptableObject; + IResource resource = (IResource) editorPart.getEditorInput().getAdapter(IResource.class); + if (resource != null) { + String extension = resource.getFileExtension(); + if (extension != null && extension.equals("pda")) { + if (adapterType.equals(IToggleBreakpointsTarget.class)) { + return new PDABreakpointAdapter(); + } + } + } + } + return null; + } + + @SuppressWarnings("unchecked") // IAdapterFactory is Java 1.3 + public Class[] getAdapterList() { + return new Class[]{IToggleBreakpointsTarget.class}; + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/editor/AnnotationHover.java b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/editor/AnnotationHover.java new file mode 100644 index 00000000000..4f27cb49e94 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/editor/AnnotationHover.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 + * Bjorn Freeman-Benson - initial API and implementation + * Wind River Systems - adopted to use with DSF + *******************************************************************************/ +package org.eclipse.cdt.examples.dsf.pda.ui.editor; + +import java.util.Iterator; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.source.Annotation; +import org.eclipse.jface.text.source.IAnnotationHover; +import org.eclipse.jface.text.source.IAnnotationModel; +import org.eclipse.jface.text.source.ISourceViewer; + +/** + * Returns hover for breakpoints. + * <p> + * This class is identical to the corresponding in PDA debugger implemented in + * org.eclipse.debug.examples.ui. + * </p> + */ +public class AnnotationHover implements IAnnotationHover { + + public String getHoverInfo(ISourceViewer sourceViewer, int lineNumber) { + IAnnotationModel annotationModel = sourceViewer.getAnnotationModel(); + Iterator<?> iterator = annotationModel.getAnnotationIterator(); + while (iterator.hasNext()) { + Annotation annotation = (Annotation) iterator.next(); + Position position = annotationModel.getPosition(annotation); + try { + int lineOfAnnotation = sourceViewer.getDocument().getLineOfOffset(position.getOffset()); + if (lineNumber == lineOfAnnotation) { + return annotation.getText(); + } + } catch (BadLocationException e) { + } + } + return null; + } + +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/editor/PDAContentAssistProcessor.java b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/editor/PDAContentAssistProcessor.java new file mode 100644 index 00000000000..6db376d2c00 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/editor/PDAContentAssistProcessor.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 + * Bjorn Freeman-Benson - initial API and implementation + * Wind River Systems - adopted to use with DSF + *******************************************************************************/ +package org.eclipse.cdt.examples.dsf.pda.ui.editor; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.contentassist.CompletionProposal; +import org.eclipse.jface.text.contentassist.ICompletionProposal; +import org.eclipse.jface.text.contentassist.IContentAssistProcessor; +import org.eclipse.jface.text.contentassist.IContextInformation; +import org.eclipse.jface.text.contentassist.IContextInformationValidator; + +public class PDAContentAssistProcessor implements IContentAssistProcessor { + + public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) { + int index = offset - 1; + StringBuffer prefix = new StringBuffer(); + IDocument document = viewer.getDocument(); + while (index > 0) { + try { + char prev = document.getChar(index); + if (Character.isWhitespace(prev)) { + break; + } + prefix.insert(0, prev); + index--; + } catch (BadLocationException e) { + } + } + + List<ICompletionProposal> proposals = new ArrayList<ICompletionProposal>(); + String[] keywords = PDAScanner.fgKeywords; + if (prefix.length() > 0) { + String word = prefix.toString(); + for (int i = 0; i < keywords.length; i++) { + String keyword = keywords[i]; + if (keyword.startsWith(word) && word.length() < keyword.length()) { + proposals.add(new CompletionProposal(keyword + " ", index + 1, offset - (index + 1), keyword.length() + 1)); + } + } + } else { + // propose all keywords + for (int i = 0; i < keywords.length; i++) { + String keyword = keywords[i]; + proposals.add(new CompletionProposal(keyword + " ", offset, 0, keyword.length() + 1)); + } + } + if (!proposals.isEmpty()) { + return proposals.toArray(new ICompletionProposal[proposals.size()]); + } + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeContextInformation(org.eclipse.jface.text.ITextViewer, int) + */ + public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) { + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getCompletionProposalAutoActivationCharacters() + */ + public char[] getCompletionProposalAutoActivationCharacters() { + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getContextInformationAutoActivationCharacters() + */ + public char[] getContextInformationAutoActivationCharacters() { + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getErrorMessage() + */ + public String getErrorMessage() { + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getContextInformationValidator() + */ + public IContextInformationValidator getContextInformationValidator() { + return null; + } + +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/editor/PDAContentAssistant.java b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/editor/PDAContentAssistant.java new file mode 100644 index 00000000000..5ba2782f8fb --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/editor/PDAContentAssistant.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 + * Bjorn Freeman-Benson - initial API and implementation + * Wind River Systems - adopted to use with DSF + *******************************************************************************/ +package org.eclipse.cdt.examples.dsf.pda.ui.editor; + +import org.eclipse.jface.text.DefaultInformationControl; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IInformationControl; +import org.eclipse.jface.text.IInformationControlCreator; +import org.eclipse.jface.text.contentassist.ContentAssistant; +import org.eclipse.swt.widgets.Shell; + +public class PDAContentAssistant extends ContentAssistant { + + public PDAContentAssistant() { + super(); + + PDAContentAssistProcessor processor= new PDAContentAssistProcessor(); + setContentAssistProcessor(processor, IDocument.DEFAULT_CONTENT_TYPE); + + enableAutoActivation(false); + enableAutoInsert(false); + + setInformationControlCreator(getInformationControlCreator()); + } + + private IInformationControlCreator getInformationControlCreator() { + return new IInformationControlCreator() { + public IInformationControl createInformationControl(Shell parent) { + return new DefaultInformationControl(parent); + } + }; + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/editor/PDAEditor.java b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/editor/PDAEditor.java new file mode 100644 index 00000000000..198575f32ef --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/editor/PDAEditor.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 + * Bjorn Freeman-Benson - initial API and implementation + * Wind River Systems - adopted to use with DSF + *******************************************************************************/ +package org.eclipse.cdt.examples.dsf.pda.ui.editor; + +import java.util.ResourceBundle; + +import org.eclipse.jface.action.IAction; +import org.eclipse.ui.texteditor.AbstractDecoratedTextEditor; +import org.eclipse.ui.texteditor.ContentAssistAction; +import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds; + +/** + * PDA editor + */ +public class PDAEditor extends AbstractDecoratedTextEditor { + + /** + * Creates a PDE editor + */ + public PDAEditor() { + super(); + setSourceViewerConfiguration(new PDASourceViewerConfiguration()); + setRulerContextMenuId("pda.editor.rulerMenu"); + setEditorContextMenuId("pda.editor.editorMenu"); + } + + /* (non-Javadoc) + * @see org.eclipse.ui.texteditor.AbstractTextEditor#createActions() + */ + protected void createActions() { + super.createActions(); + ResourceBundle bundle = ResourceBundle.getBundle("org.eclipse.cdt.examples.dsf.pda.ui.editor.PDAEditorMessages"); //$NON-NLS-1$ + IAction action = new ContentAssistAction(bundle, "ContentAssistProposal.", this); //$NON-NLS-1$ + action.setActionDefinitionId(ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS); + setAction("ContentAssistProposal", action); //$NON-NLS-1$ + } + + +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/editor/PDAEditorMessages.properties b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/editor/PDAEditorMessages.properties new file mode 100644 index 00000000000..3215e8892de --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/editor/PDAEditorMessages.properties @@ -0,0 +1,15 @@ +############################################################################### +# Copyright (c) 2000, 2007 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 +############################################################################### + +ContentAssistProposal.label=Content &Assist +ContentAssistProposal.tooltip=Content Assist +ContentAssistProposal.image= +ContentAssistProposal.description=Content Assist
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/editor/PDAScanner.java b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/editor/PDAScanner.java new file mode 100644 index 00000000000..2604c65c0d6 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/editor/PDAScanner.java @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 + * Bjorn Freeman-Benson - initial API and implementation + * Wind River Systems - adopted to use with DSF + *******************************************************************************/ +package org.eclipse.cdt.examples.dsf.pda.ui.editor; + +import org.eclipse.cdt.examples.dsf.pda.ui.PDAUIPlugin; +import org.eclipse.jface.text.TextAttribute; +import org.eclipse.jface.text.rules.BufferedRuleBasedScanner; +import org.eclipse.jface.text.rules.IRule; +import org.eclipse.jface.text.rules.IWordDetector; +import org.eclipse.jface.text.rules.Token; +import org.eclipse.jface.text.rules.WordRule; + + +/** + * PDA editor keyword scanner. + */ +public class PDAScanner extends BufferedRuleBasedScanner { + + /** + * PDA keywods + */ + public static final String[] fgKeywords = new String[] { + "add", "branch_not_zero", "call", "dec", "dup", + "halt", "output", "pop", "push", "return", "var" + }; + + /** + * Detects potential keywords + */ + class PDAWordDetector implements IWordDetector { + + /* (non-Javadoc) + * @see org.eclipse.jface.text.rules.IWordDetector#isWordStart(char) + */ + public boolean isWordStart(char c) { + return Character.isLetter(c); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.rules.IWordDetector#isWordPart(char) + */ + public boolean isWordPart(char c) { + return Character.isLetter(c) || c == '_'; + } + } + + /** + * Detects PDA branch labels + */ + class PDALabelDetector extends PDAWordDetector { + + /* (non-Javadoc) + * @see org.eclipse.jface.text.rules.IWordDetector#isWordStart(char) + */ + public boolean isWordStart(char c) { + return c == ':'; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.rules.IWordDetector#isWordPart(char) + */ + public boolean isWordPart(char c) { + return super.isWordPart(c) || Character.isDigit(c); + } + } + + /** + * Constructs a scanner that identifies PDA keywords. + */ + public PDAScanner() { + // keywords + Token token = new Token(new TextAttribute(PDAUIPlugin.getDefault().getColor(PDAUIPlugin.KEYWORD))); + WordRule keywords = new WordRule(new PDAWordDetector()); + for (int i = 0; i < fgKeywords.length; i++) { + String keyword = fgKeywords[i]; + keywords.addWord(keyword, token); + } + // labels + token = new Token(new TextAttribute(PDAUIPlugin.getDefault().getColor(PDAUIPlugin.LABEL))); + WordRule labels = new WordRule(new PDALabelDetector(), token); + setRules(new IRule[]{keywords, labels}); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/editor/PDASourceViewerConfiguration.java b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/editor/PDASourceViewerConfiguration.java new file mode 100644 index 00000000000..15b6704d341 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/editor/PDASourceViewerConfiguration.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 + * Bjorn Freeman-Benson - initial API and implementation + * Wind River Systems - adopted to use with DSF + *******************************************************************************/ +package org.eclipse.cdt.examples.dsf.pda.ui.editor; + +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.ITextHover; +import org.eclipse.jface.text.contentassist.IContentAssistant; +import org.eclipse.jface.text.presentation.IPresentationReconciler; +import org.eclipse.jface.text.presentation.PresentationReconciler; +import org.eclipse.jface.text.rules.DefaultDamagerRepairer; +import org.eclipse.jface.text.source.IAnnotationHover; +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.ui.editors.text.TextSourceViewerConfiguration; + +/** + * Source view configuration for the PDA editor + */ +public class PDASourceViewerConfiguration extends TextSourceViewerConfiguration { + + /* (non-Javadoc) + * @see org.eclipse.jface.text.source.SourceViewerConfiguration#getTextHover(org.eclipse.jface.text.source.ISourceViewer, java.lang.String) + */ + public ITextHover getTextHover(ISourceViewer sourceViewer, String contentType) { + return new TextHover(); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.source.SourceViewerConfiguration#getAnnotationHover(org.eclipse.jface.text.source.ISourceViewer) + */ + public IAnnotationHover getAnnotationHover(ISourceViewer sourceViewer) { + return new AnnotationHover(); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.source.SourceViewerConfiguration#getPresentationReconciler(org.eclipse.jface.text.source.ISourceViewer) + */ + public IPresentationReconciler getPresentationReconciler(ISourceViewer sourceViewer) { + PresentationReconciler reconciler = new PresentationReconciler(); + reconciler.setDocumentPartitioning(getConfiguredDocumentPartitioning(sourceViewer)); + DefaultDamagerRepairer dr = new DefaultDamagerRepairer(new PDAScanner()); + reconciler.setDamager(dr, IDocument.DEFAULT_CONTENT_TYPE); + reconciler.setRepairer(dr, IDocument.DEFAULT_CONTENT_TYPE); + return reconciler; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.source.SourceViewerConfiguration#getContentAssistant(org.eclipse.jface.text.source.ISourceViewer) + */ + public IContentAssistant getContentAssistant(ISourceViewer sourceViewer) { + return new PDAContentAssistant(); + } + + +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/editor/PopFrameActionDelegate.java b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/editor/PopFrameActionDelegate.java new file mode 100644 index 00000000000..e3a6212fa80 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/editor/PopFrameActionDelegate.java @@ -0,0 +1,90 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 + * Bjorn Freeman-Benson - initial API and implementation + * Wind River Systems - adopted to use with DSF + *******************************************************************************/ +package org.eclipse.cdt.examples.dsf.pda.ui.editor; + +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.swt.widgets.Event; +import org.eclipse.ui.IActionDelegate2; +import org.eclipse.ui.IObjectActionDelegate; +import org.eclipse.ui.IWorkbenchPart; + + +public class PopFrameActionDelegate implements IObjectActionDelegate, IActionDelegate2 { + + //private PDAThread fThread = null; + + /* (non-Javadoc) + * @see org.eclipse.ui.IObjectActionDelegate#setActivePart(org.eclipse.jface.action.IAction, org.eclipse.ui.IWorkbenchPart) + */ + public void setActivePart(IAction action, IWorkbenchPart targetPart) { + } + + /* (non-Javadoc) + * @see org.eclipse.ui.IActionDelegate#run(org.eclipse.jface.action.IAction) + */ + public void run(IAction action) { + /* + try { + fThread.pop(); + } catch (DebugException e) { + }*/ + } + + /* (non-Javadoc) + * @see org.eclipse.ui.IActionDelegate#selectionChanged(org.eclipse.jface.action.IAction, org.eclipse.jface.viewers.ISelection) + */ + public void selectionChanged(IAction action, ISelection selection) { + /* if (selection instanceof IStructuredSelection) { + IStructuredSelection ss = (IStructuredSelection) selection; + Object element = ss.getFirstElement(); + if (element instanceof PDAStackFrame) { + PDAStackFrame frame = (PDAStackFrame) element; + //#ifdef ex5 +//# // TODO: Exercise 5 - enable the action if the frame's thread supports it + //#else + fThread = (PDAThread) frame.getThread(); + try { + action.setEnabled(fThread.canPop() && fThread.getTopStackFrame().equals(frame)); + } catch (DebugException e) { + } + return; + //#endif + } + + } + action.setEnabled(false); + */ + } + + /* (non-Javadoc) + * @see org.eclipse.ui.IActionDelegate2#init(org.eclipse.jface.action.IAction) + */ + public void init(IAction action) { + } + + /* (non-Javadoc) + * @see org.eclipse.ui.IActionDelegate2#dispose() + */ + public void dispose() { + //fThread = null; + } + + /* (non-Javadoc) + * @see org.eclipse.ui.IActionDelegate2#runWithEvent(org.eclipse.jface.action.IAction, org.eclipse.swt.widgets.Event) + */ + public void runWithEvent(IAction action, Event event) { + run(action); + } + +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/editor/TextHover.java b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/editor/TextHover.java new file mode 100644 index 00000000000..0ca40e87d90 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/editor/TextHover.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 + * Bjorn Freeman-Benson - initial API and implementation + * Wind River Systems - adopted to use with DSF + *******************************************************************************/ +package org.eclipse.cdt.examples.dsf.pda.ui.editor; + +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITextHover; +import org.eclipse.jface.text.ITextViewer; + + +/** + * Produces debug hover for the PDA debugger. + */ +public class TextHover implements ITextHover { + + /* (non-Javadoc) + * @see org.eclipse.jface.text.ITextHover#getHoverInfo(org.eclipse.jface.text.ITextViewer, org.eclipse.jface.text.IRegion) + */ + public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) { + /*String varName = null; + try { + varName = textViewer.getDocument().get(hoverRegion.getOffset(), hoverRegion.getLength()); + } catch (BadLocationException e) { + return null; + } + if (varName.startsWith("$") && varName.length() > 1) { + varName = varName.substring(1); + } + + PDAStackFrame frame = null; + IAdaptable debugContext = DebugUITools.getDebugContext(); + if (debugContext instanceof PDAStackFrame) { + frame = (PDAStackFrame) debugContext; + } else if (debugContext instanceof PDAThread) { + PDAThread thread = (PDAThread) debugContext; + try { + frame = (PDAStackFrame) thread.getTopStackFrame(); + } catch (DebugException e) { + return null; + } + } else if (debugContext instanceof PDADebugTarget) { + PDADebugTarget target = (PDADebugTarget) debugContext; + try { + IThread[] threads = target.getThreads(); + if (threads.length > 0) { + frame = (PDAStackFrame) threads[0].getTopStackFrame(); + } + } catch (DebugException e) { + return null; + } + } + if (frame != null) { + try { + IVariable[] variables = frame.getVariables(); + for (int i = 0; i < variables.length; i++) { + IVariable variable = variables[i]; + if (variable.getName().equals(varName)) { + return varName + " = " + variable.getValue().getValueString(); + } + } + } catch (DebugException e) { + } + }*/ + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.ITextHover#getHoverRegion(org.eclipse.jface.text.ITextViewer, int) + */ + public IRegion getHoverRegion(ITextViewer textViewer, int offset) { + return WordFinder.findWord(textViewer.getDocument(), offset); + } + +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/editor/WordFinder.java b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/editor/WordFinder.java new file mode 100644 index 00000000000..e67e1adfaaa --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/editor/WordFinder.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 + * Bjorn Freeman-Benson - initial API and implementation + * Wind River Systems - adopted to use with DSF + *******************************************************************************/ +package org.eclipse.cdt.examples.dsf.pda.ui.editor; + + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.Region; + +/** + * Looks for an identifier in a source file + */ +public class WordFinder { + + /** + * Returns the region in the given document that contains an identifier, or + * <code>null</code> if none. + * + * @param document document to search + * @param offset offset at which to look for an identifier + * @return region containing an identifier, or <code>null</code> + */ + public static IRegion findWord(IDocument document, int offset) { + + int start= -1; + int end= -1; + + + try { + + int pos= offset; + char c; + + while (pos >= 0) { + c= document.getChar(pos); + if (!Character.isJavaIdentifierPart(c)) + break; + --pos; + } + + start= pos; + + pos= offset; + int length= document.getLength(); + + while (pos < length) { + c= document.getChar(pos); + if (!Character.isJavaIdentifierPart(c)) + break; + ++pos; + } + + end= pos; + + } catch (BadLocationException x) { + } + + if (start > -1 && end > -1) { + if (start == offset && end == offset) + return new Region(offset, 0); + else if (start == offset) + return new Region(start, end - start); + else + return new Region(start + 1, end - start - 1); + } + + return null; + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/launcher/PDAMainTab.java b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/launcher/PDAMainTab.java new file mode 100644 index 00000000000..6f4ca94aa4d --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/launcher/PDAMainTab.java @@ -0,0 +1,190 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 + * Bjorn Freeman-Benson - initial API and implementation + * Wind River Systems - adopted to use with DSF + *******************************************************************************/ +package org.eclipse.cdt.examples.dsf.pda.ui.launcher; + + +import org.eclipse.cdt.examples.dsf.pda.PDAPlugin; +import org.eclipse.cdt.examples.dsf.pda.ui.PDAUIPlugin; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; +import org.eclipse.debug.ui.AbstractLaunchConfigurationTab; +import org.eclipse.jface.window.Window; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.dialogs.ResourceListSelectionDialog; + + +/** + * Tab to specify the PDA program to run/debug. + * <p> + * This class is identical to the corresponding in PDA debugger implemented in + * org.eclipse.debug.examples.ui. + * </p> + */ +public class PDAMainTab extends AbstractLaunchConfigurationTab { + + private Text fProgramText; + private Button fProgramButton; + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#createControl(org.eclipse.swt.widgets.Composite) + */ + public void createControl(Composite parent) { + Font font = parent.getFont(); + + Composite comp = new Composite(parent, SWT.NONE); + setControl(comp); + GridLayout topLayout = new GridLayout(); + topLayout.verticalSpacing = 0; + topLayout.numColumns = 3; + comp.setLayout(topLayout); + comp.setFont(font); + + createVerticalSpacer(comp, 3); + + Label programLabel = new Label(comp, SWT.NONE); + programLabel.setText("&Program:"); + GridData gd = new GridData(GridData.BEGINNING); + programLabel.setLayoutData(gd); + programLabel.setFont(font); + + fProgramText = new Text(comp, SWT.SINGLE | SWT.BORDER); + gd = new GridData(GridData.FILL_HORIZONTAL); + fProgramText.setLayoutData(gd); + fProgramText.setFont(font); + fProgramText.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + updateLaunchConfigurationDialog(); + } + }); + + fProgramButton = createPushButton(comp, "&Browse...", null); //$NON-NLS-1$ + fProgramButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + browsePDAFiles(); + } + }); + } + + /** + * Open a resource chooser to select a PDA program + */ + protected void browsePDAFiles() { + ResourceListSelectionDialog dialog = new ResourceListSelectionDialog(getShell(), ResourcesPlugin.getWorkspace().getRoot(), IResource.FILE); + dialog.setTitle("PDA Program"); + dialog.setMessage("Select PDA Program"); + if (dialog.open() == Window.OK) { + Object[] files = dialog.getResult(); + IFile file = (IFile) files[0]; + fProgramText.setText(file.getFullPath().toString()); + } + + } + /* (non-Javadoc) + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#setDefaults(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy) + */ + public void setDefaults(ILaunchConfigurationWorkingCopy configuration) { + } + /* (non-Javadoc) + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#initializeFrom(org.eclipse.debug.core.ILaunchConfiguration) + */ + public void initializeFrom(ILaunchConfiguration configuration) { + //#ifdef ex1 +//# // TODO: Exercise 1 - retrieve the program path attribute from the launch configuration +//# String program = null; +//# if (program != null) { +//# fProgramText.setText(program); +//# } + //#else + try { + String program = null; + program = configuration.getAttribute(PDAPlugin.ATTR_PDA_PROGRAM, (String)null); + if (program != null) { + fProgramText.setText(program); + } + } catch (CoreException e) { + setErrorMessage(e.getMessage()); + } + //#endif + } + /* (non-Javadoc) + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#performApply(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy) + */ + public void performApply(ILaunchConfigurationWorkingCopy configuration) { + String program = fProgramText.getText().trim(); + if (program.length() == 0) { + program = null; + } + //#ifdef ex1 +//# // TODO: Exercise 1 - update the launch configuration with the path to +//# // currently specified program + //#else + configuration.setAttribute(PDAPlugin.ATTR_PDA_PROGRAM, program); + //#endif + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#getName() + */ + public String getName() { + return "Main"; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#isValid(org.eclipse.debug.core.ILaunchConfiguration) + */ + public boolean isValid(ILaunchConfiguration launchConfig) { + setErrorMessage(null); + setMessage(null); + String text = fProgramText.getText(); + //#ifdef ex1 +//# // TODO: Exercise 1 - validate the currently specified program exists and is not +//# // empty, providing the user with feedback. + //#else + if (text.length() > 0) { + IPath path = new Path(text); + if (ResourcesPlugin.getWorkspace().getRoot().findMember(path) == null) { + setErrorMessage("Specified program does not exist"); + return false; + } + } else { + setMessage("Specify a program"); + } + //#endif + return true; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#getImage() + */ + public Image getImage() { + return PDAUIPlugin.getDefault().getImageRegistry().get(PDAUIPlugin.IMG_OBJ_PDA); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/launcher/PDATabGroup.java b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/launcher/PDATabGroup.java new file mode 100644 index 00000000000..7c9ed2fb073 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/launcher/PDATabGroup.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 + * Bjorn Freeman-Benson - initial API and implementation + * Wind River Systems - adopted to use with DSF + *******************************************************************************/ +package org.eclipse.cdt.examples.dsf.pda.ui.launcher; + +import org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup; +import org.eclipse.debug.ui.CommonTab; +import org.eclipse.debug.ui.ILaunchConfigurationDialog; +import org.eclipse.debug.ui.ILaunchConfigurationTab; +import org.eclipse.debug.ui.sourcelookup.SourceLookupTab; + +/** + * Tab group for a PDA application + * <p> + * This class is identical to the corresponding in PDA debugger implemented in + * org.eclipse.debug.examples.ui. + * </p> + */ +public class PDATabGroup extends AbstractLaunchConfigurationTabGroup { + /* (non-Javadoc) + * @see org.eclipse.debug.ui.ILaunchConfigurationTabGroup#createTabs(org.eclipse.debug.ui.ILaunchConfigurationDialog, java.lang.String) + */ + public void createTabs(ILaunchConfigurationDialog dialog, String mode) { + //#ifdef ex1 +//# // TODO: Exercise 1 - add the PDA main tab, source lookup tab and common +//# // tab to the tab group + //#else + setTabs(new ILaunchConfigurationTab[] { + new PDAMainTab(), + new SourceLookupTab(), + new CommonTab() + }); + //#endif + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/viewmodel/PDAVMAdapter.java b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/viewmodel/PDAVMAdapter.java new file mode 100644 index 00000000000..2e1761f769c --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/viewmodel/PDAVMAdapter.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * 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.examples.dsf.pda.ui.viewmodel; + +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.AbstractDebugVMAdapter; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.SteppingController; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.expression.ExpressionVMProvider; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.register.RegisterVMProvider; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.variable.VariableVMProvider; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider; +import org.eclipse.cdt.examples.dsf.pda.ui.viewmodel.launch.PDALaunchVMProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentationFactory; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.ui.IDebugUIConstants; + +@ThreadSafe +@SuppressWarnings("restriction") +public class PDAVMAdapter extends AbstractDebugVMAdapter +{ + public PDAVMAdapter(DsfSession session, SteppingController controller) { + super(session, controller); + getSession().registerModelAdapter(IColumnPresentationFactory.class, this); + } + + @Override + public void dispose() { + getSession().unregisterModelAdapter(IColumnPresentationFactory.class); + super.dispose(); + } + + @Override + protected AbstractDMVMProvider createViewModelProvider(IPresentationContext context) { + if ( IDebugUIConstants.ID_DEBUG_VIEW.equals(context.getId()) ) { + return new PDALaunchVMProvider(this, context, getSession()); + } else if (IDebugUIConstants.ID_VARIABLE_VIEW.equals(context.getId()) ) { + return new VariableVMProvider(this, context, getSession()); + } else if (IDebugUIConstants.ID_EXPRESSION_VIEW.equals(context.getId()) ) { + return new ExpressionVMProvider(this, context, getSession()); + } else if (IDebugUIConstants.ID_REGISTER_VIEW.equals(context.getId()) ) { + return new RegisterVMProvider(this, context, getSession()); + } + return null; + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/viewmodel/launch/PDALaunchVMProvider.java b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/viewmodel/launch/PDALaunchVMProvider.java new file mode 100644 index 00000000000..b00b63e8946 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/viewmodel/launch/PDALaunchVMProvider.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 + * Ericsson - Modified for new functionality + *******************************************************************************/ +package org.eclipse.cdt.examples.dsf.pda.ui.viewmodel.launch; + +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.launch.AbstractLaunchVMProvider; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.launch.LaunchRootVMNode; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.launch.StackFramesVMNode; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.launch.StandardProcessVMNode; +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.debug.core.DebugPlugin; +import org.eclipse.debug.core.IDebugEventSetListener; +import org.eclipse.debug.core.ILaunchesListener2; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; + + +/** + * View Model provider for the Launch (AKA Debug) view. The launch VM + * provider is configured with three nodes: + * <ul> + * <li> LaunchRootVMNode - This is the root of the PDA view model.</li> + * <li> PDAVirtualMachineVMNode - Supplies the element representing PDA VM</li> + * <li> PDAThreadsVMNode - Supplies the PDA thread elements</li> + * <li> StackFramesVMNode - Supplies the stack frame elements.</li> + * <li> StandardProcessVMNode - Supplies elements representing the PDA + * debugger process.</li> + * </ul> + */ +@SuppressWarnings("restriction") +public class PDALaunchVMProvider extends AbstractLaunchVMProvider + implements IDebugEventSetListener, ILaunchesListener2 +{ + @ThreadSafe + public PDALaunchVMProvider(AbstractVMAdapter adapter, IPresentationContext presentationContext, DsfSession session) + { + super(adapter, presentationContext, session); + + IRootVMNode launchNode = new LaunchRootVMNode(this); + setRootNode(launchNode); + + // Launch node is a parent to the processes and program nodes. + IVMNode pdaVirtualMachineNode = new PDAVirtualMachineVMNode(this, getSession()); + IVMNode processesNode = new StandardProcessVMNode(this); + addChildNodes(launchNode, new IVMNode[] { pdaVirtualMachineNode, processesNode}); + + // Virtual machine node is under the PDA threads node. + IVMNode threadsNode = new PDAThreadsVMNode(this, getSession()); + addChildNodes(pdaVirtualMachineNode, new IVMNode[] { threadsNode }); + + // Stack frames node is under the PDA threads node. + IVMNode stackFramesNode = new StackFramesVMNode(this, getSession()); + addChildNodes(threadsNode, new IVMNode[] { stackFramesNode }); + + // Register the LaunchVM provider as a listener to debug and launch + // events. These events are used by the launch and processes nodes. + DebugPlugin.getDefault().addDebugEventListener(this); + DebugPlugin.getDefault().getLaunchManager().addLaunchListener(this); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/viewmodel/launch/PDAThreadsVMNode.java b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/viewmodel/launch/PDAThreadsVMNode.java new file mode 100644 index 00000000000..043f4bc9f08 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/viewmodel/launch/PDAThreadsVMNode.java @@ -0,0 +1,163 @@ +/******************************************************************************* + * 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.examples.dsf.pda.ui.viewmodel.launch; + +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMData; +import org.eclipse.cdt.dsf.debug.service.IRunControl.StateChangeReason; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.launch.AbstractThreadVMNode; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.ui.concurrent.ViewerDataRequestMonitor; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; +import org.eclipse.cdt.examples.dsf.pda.service.PDARunControl; +import org.eclipse.cdt.examples.dsf.pda.service.PDAThreadDMContext; +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.ILabelUpdate; +import org.eclipse.debug.ui.DebugUITools; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.ui.IMemento; + + +/** + * View model node supplying the PDA thread elements. It extends + * the base threads node and adds label and memento generation. + */ +@SuppressWarnings("restriction") +public class PDAThreadsVMNode extends AbstractThreadVMNode + implements IElementLabelProvider, IElementMementoProvider +{ + public PDAThreadsVMNode(AbstractDMVMProvider provider, DsfSession session) { + super(provider, session); + } + + @Override + public String toString() { + return "PDAThreadVMNode(" + getSession().getId() + ")"; + } + + @Override + protected void updateLabelInSessionThread(ILabelUpdate[] updates) { + for (final ILabelUpdate update : updates) { + final PDARunControl runControl = getServicesTracker().getService(PDARunControl.class); + if ( runControl == null ) { + handleFailedUpdate(update); + continue; + } + + final PDAThreadDMContext dmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), PDAThreadDMContext.class); + + String imageKey = null; + if (getServicesTracker().getService(IRunControl.class).isSuspended(dmc)) { + imageKey = IDebugUIConstants.IMG_OBJS_THREAD_SUSPENDED; + } else { + imageKey = IDebugUIConstants.IMG_OBJS_THREAD_RUNNING; + } + update.setImageDescriptor(DebugUITools.getImageDescriptor(imageKey), 0); + + // Find the Reason for the State + getDMVMProvider().getModelData( + this, update, runControl, dmc, + new ViewerDataRequestMonitor<IExecutionDMData>(getSession().getExecutor(), update) { + @Override + public void handleCompleted(){ + if (!isSuccess()) { + update.setLabel("<unavailable>", 0); + update.done(); + return; + } + + // We're in a new dispatch cycle, and we have to check whether the + // service reference is still valid. + final PDARunControl runControl = getServicesTracker().getService(PDARunControl.class); + if ( runControl == null ) { + handleFailedUpdate(update); + return; + } + + final StateChangeReason reason = getData().getStateChangeReason(); + + // Create Labels of type Thread[GDBthreadId]RealThreadID/Name (State: Reason) + // Thread[1] 3457 (Suspended:BREAKPOINT) + final StringBuilder builder = new StringBuilder(); + builder.append("Thread "); + builder.append(dmc.getID()); + if(getServicesTracker().getService(IRunControl.class).isSuspended(dmc)) + builder.append(" (Suspended"); + else + builder.append(" (Running"); + // Reason will be null before ContainerSuspendEvent is fired + if(reason != null) { + builder.append(" : "); + builder.append(reason); + } + builder.append(")"); + update.setLabel(builder.toString(), 0); + update.done(); + } + }, + getExecutor()); + + } + } + + private String produceThreadElementName(String viewName, PDAThreadDMContext execCtx) { + return "Thread." + execCtx.getID(); + } + + private static final String MEMENTO_NAME = "THREAD_MEMENTO_NAME"; + + /* + * @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(MEMENTO_NAME); + if (mementoName != null) { + if (element instanceof IDMVMContext) { + IDMContext dmc = ((IDMVMContext)element).getDMContext(); + if ( dmc instanceof PDAThreadDMContext) { + String elementName = produceThreadElementName( + request.getPresentationContext().getId(), (PDAThreadDMContext) dmc ); + request.setEqual( elementName.equals( mementoName ) ); + } + } + } + request.done(); + } + } + + /* + * @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 PDAThreadDMContext) { + String elementName = produceThreadElementName( request.getPresentationContext().getId(), (PDAThreadDMContext) dmc ); + memento.putString(MEMENTO_NAME, elementName); + } + } + request.done(); + } + } + +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/viewmodel/launch/PDAVirtualMachineVMNode.java b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/viewmodel/launch/PDAVirtualMachineVMNode.java new file mode 100644 index 00000000000..62b57922fa3 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda.ui/src/org/eclipse/cdt/examples/dsf/pda/ui/viewmodel/launch/PDAVirtualMachineVMNode.java @@ -0,0 +1,187 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Ericsson 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 - Initial API and implementation + * Wind River Systems - Factored out AbstractContainerVMNode + *******************************************************************************/ + +package org.eclipse.cdt.examples.dsf.pda.ui.viewmodel.launch; + + +import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMData; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.launch.AbstractContainerVMNode; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.ui.concurrent.ViewerDataRequestMonitor; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; +import org.eclipse.cdt.examples.dsf.pda.service.PDACommandControl; +import org.eclipse.cdt.examples.dsf.pda.service.PDAVirtualMachineDMContext; +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.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.ui.DebugUITools; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.ui.IMemento; + + +/** + * View Model node representing a PDA virtual machine. It extends + * the base container node and adds label and memento generation. + */ +@SuppressWarnings("restriction") +public class PDAVirtualMachineVMNode extends AbstractContainerVMNode + implements IElementMementoProvider +{ + public PDAVirtualMachineVMNode(AbstractDMVMProvider provider, DsfSession session) { + super(provider, session); + } + + @Override + public String toString() { + return "PDAContainerVMNode(" + getSession().getId() + ")"; + } + + + @Override + protected void updateElementsInSessionThread(IChildrenUpdate update) { + // Get the instance of the service. Note that there is no race condition + // in getting the service since this method is called only in the + // service executor thread. + final PDACommandControl commandControl = getServicesTracker().getService(PDACommandControl.class); + + // Check if the service is available. If it is not, no elements are + // updated. + if (commandControl == null) { + handleFailedUpdate(update); + return; + } + + update.setChild(createVMContext(commandControl.getContext()), 0); + update.done(); + } + + + @Override + protected void updateLabelInSessionThread(final ILabelUpdate update) { + // Get a reference to the run control service. + final IRunControl runControl = getServicesTracker().getService(IRunControl.class); + if (runControl == null) { + handleFailedUpdate(update); + return; + } + + // Find the PDA program context. + final PDAVirtualMachineDMContext programCtx = + findDmcInPath(update.getViewerInput(), update.getElementPath(), PDAVirtualMachineDMContext.class); + + // Call service to get current program state + final boolean isSuspended = runControl.isSuspended(programCtx); + + // Set the program icon based on the running state of the program. + String imageKey = null; + if (isSuspended) { + imageKey = IDebugUIConstants.IMG_OBJS_THREAD_SUSPENDED; + } else { + imageKey = IDebugUIConstants.IMG_OBJS_THREAD_RUNNING; + } + update.setImageDescriptor(DebugUITools.getImageDescriptor(imageKey), 0); + + // Retrieve the last state change reason + getDMVMProvider().getModelData( + this, update, runControl, programCtx, + new ViewerDataRequestMonitor<IExecutionDMData>(ImmediateExecutor.getInstance(), update) + { + @Override + public void handleCompleted(){ + // If the request failed, fail the udpate. + if (!isSuccess()) { + handleFailedUpdate(update); + return; + } + + // Compose the thread name string. + final StringBuilder builder = new StringBuilder(); + + builder.append("PDA ["); + builder.append(programCtx.getProgram()); + builder.append("]"); + + if(isSuspended) { + builder.append(" (Suspended"); + } else { + builder.append(" (Running"); + } + // Reason will be null before ContainerSuspendEvent is fired + if(getData().getStateChangeReason() != null) { + builder.append(" : "); + builder.append(getData().getStateChangeReason()); + } + builder.append(")"); + update.setLabel(builder.toString(), 0); + update.done(); + } + }, + getExecutor()); + } + + private String produceProgramElementName( String viewName , PDAVirtualMachineDMContext execCtx ) { + return "PDA." + execCtx.getProgram(); //$NON-NLS-1$ + } + + private final String MEMENTO_NAME = "PDAPROGRAM_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); + + if (mementoName != null) { + if (element instanceof IDMVMContext) { + + IDMContext dmc = ((IDMVMContext)element).getDMContext(); + + if ( dmc instanceof PDAVirtualMachineDMContext) { + + String elementName = produceProgramElementName( request.getPresentationContext().getId(), (PDAVirtualMachineDMContext) dmc ); + request.setEqual( elementName.equals( mementoName ) ); + } + } + } + request.done(); + } + } + + 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 PDAVirtualMachineDMContext) { + + String elementName = produceProgramElementName( request.getPresentationContext().getId(), (PDAVirtualMachineDMContext) dmc ); + memento.putString(MEMENTO_NAME, elementName); + } + } + request.done(); + } + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/.classpath b/dsf/org.eclipse.cdt.examples.dsf.pda/.classpath new file mode 100644 index 00000000000..8fe3727bbf5 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/.classpath @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src"/> + <classpathentry kind="src" path="pdavm/src"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/> + <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/.cvsignore b/dsf/org.eclipse.cdt.examples.dsf.pda/.cvsignore new file mode 100644 index 00000000000..ba077a4031a --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/.cvsignore @@ -0,0 +1 @@ +bin diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/.options b/dsf/org.eclipse.cdt.examples.dsf.pda/.options new file mode 100755 index 00000000000..f796395f8e0 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/.options @@ -0,0 +1 @@ +org.eclipse.cdt.examples.dsf.pda/debug = false diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/.project b/dsf/org.eclipse.cdt.examples.dsf.pda/.project new file mode 100644 index 00000000000..4546d07eba3 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/.project @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>org.eclipse.cdt.examples.dsf.pda</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.ManifestBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.SchemaBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.pde.PluginNature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/.settings/org.eclipse.jdt.core.prefs b/dsf/org.eclipse.cdt.examples.dsf.pda/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..cfb0bdd17eb --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,70 @@ +#Tue Jun 24 11:03:29 PDT 2008 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.5 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.autoboxing=ignore +org.eclipse.jdt.core.compiler.problem.deprecation=warning +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning +org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=warning +org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning +org.eclipse.jdt.core.compiler.problem.forbiddenReference=error +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore +org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore +org.eclipse.jdt.core.compiler.problem.nullReference=error +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning +org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled +org.eclipse.jdt.core.compiler.problem.unusedImport=error +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=warning +org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.source=1.5 diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/META-INF/MANIFEST.MF b/dsf/org.eclipse.cdt.examples.dsf.pda/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..076c5b661d1 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/META-INF/MANIFEST.MF @@ -0,0 +1,21 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: PDA Example Device Debugging Core Plug-in +Bundle-SymbolicName: org.eclipse.cdt.examples.dsf.pda;singleton:=true +Bundle-Version: 2.0.0.qualifier +Bundle-Activator: org.eclipse.cdt.examples.dsf.pda.PDAPlugin +Require-Bundle: org.eclipse.core.runtime, + org.eclipse.core.resources, + org.eclipse.core.variables, + org.eclipse.debug.core, + org.eclipse.cdt.dsf, + org.junit4;bundle-version="4.3.1", + org.eclipse.cdt.core;bundle-version="5.0.0" +Bundle-ActivationPolicy: lazy +Export-Package: org.eclipse.cdt.examples.dsf.pda, + org.eclipse.cdt.examples.dsf.pda.breakpoints, + org.eclipse.cdt.examples.dsf.pda.launch, + org.eclipse.cdt.examples.dsf.pda.service, + org.eclipse.cdt.examples.dsf.pda.service.commands, + org.eclipse.cdt.examples.dsf.pda.sourcelookup +Bundle-RequiredExecutionEnvironment: J2SE-1.5 diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/about.html b/dsf/org.eclipse.cdt.examples.dsf.pda/about.html new file mode 100644 index 00000000000..c1cb2e12f40 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/about.html @@ -0,0 +1,28 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/> +<title>About</title> +</head> +<body lang="EN-US"> +<h2>About This Content</h2> + +<p>May 14, 2008</p> +<h3>License</h3> + +<p>The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>. +For purposes of the EPL, "Program" will mean the Content.</p> + +<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at <a href="http://www.eclipse.org">http://www.eclipse.org</a>.</p> + +</body> +</html>
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/build.properties b/dsf/org.eclipse.cdt.examples.dsf.pda/build.properties new file mode 100644 index 00000000000..04af87485ac --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/build.properties @@ -0,0 +1,22 @@ +############################################################################### +# Copyright (c) 2005, 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 - adapted to use with DSF +############################################################################### +source.. = src/ +output.. = bin/ +bin.includes = plugin.xml,\ + pdavm/,\ + META-INF/,\ + about.html,\ + .,\ + readme.html,\ + samples/ +src.includes = about.html,\ + src/ diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/pdavm/docs/protocol.html b/dsf/org.eclipse.cdt.examples.dsf.pda/pdavm/docs/protocol.html new file mode 100644 index 00000000000..1d79f31a4fc --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/pdavm/docs/protocol.html @@ -0,0 +1,308 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html> +<head> + <meta content="text/html; charset=ISO-8859-1" + http-equiv="content-type"> + <title>PDA Debugger Protocol Reference</title> +</head> +<body> + +<h2>PDA Debugger Protocol Reference</h2> + +<h3>clear</h3> +Clears any breakpoint set on given line +<pre> + C: clear {line} + R: ok +</pre> + + +<h3>data</h3> +Retrieves data stack information +<pre> + C: data {thread_id} + R: {value 1}|{value 2}|{value 3}|...| + +Errors: + error: invalid thread +</pre> + + +<h3>drop</h3> +Returns from the current frame without executing the rest of instructions. + +<pre> +If VM running: + C: drop {thread_id} + R: ok + E: resumed {thread_id} drop + E: suspended {thread_id} drop + +If VM suspended: + C: drop {thread_id} + R: ok + E: vmresumed drop + E: vmsuspended {thread_id} drop + +Errors: + error: invalid thread +</pre> + + +<h3>eval</h3> +Sets what events cause the execution to stop. + +<pre> + C: eval {thread_id} {instruction}%20{parameter}|{instruction}%20{parameter}|... + R: ok + E: resumed {thread_id} client + E: evalresult result + E: suspended {thread_id} eval + +Errors: + error: invalid thread + error: cannot evaluate while vm is suspended + error: thread running +</pre> + + +<h3>eventstop</h3> +Sets what events cause the execution to stop. + +<pre> + C: eventstop {event_name} {0|1} + R: ok + ... + E: suspended event {event_name} +</pre> + + +<h3>exit</h3> +Instructs the debugger to exit. + +<pre> + C: exit + R: ok +</pre> + + +<h3>popdata</h3> +Pops the top value from the data stack + +<pre> + C: popdata {thread_id} + R: ok + +Errors: + error: invalid thread +</pre> + + +<h3>pushdata</h3> +Pushes the given value on top of the data stack. + +<pre> + C: pushdata {thread_id} {value} + R: ok + +Errors: + error: invalid thread +</pre> + + +<h3>resume</h3> +Resumes the execution of a single thread. Can be issued only if the virtual +machine is running. + +<pre> + C: resume {thread_id} + R: ok + E: resumed {thread_id} client + +Errors: + error: invalid thread + error: cannot resume thread when vm is suspended + error: thread already running +</pre> + + +<h3>set</h3> +Sets a breakpoint at given line + +<pre> +Suspend a single thread: + C: set {line_number} 0 + R: ok + C: resume {thread_id} + E: resumed {thread_id} client + E: suspended {thread_id} breakpoint line_number + +Suspend the VM: + C: set {line_number} 1 + R: ok + C: vmresume + E: vmresumed client + E: vmsuspended {thread_id} breakpoint line_number +</pre> + + +<h3>setdata</h3> +Sets a data value in the data stack at the given location + +<pre> + C: setdata {thread_id} {index} {value} + R: ok + +Errors: + error: invalid thread +</pre> + + +<h3>setvar</h3> +Sets a variable value + +<pre> + C: setvar {thread_id} {frame_number} {variable} {value} + R: ok + +Errors: + error: invalid thread +</pre> + + +<h3>stack</h3> +Retrieves command stack information + +<pre> + C: stack {thread_id} + R: {file}|{line}|{function}|{var_1}|{var_2}|...#{file}|{line}|{function}|{var_1}|{var_2}|...#... + +Errors: + error: invalid thread +</pre> + + +<h3>step</h3> +Executes next instruction + +<pre> +If VM running: + C: step {thread_id} + R: ok + E: resumed {thread_id} client + E: suspended {thread_id} step + +If VM suspended: + C: step {thread_id} + R: ok + E: vmresumed client + E: vmsuspended {thread_id} step + +Errors: + error: invalid thread +</pre> + + +<h3>stepreturn</h3> +Executes instructions until the current subroutine is finished + +<pre> +If VM running: + C: stepreturn {thread_id} + R: ok + E: resumed {thread_id} client + E: suspended {thread_id} step + +If VM suspended: + C: stepreturn {thread_id} + R: ok + E: vmresumed client + E: vmsuspended {thread_id} step + +Errors: + error: invalid thread +</pre> + + +<h3>suspend</h3> +Suspends execution of a single thread. Can be issued only if the virtual +machine is running. + +<pre> + C: suspend {thread_id} + R: ok + E: suspended {thread_id} client + +Errors: + error: invalid thread + error: vm already suspended + error: thread already suspended +</pre> + +<h3>threads</h3> +Retrieves the list of active threads + +<pre> + C: threads + R: {thread id} {thread id} ... +</pre> + +<h3>var</h3> +Retrieves variable value + +<pre> + C: var {thread_id} {frame_number} {variable_name} + R: {variable_value} + +Errors: + error: invalid thread + error: variable undefined +</pre> + + +<h3>watch</h3> +Sets a watchpoint on a given variable + +<pre> + C: watch {function}::{variable_name} {watch_operation} + R: ok + C: resume + R: resumed client + E: suspended watch {watch_operation} {function}::{variable_name} +</pre> + +The <code>watch_operation<code> value can be: +<ul> + <li>0 - no watch</li> + <li>1 - read watch</li> + <li>2 - write watch</li> + <li>3 - both, etc.</li> +</ul> + + +<h3>vmresume</h3> +Resumes the execution of the whole virtual machine + +<pre> + C: vmresume + R: ok + E: vmresumed client + +Errors: + error: vm already running +</pre> + +<h3>vmsuspend</h3> +Suspends the execution of the whole virtual machine + +<pre> + C: vmsuspend + R: ok + E: vmsuspended client + +Errors: + error: thread already suspended +</pre> +</body> + +</html> diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/pdavm/src/org/eclipse/cdt/examples/pdavm/PDAVirtualMachine.java b/dsf/org.eclipse.cdt.examples.dsf.pda/pdavm/src/org/eclipse/cdt/examples/pdavm/PDAVirtualMachine.java new file mode 100644 index 00000000000..5b40a1da7c7 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/pdavm/src/org/eclipse/cdt/examples/pdavm/PDAVirtualMachine.java @@ -0,0 +1,1378 @@ +/******************************************************************************* + * 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.examples.pdavm; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.StringWriter; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; +import java.util.TreeSet; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * + */ +@SuppressWarnings("serial") +public class PDAVirtualMachine { + + static class Stack extends LinkedList<Object> { + public Object pop() { + return isEmpty() ? 0 : remove(size() - 1); + } + + public void push(Object value) { + add(value); + } + } + + static class Register { + Register(String name) { + fName = name; + } + String fName; + String fGroup = "<no_group>"; + boolean fIsWriteable = true; + Map<String, BitField> fBitFields = new LinkedHashMap<String, BitField>(0); + int fValue; + } + + static class BitField { + BitField(String name) { + fName = name; + } + String fName; + int fBitOffset; + int fBitCount; + Map<String, Integer> fMnemonics = new LinkedHashMap<String, Integer>(0); + } + + Map<String,Register> fRegisters = new LinkedHashMap<String,Register>(0); + + class Args { + final String[] fArgs; + + int next = 0; + + Args(String[] args) { + fArgs = args; + } + + String getNextStringArg() { + if (fArgs.length > next) { + return fArgs[next++]; + } + return ""; + } + + int getNextIntArg() { + String arg = getNextStringArg(); + try { + return Integer.parseInt(arg); + } catch (NumberFormatException e) { + } + return 0; + } + + boolean getNextBooleanArg() { + String arg = getNextStringArg(); + try { + return Boolean.parseBoolean(arg); + } catch (NumberFormatException e) { + } + return false; + } + + Object getNextIntOrStringArg() { + String arg = getNextStringArg(); + try { + return Integer.parseInt(arg); + } catch (NumberFormatException e) { + } + return arg; + } + + PDAThread getThreadArg() { + int id = getNextIntArg(); + return fThreads.get(id); + } + } + + class PDAThread { + final int fID; + + /** The push down automata data stack (the data stack). */ + final Stack fStack = new Stack(); + + /** + * PDAThread copy of the code. It can differ from the program if + * performing an evaluation. + */ + String[] fThreadCode; + + /** PDAThread copy of the labels. */ + Map<String, Integer> fThreadLabels; + + /** The stack of stack frames (the control stack) */ + final List<Frame> fFrames = new LinkedList<Frame>(); + + /** Current stack frame (not includced in fFrames) */ + Frame fCurrentFrame; + + /** + * The run flag is true if the thread is running. If the run flag is + * false, the thread exits the next time the main instruction loop runs. + */ + boolean fRun = true; + + String fSuspend = null; + + boolean fStep = false; + + boolean fStepReturn = false; + + int fSavedPC; + + boolean fPerformingEval = false; + + PDAThread(int id, String function, int pc) { + fID = id; + fCurrentFrame = new Frame(function, pc); + fThreadCode = fCode; + fThreadLabels = fLabels; + } + } + + final Map<Integer, PDAThread> fThreads = new LinkedHashMap<Integer, PDAThread>(); + + int fNextThreadId = 1; + + boolean fStarted = true; + /** + * The code is stored as an array of strings, each line of the source file + * being one entry in the array. + */ + final String[] fCode; + + /** A mapping of labels to indicies in the code array */ + final Map<String, Integer> fLabels; + + /** Each stack frame is a mapping of variable names to values. */ + class Frame { + final Map<String, Object> fLocalVariables = new LinkedHashMap<String, Object>(); + + /** + * The name of the function in this frame + */ + final String fFunction; + + /** + * The current program counter in the frame the pc points to the next + * instruction to be executed + */ + int fPC; + + Frame(String function, int pc) { + fFunction = function; + fPC = pc; + } + + void set(String name, Object value) { + if (name.startsWith("$")) { + setRegisterValue(name, value); + } else { + fLocalVariables.put(name, value); + } + } + + Object get(String name) { + if (name.startsWith("$")) { + return getRegisterValue(name); + } else { + return fLocalVariables.get(name); + } + } + } + + void setRegisterValue(String name, Object value) { + Register reg = fRegisters.get(getRegisterPartOfName(name)); + if (reg == null) return; + String bitFieldName = getBitFieldPartOfName(name); + if (bitFieldName != null) { + BitField bitField = reg.fBitFields.get(bitFieldName); + if (bitField == null) return; + Integer intValue = null; + if (value instanceof Integer) { + intValue = (Integer)value; + } else if (value instanceof String) { + intValue = bitField.fMnemonics.get(value); + } + if (intValue != null) { + int bitFieldMask = 2^(bitField.fBitCount - 1); + int registerMask = ~(bitFieldMask << bitField.fBitOffset); + int bitFieldValue = intValue & bitFieldMask; + reg.fValue = (reg.fValue & registerMask) | (bitFieldValue << bitField.fBitOffset); + } + } else if (value instanceof Integer) { + reg.fValue = ((Integer)value).intValue(); + } + } + + Object getRegisterValue(String name) { + Register reg = fRegisters.get(getRegisterPartOfName(name)); + if (reg == null) return null; + String bitFieldName = getBitFieldPartOfName(name); + if (bitFieldName != null) { + BitField bitField = reg.fBitFields.get(bitFieldName); + if (bitField == null) return null; + int bitFieldMask = 2^(bitField.fBitCount - 1); + int registerMask = bitFieldMask << bitField.fBitOffset; + return (reg.fValue & registerMask) >> bitField.fBitOffset; + } else { + return reg.fValue; + } + } + + /** + * Breakpoints are stored per each each line of code. The boolean indicates + * whether the whole VM should suspend or just the triggering thread. + */ + final Map<Integer, Boolean> fBreakpoints = new HashMap<Integer, Boolean>(); + + /** + * The suspend flag is true if the VM should suspend running the program and + * just listen for debug commands. + */ + String fSuspendVM; + + /** Flag indicating whether the debugger is performing a step. */ + boolean fStepVM = false; + + /** Flag indicating whether the debugger is performing a step return */ + boolean fStepReturnVM = false; + + int fSteppingThread = 0; + + /** Name of the pda program being debugged */ + final String fFilename; + + /** The command line argument to start a debug session. */ + final boolean fDebug; + + /** The port to listen for debug commands on */ + final int fCommandPort; + + /** + * Command socket for receiving debug commands and sending command responses + */ + Socket fCommandSocket; + + /** Command socket reader */ + BufferedReader fCommandReceiveStream; + + /** Command socket write stream. */ + OutputStream fCommandResponseStream; + + /** The port to send debug events to */ + final int fEventPort; + + /** Event socket */ + Socket fEventSocket; + + /** Event socket and write stream. */ + OutputStream fEventStream; + + /** The eventstops table holds which events cause suspends and which do not. */ + final Map<String, Boolean> fEventStops = new HashMap<String, Boolean>(); + { + fEventStops.put("unimpinstr", false); + fEventStops.put("nosuchlabel", false); + } + + /** + * The watchpoints table holds watchpoint information. + * <p/> + * variablename_stackframedepth => N + * <ul> + * <li>N = 0 is no watch</li> + * <li>N = 1 is read watch</li> + * <li>N = 2 is write watch</li> + * <li>N = 3 is both, etc.</li> + */ + final Map<String, Integer> fWatchpoints = new HashMap<String, Integer>(); + + public static void main(String[] args) { + String programFile = args.length >= 1 ? args[0] : null; + if (programFile == null) { + System.err.println("Error: No program specified"); + return; + } + + String debugFlag = args.length >= 2 ? args[1] : ""; + boolean debug = "-debug".equals(debugFlag); + int commandPort = 0; + int eventPort = 0; + + if (debug) { + String commandPortStr = args.length >= 3 ? args[2] : ""; + try { + commandPort = Integer.parseInt(commandPortStr); + } catch (NumberFormatException e) { + System.err.println("Error: Invalid command port"); + return; + } + + String eventPortStr = args.length >= 4 ? args[3] : ""; + try { + eventPort = Integer.parseInt(eventPortStr); + } catch (NumberFormatException e) { + System.err.println("Error: Invalid event port"); + return; + } + } + + PDAVirtualMachine pdaVM = null; + try { + pdaVM = new PDAVirtualMachine(programFile, debug, commandPort, eventPort); + pdaVM.startDebugger(); + } catch (IOException e) { + System.err.println("Error: " + e.toString()); + return; + } + pdaVM.run(); + } + + PDAVirtualMachine(String inputFile, boolean debug, int commandPort, int eventPort) throws IOException { + fFilename = inputFile; + + // Load all the code into memory + FileReader fileReader = new FileReader(inputFile); + StringWriter stringWriter = new StringWriter(); + List<String> code = new LinkedList<String>(); + int c = fileReader.read(); + while (c != -1) { + if (c == '\n') { + code.add(stringWriter.toString().trim()); + stringWriter = new StringWriter(); + } else { + stringWriter.write(c); + } + c = fileReader.read(); + } + code.add(stringWriter.toString().trim()); + fCode = code.toArray(new String[code.size()]); + + fLabels = mapLabels(fCode); + + fDebug = debug; + fCommandPort = commandPort; + fEventPort = eventPort; + } + + /** + * Initializes the labels map + */ + Map<String, Integer> mapLabels(String[] code) { + Map<String, Integer> labels = new HashMap<String, Integer>(); + for (int i = 0; i < code.length; i++) { + if (code[i].length() != 0 && code[i].charAt(0) == ':') { + labels.put(code[i].substring(1), i); + } + } + return labels; + } + + void sendCommandResponse(String response) { + try { + fCommandResponseStream.write(response.getBytes()); + fCommandResponseStream.flush(); + } catch (IOException e) { + } + } + + void sendDebugEvent(String event, boolean error) { + if (fDebug) { + try { + fEventStream.write(event.getBytes()); + fEventStream.write('\n'); + fEventStream.flush(); + } catch (IOException e) { + System.err.println("Error: " + e); + System.exit(1); + } + } else if (error) { + System.err.println("Error: " + event); + } + } + + void startDebugger() throws IOException { + if (fDebug) { + System.out.println("-debug " + fCommandPort + " " + fEventPort); + } + + ServerSocket commandServerSocket = new ServerSocket(fCommandPort); + fCommandSocket = commandServerSocket.accept(); + fCommandReceiveStream = new BufferedReader(new InputStreamReader(fCommandSocket.getInputStream())); + fCommandResponseStream = new PrintStream(fCommandSocket.getOutputStream()); + commandServerSocket.close(); + + ServerSocket eventServerSocket = new ServerSocket(fEventPort); + fEventSocket = eventServerSocket.accept(); + fEventStream = new PrintStream(fEventSocket.getOutputStream()); + eventServerSocket.close(); + + System.out.println("debug connection accepted"); + + fSuspendVM = "client"; + } + + void run() { + int id = fNextThreadId++; + fThreads.put(id, new PDAThread(id, "main", 0)); + if (fDebug) { + sendDebugEvent("started " + id, false); + } + + boolean allThreadsSuspended = false; + while (!fThreads.isEmpty()) { + checkForBreakpoint(); + + if (fSuspendVM != null) { + debugUI(); + } else { + yieldToDebug(allThreadsSuspended); + if (fSuspendVM != null) { + // Received a command to suspend VM, skip executing threads. + continue; + } + } + + PDAThread[] threadsCopy = fThreads.values().toArray(new PDAThread[fThreads.size()]); + allThreadsSuspended = true; + for (PDAThread thread : threadsCopy) { + if (thread.fSuspend == null) { + allThreadsSuspended = false; + + String instruction = thread.fThreadCode[thread.fCurrentFrame.fPC]; + thread.fCurrentFrame.fPC++; + doOneInstruction(thread, instruction); + if (thread.fCurrentFrame.fPC >= thread.fThreadCode.length) { + // Thread reached end of code, exit from the thread. + thread.fRun = false; + } else if (thread.fStepReturn) { + // If this thread is in a step-return operation, check + // if we've returned from a call. + instruction = thread.fThreadCode[thread.fCurrentFrame.fPC]; + if ("return".equals(instruction)) { + // Note: this will only be triggered if the current + // thread also has the fStepReturn flag set. + if (fStepReturnVM) { + fSuspendVM = thread.fID + " step"; + } else { + thread.fSuspend = "step"; + } + } + } + if (!thread.fRun) { + sendDebugEvent("exited " + thread.fID, false); + fThreads.remove(thread.fID); + } else if (thread.fSuspend != null) { + sendDebugEvent("suspended " + thread.fID + " " + thread.fSuspend, false); + thread.fStep = thread.fStepReturn = thread.fPerformingEval = false; + } + } + } + + // Force thread context switch to avoid starving out other + // processes in the system. + Thread.yield(); + } + + sendDebugEvent("terminated", false); + if (fDebug) { + try { + fCommandReceiveStream.close(); + fCommandResponseStream.close(); + fCommandSocket.close(); + fEventStream.close(); + fEventSocket.close(); + } catch (IOException e) { + System.out.println("Error: " + e); + } + } + + } + + void doOneInstruction(PDAThread thread, String instr) { + StringTokenizer tokenizer = new StringTokenizer(instr); + String op = tokenizer.nextToken(); + List<String> tokens = new LinkedList<String>(); + while (tokenizer.hasMoreTokens()) { + tokens.add(tokenizer.nextToken()); + } + Args args = new Args(tokens.toArray(new String[tokens.size()])); + + boolean opValid = true; + if (op.equals("add")) iAdd(thread, args); + else if (op.equals("branch_not_zero")) iBranchNotZero(thread, args); + else if (op.equals("call")) iCall(thread, args); + else if (op.equals("dec")) iDec(thread, args); + else if (op.equals("def")) iDef(thread, args); + else if (op.equals("dup")) iDup(thread, args); + else if (op.equals("exec")) iExec(thread, args); + else if (op.equals("halt")) iHalt(thread, args); + else if (op.equals("output")) iOutput(thread, args); + else if (op.equals("pop")) iPop(thread, args); + else if (op.equals("push")) iPush(thread, args); + else if (op.equals("return")) iReturn(thread, args); + else if (op.equals("var")) iVar(thread, args); + else if (op.equals("xyzzy")) iInternalEndEval(thread, args); + else if (op.startsWith(":")) {} // label + else if (op.startsWith("#")) {} // comment + else { + opValid = false; + } + + if (!opValid) { + sendDebugEvent("unimplemented instruction " + op, true); + if (fEventStops.get("unimpinstr")) { + fSuspendVM = thread.fID + " event unimpinstr"; + thread.fCurrentFrame.fPC--; + } + } else if (thread.fStep) { + if (fStepVM) { + fSuspendVM = thread.fID + " step"; + fStepVM = false; + } else { + thread.fSuspend = "step"; + } + thread.fStep = false; + } + } + + void checkForBreakpoint() { + if (fDebug) { + for (PDAThread thread : fThreads.values()) { + int pc = thread.fCurrentFrame.fPC; + // Suspend for breakpoint if: + // - the VM is not yet set to suspend, for e.g. as a result of step end, + // - the thread is not yet suspended and is not performing an evaluation + // - the breakpoints table contains a breakpoint for the given line. + if (fSuspendVM == null && + thread.fSuspend == null && !thread.fPerformingEval && + fBreakpoints.containsKey(pc)) + { + if (fBreakpoints.get(pc)) { + fSuspendVM = thread.fID + " breakpoint " + pc; + } else { + thread.fSuspend = "breakpoint " + pc; + thread.fStep = thread.fStepReturn = false; + sendDebugEvent("suspended " + thread.fID + " " + thread.fSuspend, false); + } + } + } + } + } + + /** + * After each instruction, we check the debug command channel for control input. If + * there are commands, process them. + */ + void yieldToDebug(boolean allThreadsSuspended) { + if (fDebug) { + String line = ""; + try { + if (allThreadsSuspended || fCommandReceiveStream.ready()) { + line = fCommandReceiveStream.readLine(); + processDebugCommand(line); + } + } catch (IOException e) { + System.err.println("Error: " + e); + System.exit(1); + } + } + } + + /** + * Service the debugger commands while the VM is suspended + */ + void debugUI() { + if (!fStarted) { + sendDebugEvent("vmsuspended " + fSuspendVM, false); + } else { + fStarted = false; + } + + // Clear all stepping flags. In case the VM suspended while + // a step operation was being performed for the VM or some thread. + fStepVM = fStepReturnVM = false; + for (PDAThread thread : fThreads.values()) { + thread.fSuspend = null; + thread.fStep = thread.fStepReturn = thread.fPerformingEval = false; + } + + while (fSuspendVM != null) { + String line = ""; + try { + line = fCommandReceiveStream.readLine(); + } catch (IOException e) { + System.err.println("Error: " + e); + System.exit(1); + return; + } + processDebugCommand(line); + } + + if (fStepVM || fStepReturnVM) { + sendDebugEvent("vmresumed step", false); + } else { + sendDebugEvent("vmresumed client", false); + } + } + + void processDebugCommand(String line) { + StringTokenizer tokenizer = new StringTokenizer(line.trim()); + if (line.length() == 0) { + return; + } + + String command = tokenizer.nextToken(); + List<String> tokens = new LinkedList<String>(); + while (tokenizer.hasMoreTokens()) { + tokens.add(tokenizer.nextToken()); + } + Args args = new Args(tokens.toArray(new String[tokens.size()])); + + if ("children".equals(command)) debugChildren(args); + else if ("clear".equals(command)) debugClearBreakpoint(args); + else if ("data".equals(command)) debugData(args); + else if ("drop".equals(command)) debugDropFrame(args); + else if ("eval".equals(command)) debugEval(args); + else if ("eventstop".equals(command)) debugEventStop(args); + else if ("exit".equals(command)) debugExit(); + else if ("frame".equals(command)) debugFrame(args); + else if ("groups".equals(command)) debugGroups(args); + else if ("popdata".equals(command)) debugPop(args); + else if ("pushdata".equals(command)) debugPush(args); + else if ("registers".equals(command)) debugRegisters(args); + else if ("resume".equals(command)) debugResume(args); + else if ("set".equals(command)) debugSetBreakpoint(args); + else if ("setdata".equals(command)) debugSetData(args); + else if ("setvar".equals(command)) debugSetVariable(args); + else if ("stack".equals(command)) debugStack(args); + else if ("stackdepth".equals(command)) debugStackDepth(args); + else if ("state".equals(command)) debugState(args); + else if ("step".equals(command)) debugStep(args); + else if ("stepreturn".equals(command)) debugStepReturn(args); + else if ("suspend".equals(command)) debugSuspend(args); + else if ("threads".equals(command)) debugThreads(); + else if ("var".equals(command)) debugVar(args); + else if ("vmresume".equals(command)) debugVMResume(); + else if ("vmsuspend".equals(command)) debugVMSuspend(); + else if ("watch".equals(command)) debugWatch(args); + else { + sendCommandResponse("error: invalid command\n"); + } + } + + void debugChildren(Args args) { + PDAThread thread = args.getThreadArg(); + if (thread == null) { + sendCommandResponse("error: invalid thread\n"); + return; + } + + int sfnumber = args.getNextIntArg(); + String var = args.getNextStringArg(); + + Frame frame = sfnumber >= thread.fFrames.size() + ? thread.fCurrentFrame : thread.fFrames.get(sfnumber); + + String varDot = var + "."; + List<String> children = new ArrayList<String>(); + for (String localVar : frame.fLocalVariables.keySet()) { + if (localVar.startsWith(varDot) && localVar.indexOf('.', varDot.length() + 1) == -1) { + children.add(localVar); + } + } + + StringBuffer result = new StringBuffer(); + for (String child : children) { + result.append(child); + result.append('|'); + } + result.append('\n'); + + sendCommandResponse(result.toString()); + } + + void debugClearBreakpoint(Args args) { + int line = args.getNextIntArg(); + + fBreakpoints.remove(line); + sendCommandResponse("ok\n"); + } + + private static Pattern fPackPattern = Pattern.compile("%([a-fA-F0-9][a-fA-F0-9])"); + + void debugData(Args args) { + PDAThread thread = args.getThreadArg(); + if (thread == null) { + sendCommandResponse("error: invalid thread\n"); + return; + } + + StringBuffer result = new StringBuffer(); + for (Object val : thread.fStack) { + result.append(val); + result.append('|'); + } + result.append('\n'); + sendCommandResponse(result.toString()); + } + + void debugDropFrame(Args args) { + PDAThread thread = args.getThreadArg(); + if (thread == null) { + sendCommandResponse("error: invalid thread\n"); + return; + } + + if (!thread.fFrames.isEmpty()) { + thread.fCurrentFrame = thread.fFrames.remove(thread.fFrames.size() - 1); + } + thread.fCurrentFrame.fPC--; + sendCommandResponse("ok\n"); + if (fSuspendVM != null) { + sendDebugEvent("vmresumed drop", false); + sendDebugEvent("vmsuspended " + thread.fID + " drop", false); + } else { + sendDebugEvent("resumed " + thread.fID + " drop", false); + sendDebugEvent("suspended " + thread.fID + " drop", false); + } + } + + void debugEval(Args args) { + if (fSuspendVM != null) { + sendCommandResponse("error: cannot evaluate while vm is suspended\n"); + return; + } + + PDAThread thread = args.getThreadArg(); + if (thread == null) { + sendCommandResponse("error: invalid thread\n"); + return; + } + + if (thread.fSuspend == null) { + sendCommandResponse("error: thread running\n"); + return; + } + + StringTokenizer tokenizer = new StringTokenizer(args.getNextStringArg(), "|"); + tokenizer.countTokens(); + + int numEvalLines = tokenizer.countTokens(); + thread.fThreadCode = new String[fCode.length + numEvalLines + 1]; + System.arraycopy(fCode, 0, thread.fThreadCode, 0, fCode.length); + for (int i = 0; i < numEvalLines; i++) { + String line = tokenizer.nextToken(); + StringBuffer lineBuf = new StringBuffer(line.length()); + Matcher matcher = fPackPattern.matcher(line); + int lastMatchEnd = 0; + while (matcher.find()) { + lineBuf.append(line.substring(lastMatchEnd, matcher.start())); + String charCode = line.substring(matcher.start() + 1, matcher.start() + 3); + try { + lineBuf.append((char) Integer.parseInt(charCode, 16)); + } catch (NumberFormatException e) { + } + lastMatchEnd = matcher.end(); + } + if (lastMatchEnd < line.length()) { + lineBuf.append(line.substring(lastMatchEnd)); + } + thread.fThreadCode[fCode.length + i] = lineBuf.toString(); + } + thread.fThreadCode[fCode.length + numEvalLines] = "xyzzy"; + thread.fThreadLabels = mapLabels(fCode); + + thread.fSavedPC = thread.fCurrentFrame.fPC; + thread.fCurrentFrame.fPC = fCode.length; + thread.fPerformingEval = true; + + thread.fSuspend = null; + + sendCommandResponse("ok\n"); + + sendDebugEvent("resumed " + thread.fID + " eval", false); + } + + void debugEventStop(Args args) { + String event = args.getNextStringArg(); + int stop = args.getNextIntArg(); + fEventStops.put(event, stop > 0); + sendCommandResponse("ok\n"); + } + + void debugExit() { + sendCommandResponse("ok\n"); + sendDebugEvent("terminated", false); + System.exit(0); + } + + void debugFrame(Args args) { + PDAThread thread = args.getThreadArg(); + if (thread == null) { + sendCommandResponse("error: invalid thread\n"); + return; + } + + int sfnumber = args.getNextIntArg(); + Frame frame = null; + if (sfnumber >= thread.fFrames.size()) { + frame = thread.fCurrentFrame; + } else { + frame = thread.fFrames.get(sfnumber); + } + sendCommandResponse(printFrame(frame) + "\n"); + } + + void debugGroups(Args args) { + TreeSet<String> groups = new TreeSet<String>(); + for (Register reg : fRegisters.values()) { + groups.add(reg.fGroup); + } + StringBuffer response = new StringBuffer(); + for (String group : groups) { + response.append(group); + response.append('|'); + } + response.append('\n'); + sendCommandResponse(response.toString()); + } + + void debugPop(Args args) { + PDAThread thread = args.getThreadArg(); + if (thread == null) { + sendCommandResponse("error: invalid thread\n"); + return; + } + + thread.fStack.pop(); + sendCommandResponse("ok\n"); + } + + void debugPush(Args args) { + PDAThread thread = args.getThreadArg(); + if (thread == null) { + sendCommandResponse("error: invalid thread\n"); + return; + } + + Object val = args.getNextIntOrStringArg(); + thread.fStack.push(val); + sendCommandResponse("ok\n"); + } + + void debugRegisters(Args args) { + String group = args.getNextStringArg(); + + StringBuffer response = new StringBuffer(); + for (Register reg : fRegisters.values()) { + if (group.equals(reg.fGroup)) { + response.append(reg.fName); + response.append(' '); + response.append(reg.fIsWriteable); + for (BitField bitField : reg.fBitFields.values()) { + response.append('|'); + response.append(bitField.fName); + response.append(' '); + response.append(bitField.fBitOffset); + response.append(' '); + response.append(bitField.fBitCount); + response.append(' '); + for (Map.Entry<String, Integer> mnemonicEntry : bitField.fMnemonics.entrySet()) { + response.append(mnemonicEntry.getKey()); + response.append(' '); + response.append(mnemonicEntry.getValue()); + response.append(' '); + } + } + + response.append('#'); + } + } + response.append('\n'); + sendCommandResponse(response.toString()); + } + + void debugResume(Args args) { + PDAThread thread = args.getThreadArg(); + if (thread == null) { + sendCommandResponse("error: invalid thread\n"); + return; + } + if (fSuspendVM != null) { + sendCommandResponse("error: cannot resume thread when vm is suspended\n"); + return; + } + if (thread.fSuspend == null) { + sendCommandResponse("error: thread already running\n"); + return; + } + + thread.fSuspend = null; + sendDebugEvent("resumed " + thread.fID + " client", false); + + sendCommandResponse("ok\n"); + } + + void debugSetBreakpoint(Args args) { + int line = args.getNextIntArg(); + int stopVM = args.getNextIntArg(); + + fBreakpoints.put(line, stopVM != 0); + sendCommandResponse("ok\n"); + } + + void debugSetData(Args args) { + PDAThread thread = args.getThreadArg(); + if (thread == null) { + sendCommandResponse("error: invalid thread\n"); + return; + } + + int offset = args.getNextIntArg(); + Object val = args.getNextIntOrStringArg(); + + if (offset < thread.fStack.size()) { + thread.fStack.set(offset, val); + } else { + thread.fStack.add(0, val); + } + sendCommandResponse("ok\n"); + } + + void debugSetVariable(Args args) { + PDAThread thread = args.getThreadArg(); + if (thread == null) { + sendCommandResponse("error: invalid thread\n"); + return; + } + + int sfnumber = args.getNextIntArg(); + String var = args.getNextStringArg(); + Object val = args.getNextIntOrStringArg(); + + if (sfnumber >= thread.fFrames.size()) { + thread.fCurrentFrame.set(var, val); + } else { + thread.fFrames.get(sfnumber).set(var, val); + } + sendCommandResponse("ok\n"); + } + + void debugStack(Args args) { + PDAThread thread = args.getThreadArg(); + if (thread == null) { + sendCommandResponse("error: invalid thread\n"); + return; + } + + StringBuffer result = new StringBuffer(); + for (Frame frame : thread.fFrames) { + result.append(printFrame(frame)); + result.append('#'); + } + result.append(printFrame(thread.fCurrentFrame)); + result.append('\n'); + sendCommandResponse(result.toString()); + } + + void debugStackDepth(Args args) { + PDAThread thread = args.getThreadArg(); + if (thread == null) { + sendCommandResponse("error: invalid thread\n"); + return; + } + sendCommandResponse( Integer.toString(thread.fFrames.size() + 1) + "\n" ); + } + + + /** + * The stack frame output is: frame # frame # frame ... where each frame is: + * filename | line number | function name | var | var | var | var ... + */ + private String printFrame(Frame frame) { + StringBuffer buf = new StringBuffer(); + buf.append(fFilename); + buf.append('|'); + buf.append(frame.fPC); + buf.append('|'); + buf.append(frame.fFunction); + for (String var : frame.fLocalVariables.keySet()) { + if (var.indexOf('.') == -1) { + buf.append('|'); + buf.append(var); + } + } + return buf.toString(); + } + + void debugState(Args args) { + PDAThread thread = args.getThreadArg(); + String response = null; + if (thread == null) { + response = fSuspendVM == null ? "running" : fSuspendVM; + } else if (fSuspendVM != null) { + response = "vm"; + } else { + response = thread.fSuspend == null ? "running" : thread.fSuspend; + } + sendCommandResponse(response + "\n"); + } + + void debugStep(Args args) { + PDAThread thread = args.getThreadArg(); + if (thread == null) { + sendCommandResponse("error: invalid thread\n"); + return; + } + + // Set suspend to null to allow the debug loop to exit back to the + // instruction loop and thus run an instruction. However, we want to + // come back to the debug loop right away, so the step flag is set to + // true which will cause the suspend flag to get set to true when we + // get to the next instruction. + if (fSuspendVM != null) { + // All threads are suspended, so suspend all threads again when + // step completes. + fSuspendVM = null; + fStepVM = true; + // Also mark the thread that initiated the step to mark it as + // the triggering thread when suspending. + thread.fStep = true; + } else { + if (thread.fSuspend == null) { + sendCommandResponse("error: thread already running\n"); + return; + } + thread.fSuspend = null; + thread.fStep = true; + sendDebugEvent("resumed " + thread.fID + " step", false); + } + sendCommandResponse("ok\n"); + } + + void debugStepReturn(Args args) { + PDAThread thread = args.getThreadArg(); + if (thread == null) { + sendCommandResponse("error: invalid thread\n"); + return; + } + + if (fSuspendVM != null) { + fSuspendVM = null; + fStepReturnVM = true; + thread.fStepReturn = true; + } else { + if (thread.fSuspend == null) { + sendCommandResponse("error: thread running\n"); + return; + } + thread.fSuspend = null; + thread.fStepReturn = true; + sendDebugEvent("resumed " + thread.fID + " step", false); + } + sendCommandResponse("ok\n"); + } + + void debugSuspend(Args args) { + PDAThread thread = args.getThreadArg(); + if (thread == null) { + sendCommandResponse("error: invalid thread\n"); + return; + } + if (fSuspendVM != null) { + sendCommandResponse("error: vm already suspended\n"); + return; + } + if (thread.fSuspend != null) { + sendCommandResponse("error: thread already suspended\n"); + return; + } + + thread.fSuspend = "client"; + sendDebugEvent("suspended " + thread.fID + " client", false); + sendCommandResponse("ok\n"); + } + + void debugThreads() { + StringBuffer response = new StringBuffer(); + for (int threadId : fThreads.keySet()) { + response.append(threadId); + response.append(' '); + } + sendCommandResponse(response.toString().trim() + "\n"); + } + + void debugVar(Args args) { + PDAThread thread = args.getThreadArg(); + if (thread == null) { + sendCommandResponse("error: invalid thread\n"); + return; + } + + int sfnumber = args.getNextIntArg(); + String var = args.getNextStringArg(); + + Frame frame = sfnumber >= thread.fFrames.size() + ? thread.fCurrentFrame : thread.fFrames.get(sfnumber); + + Object val = frame.get(var); + if (val == null) { + sendCommandResponse("error: variable undefined\n"); + } else { + sendCommandResponse(val.toString() + "\n"); + } + } + + void debugVMResume() { + if (fSuspendVM == null) { + sendCommandResponse("error: vm already running\n"); + return; + } + + fSuspendVM = null; + sendCommandResponse("ok\n"); + } + + void debugVMSuspend() { + if (fSuspendVM != null) { + sendCommandResponse("error: vm already suspended\n"); + return; + } + + fSuspendVM = "client"; + sendCommandResponse("ok\n"); + } + + void debugWatch(Args args) { + String funcAndVar = args.getNextStringArg(); + int flags = args.getNextIntArg(); + fWatchpoints.put(funcAndVar, flags); + sendCommandResponse("ok\n"); + } + + void iAdd(PDAThread thread, Args args) { + Object val1 = thread.fStack.pop(); + Object val2 = thread.fStack.pop(); + if (val1 instanceof Integer && val2 instanceof Integer) { + int intVal1 = ((Integer) val1).intValue(); + int intVal2 = ((Integer) val2).intValue(); + thread.fStack.push(intVal1 + intVal2); + } else { + thread.fStack.push(-1); + } + } + + void iBranchNotZero(PDAThread thread, Args args) { + Object val = thread.fStack.pop(); + if (val instanceof Integer && ((Integer) val).intValue() != 0) { + String label = args.getNextStringArg(); + if (thread.fThreadLabels.containsKey(label)) { + thread.fCurrentFrame.fPC = thread.fThreadLabels.get(label); + } else { + sendDebugEvent("no such label " + label, true); + if (fEventStops.get("nosuchlabel")) { + fSuspendVM = thread.fID + " event nosuchlabel"; + thread.fStack.push(val); + thread.fCurrentFrame.fPC--; + } + } + } + } + + void iCall(PDAThread thread, Args args) { + String label = args.getNextStringArg(); + if (thread.fThreadLabels.containsKey(label)) { + thread.fFrames.add(thread.fCurrentFrame); + thread.fCurrentFrame = new Frame(label, thread.fThreadLabels.get(label)); + } else { + sendDebugEvent("no such label " + label, true); + if (fEventStops.get("nosuchlabel")) { + fSuspendVM = thread.fID + " event nosuchlabel"; + thread.fCurrentFrame.fPC--; + } + } + } + + void iDec(PDAThread thread, Args args) { + Object val = thread.fStack.pop(); + if (val instanceof Integer) { + val = new Integer(((Integer) val).intValue() - 1); + } + thread.fStack.push(val); + } + + void iDef(PDAThread thread, Args args) { + String type = args.getNextStringArg(); + + String name = args.getNextStringArg(); + String regName = getRegisterPartOfName(name); + String bitFieldName = getBitFieldPartOfName(name); + + if ("register".equals(type)) { + Register reg = new Register(regName); + reg.fGroup = args.getNextStringArg(); + fRegisters.put(regName, reg); + reg.fIsWriteable = args.getNextBooleanArg(); + } else if ("bitfield".equals(type)) { + Register reg = fRegisters.get(regName); + if (reg == null) return; + BitField bitField = new BitField(bitFieldName); + bitField.fBitOffset = args.getNextIntArg(); + bitField.fBitCount = args.getNextIntArg(); + reg.fBitFields.put(bitFieldName, bitField); + } else if ("mnemonic".equals(type)) { + Register reg = fRegisters.get(regName); + if (reg == null) return; + BitField bitField = reg.fBitFields.get(bitFieldName); + if (bitField == null) return; + bitField.fMnemonics.put(args.getNextStringArg(), args.getNextIntArg()); + } + sendDebugEvent("registers", false); + } + + private String getRegisterPartOfName(String name) { + if (name.startsWith("$")) { + int end = name.indexOf('.'); + end = end != -1 ? end : name.length(); + return name.substring(1, end); + } + return null; + } + + private String getBitFieldPartOfName(String name) { + int start = name.indexOf('.'); + if (name.startsWith("$") && start != -1) { + return name.substring(start + 1, name.length()); + } + return null; + } + + void iDup(PDAThread thread, Args args) { + Object val = thread.fStack.pop(); + thread.fStack.push(val); + thread.fStack.push(val); + } + + void iExec(PDAThread thread, Args args) { + String label = args.getNextStringArg(); + if (fLabels.containsKey(label)) { + int id = fNextThreadId++; + fThreads.put(id, new PDAThread(id, label, fLabels.get(label))); + sendDebugEvent("started " + id, false); + } else { + sendDebugEvent("no such label " + label, true); + if (fEventStops.get("nosuchlabel")) { + thread.fSuspend = "event nosuchlabel"; + thread.fCurrentFrame.fPC--; + } + } + } + + void iHalt(PDAThread thread, Args args) { + thread.fRun = false; + } + + void iOutput(PDAThread thread, Args args) { + System.out.println(thread.fStack.pop()); + } + + void iPop(PDAThread thread, Args args) { + String arg = args.getNextStringArg(); + if (arg.startsWith("$")) { + String var = arg.substring(1); + thread.fCurrentFrame.set(var, thread.fStack.pop()); + String key = thread.fCurrentFrame.fFunction + "::" + var; + if (fWatchpoints.containsKey(key) && (fWatchpoints.get(key) & 2) != 0) { + fSuspendVM = thread.fID + " watch write " + key; + } + } else { + thread.fStack.pop(); + } + } + + void iPush(PDAThread thread, Args args) { + String arg = args.getNextStringArg(); + while (arg.length() != 0) { + if (arg.startsWith("$")) { + String var = arg.substring(1); + Object val = thread.fCurrentFrame.get(var); + if (val == null) val = "<undefined>"; + thread.fStack.push(val); + String key = thread.fCurrentFrame.fFunction + "::" + var; + if (fWatchpoints.containsKey(key) && (fWatchpoints.get(key) & 1) != 0) { + fSuspendVM = thread.fID + " watch read " + key; + } + } else { + Object val = arg; + try { + val = Integer.parseInt(arg); + } catch (NumberFormatException e) { + } + thread.fStack.push(val); + } + + arg = args.getNextStringArg(); + } + } + + void iReturn(PDAThread thread, Args args) { + if (!thread.fFrames.isEmpty()) { + thread.fCurrentFrame = thread.fFrames.remove(thread.fFrames.size() - 1); + } else { + // Execution returned from the top frame, which means this thread + // should exit. + thread.fRun = false; + } + } + + void iVar(PDAThread thread, Args args) { + String var = args.getNextStringArg(); + thread.fCurrentFrame.set(var, 0); + } + + void iInternalEndEval(PDAThread thread, Args args) { + Object result = thread.fStack.pop(); + thread.fThreadCode = fCode; + thread.fThreadLabels = fLabels; + thread.fCurrentFrame.fPC = thread.fSavedPC; + sendDebugEvent("evalresult " + result, false); + thread.fSuspend = "eval"; + thread.fPerformingEval = false; + } + +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/pdavm/tests/vmtest10.pda b/dsf/org.eclipse.cdt.examples.dsf.pda/pdavm/tests/vmtest10.pda new file mode 100644 index 00000000000..837277f5ff3 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/pdavm/tests/vmtest10.pda @@ -0,0 +1,38 @@ +def register $reg1 group1 true +def register $reg2 group1 false +def register $reg3 group2 true +def bitfield $reg1.field1 0 2 +def bitfield $reg1.field2 2 2 +def mnemonic $reg1.field2 zero 0 +def mnemonic $reg1.field2 one 1 +def mnemonic $reg1.field2 two 2 +def mnemonic $reg1.field2 three 3 +push 1 +pop $$reg1 +push $$reg1 +output +push 2 +pop $$reg1.field1 +push $$reg1.field1 +output +push 4 +pop $$reg1.field1 +push $$reg1.field1 +output +push 1 +pop $$reg1.field2 +push $$reg1 +output +push zero +pop $$reg1.field2 +push $$reg1.field2 +output +push $$reg1 +output +push 2 +pop $$reg1.field2 +push $$reg1.field2 +output +push $$reg1 +output +halt
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/pdavm/tests/vmtest2.pda b/dsf/org.eclipse.cdt.examples.dsf.pda/pdavm/tests/vmtest2.pda new file mode 100644 index 00000000000..95a35f04ffb --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/pdavm/tests/vmtest2.pda @@ -0,0 +1,48 @@ +push 6 +push 7 +push 8 +push 9 +push 10 +call sub1 +output +call sub3 +call sub5 +push 3 +halt +:sub2 +push 27 +return +:sub1 +var m +var n +call sub2 +pop $n +pop $m +push $n +push $m +return +# zero-based line 23 +:sub3 +push 1 +call sub4 +push 2 +call sub4 +push 3 +return +:sub4 +push 4 +return +# zero-based line 34 +:sub5 +var a +var b +var c +pop $c +pop $b +call sub6 +push $a +return +:sub6 +var b +pop $b +return diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/pdavm/tests/vmtest3.pda b/dsf/org.eclipse.cdt.examples.dsf.pda/pdavm/tests/vmtest3.pda new file mode 100644 index 00000000000..5aecdc5e6e0 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/pdavm/tests/vmtest3.pda @@ -0,0 +1,13 @@ +push 1 +push 2 +push 3 +foobar swish +push 4 +add +add +call zippy +add +output +push 1 +branch_not_zero swishy +halt diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/pdavm/tests/vmtest6.pda b/dsf/org.eclipse.cdt.examples.dsf.pda/pdavm/tests/vmtest6.pda new file mode 100644 index 00000000000..d90a960cf38 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/pdavm/tests/vmtest6.pda @@ -0,0 +1,31 @@ +var a +var b +push 1 +pop $a +push 2 +pop $b +push 3 +push 4 +# +call inner +# +push $a +push 2 +add +pop $b +output +# +halt +# +:inner +var a +var c +pop $a +pop $c +push $a +push $a +add +return +:other +push 15 +return
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/pdavm/tests/vmtest8.pda b/dsf/org.eclipse.cdt.examples.dsf.pda/pdavm/tests/vmtest8.pda new file mode 100644 index 00000000000..7729409c279 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/pdavm/tests/vmtest8.pda @@ -0,0 +1,14 @@ +var a +call inner +push 1 +output +halt +:inner +var b +call inner2 +push 2 +return +:inner2 +var c +push 3 +return diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/pdavm/tests/vmtest9.pda b/dsf/org.eclipse.cdt.examples.dsf.pda/pdavm/tests/vmtest9.pda new file mode 100644 index 00000000000..336a251d9ab --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/pdavm/tests/vmtest9.pda @@ -0,0 +1,23 @@ +push 5 +:thread_create +exec foo +dec +dup +branch_not_zero thread_create +push finished +output +halt +:foo +push thread_created +output +call inner +halt +:inner +var b +call inner2 +push 2 +return +:inner2 +var c +push 3 +return diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/pdavm/tests/vmtest_children.pda b/dsf/org.eclipse.cdt.examples.dsf.pda/pdavm/tests/vmtest_children.pda new file mode 100644 index 00000000000..b0bbd163d7d --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/pdavm/tests/vmtest_children.pda @@ -0,0 +1,8 @@ +var a +var a.b +var a.c +push 1 +pop $a.b +push $a.b +output +halt diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/plugin.xml b/dsf/org.eclipse.cdt.examples.dsf.pda/plugin.xml new file mode 100644 index 00000000000..71a16a26b32 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/plugin.xml @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="UTF-8"?> +<?eclipse version="3.0"?> +<plugin> + <extension + point="org.eclipse.core.variables.valueVariables"> + <variable + description="Path to Perl executable in the local file system" + name="dsfPerlExecutable" + initialValue="/usr/bin/perl"/> + </extension> + <extension + point="org.eclipse.debug.core.launchConfigurationTypes"> + <launchConfigurationType + sourceLocatorId="org.eclipse.cdt.examples.dsf.pda.sourceLocator" + delegate="org.eclipse.cdt.examples.dsf.pda.launch.PDALaunchDelegate" + sourcePathComputerId="org.eclipse.cdt.examples.pda.dsf.sourcePathComputer" + name="DSF PDA Application" + id="org.eclipse.cdt.examples.dsf.pda.launchType" + modes="run, debug"/> + </extension> + <extension + point="org.eclipse.debug.core.sourceLocators"> + <sourceLocator + class="org.eclipse.cdt.examples.dsf.pda.sourcelookup.PDASourceLookupDirector" + name="DSF PDA Source Locator" + id="org.eclipse.cdt.examples.dsf.pda.sourceLocator"/> + </extension> + <extension + point="org.eclipse.debug.core.sourcePathComputers"> + <sourcePathComputer + class="org.eclipse.cdt.examples.dsf.pda.sourcelookup.PDASourcePathComputerDelegate" + id="org.eclipse.cdt.examples.dsf.pda.sourcePathComputer"/> + </extension> + <extension + point="org.eclipse.debug.core.breakpoints"> + <breakpoint + class="org.eclipse.cdt.examples.dsf.pda.breakpoints.PDALineBreakpoint" + name="DSF PDA Line Breakpoints" + markerType="org.eclipse.cdt.examples.dsf.pda.markerType.lineBreakpoint" + id="org.eclipse.cdt.examples.dsf.pda.lineBreakpoint"/> + <breakpoint + class="org.eclipse.cdt.examples.dsf.pda.breakpoints.PDAWatchpoint" + name="DSF PDA Watchpoints" + markerType="org.eclipse.cdt.examples.dsf.pda.markerType.watchpoint" + id="org.eclipse.cdt.examples.dsf.pda.watchpoint"/> + </extension> + <extension + id="markerType.lineBreakpoint" + name="PDA Line Breakpoint Marker" + point="org.eclipse.core.resources.markers"> + <super type="org.eclipse.debug.core.lineBreakpointMarker"/> + <persistent value="true"/> + </extension> + <extension + id="org.eclipse.cdt.examples.dsf.pda.markerType.watchpoint" + name="DD PDA Watchpoint Marker" + point="org.eclipse.core.resources.markers"> + <super type="org.eclipse.cdt.examples.dsf.pda.markerType.lineBreakpoint"/> + <persistent value="true"/> + </extension> + + + <extension + point="org.eclipse.debug.core.logicalStructureTypes"> + <logicalStructureType + class="org.eclipse.debug.examples.core.pda.model.WordStructureDelegate" + description="Words" + id="pda.wordStructure" + modelIdentifier="org.eclipse.cdt.examples.dsf.pda.debugModel"/> + </extension> +</plugin> + diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/readme.html b/dsf/org.eclipse.cdt.examples.dsf.pda/readme.html new file mode 100644 index 00000000000..c8858def16d --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/readme.html @@ -0,0 +1,11 @@ +<h1>Debug Examples ReadMe Notes</h1> +<h2>PDA Debugger Example</h2> +<p>In order to actually run the PDA debugger example, you will need a Perl interpreter. + Linux®™ comes with Perl. For Microsoft® Windows®, we use either + ActivePerl (<a href="http://www.activeperl.com/">http://www.activeperl.com/</a>) or Indigo Perl + (<a href="http://www.indigostar.com/">http://www.indigostar.com/</a>). You also + have to set the string substitution variable named “dsfPerlExecutable” + to the complete path to your Perl interpreter. (For example, ours was C:\perl\bin\perl.exe) + To set a string substitution variable, use the Windows > Preferences > + Run/Debug > String Substitution preferences page.<br> +</p> diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/samples/counter.pda b/dsf/org.eclipse.cdt.examples.dsf.pda/samples/counter.pda new file mode 100644 index 00000000000..9b2b731006b --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/samples/counter.pda @@ -0,0 +1,11 @@ +push 0 +:main +var n +pop $n +push $n +push 1 +add +dup +push $n +output +branch_not_zero main
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/samples/drop.pda b/dsf/org.eclipse.cdt.examples.dsf.pda/samples/drop.pda new file mode 100644 index 00000000000..84f60fee035 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/samples/drop.pda @@ -0,0 +1,12 @@ +call one +:one +call two +:two +call three +:three +call four +:four +push DONE +output + + diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/samples/example.pda b/dsf/org.eclipse.cdt.examples.dsf.pda/samples/example.pda new file mode 100644 index 00000000000..a95886358c2 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/samples/example.pda @@ -0,0 +1,35 @@ +push "hello" +output +call foobar +push 3 +:label +dup +push 4 +push 5 +add +add +output +dec +dup +branch_not_zero label +call foobar +push "end" +output +halt +:foobar +var a +var b +call barfoo +push "first" +push "second" +pop $a +pop $b +push $a +push $b +output +output +return +:barfoo +push "barfoo" +output +return diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/samples/fibonacci.pda b/dsf/org.eclipse.cdt.examples.dsf.pda/samples/fibonacci.pda new file mode 100644 index 00000000000..e39595a9811 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/samples/fibonacci.pda @@ -0,0 +1,32 @@ +push 6 +call fibonacci +output +halt +# +# f(n) = f(n-1) + f(n-2) +# f(0) = 1 +# f(1) = 1 +# +:fibonacci +var n +pop $n +push $n +branch_not_zero gt0 +push 1 +return +:gt0 +push $n +dec +branch_not_zero gt1 +push 1 +return +:gt1 +push $n +dec +call fibonacci +push $n +dec +dec +call fibonacci +add +return diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/samples/registers.pda b/dsf/org.eclipse.cdt.examples.dsf.pda/samples/registers.pda new file mode 100644 index 00000000000..97fd8d4094d --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/samples/registers.pda @@ -0,0 +1,72 @@ +def register $pc General true +def register $sp General true +def register $status General true +def bitfield $status.BITS_00_07 0 8 +def bitfield $status.BITS_08_15 8 8 +def bitfield $status.BITS_16_23 16 8 +def bitfield $status.BITS_24_31 24 8 +def mnemonic $status.BITS_24_31 three 3 +def mnemonic $status.BITS_24_31 twelve 12 +def mnemonic $status.BITS_24_31 fourty_eight 48 +def mnemonic $status.BITS_24_31 one_nighty_two 192 +def register $stackdepth General true +def register $stack[0] General true +def register $stack[1] General true +def register $stack[2] General true +def register $stack[3] General true +def register $stack[4] General true +push 103 +pop $$pc +push 306 +push 2 +pop $$sp +push 400 +pop $$status +push 5 +pop $$stackdepth +push 12 +pop $$stack[0] +push 45 +pop $$stack[1] +push 146 +pop $$stack[2] +push 215 +pop $$stack[3] +push 251 +pop $$stack[4] +push 306 +pop $$stack[5] +def register $total-instructions Analysis false +def register $add-instructions Analysis false +def register $call-instructions Analysis false +def register $dec-instructions Analysis false +def register $dup-instructions Analysis false +def register $halt-instructions Analysis false +def register $output-instructions Analysis false +def register $pop-instructions Analysis false +def register $push-instructions Analysis false +def register $return-instructions Analysis false +def register $var-instructions Analysis false +push 1046 +pop $$total-instructions +push 12 +pop $$add-instructions +push 24 +pop $$call-instructions +push 36 +pop $$dec-instructions +push 50 +pop $$dup-instructions +push 62 +pop $$halt-instructions +push 74 +pop $$output-instructions +push 106 +pop $$pop-instructions +push 120 +pop $$push-instructions +push 132 +pop $$return-instructions +push 144 +pop $$var-instructions +halt
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/samples/stack.pda b/dsf/org.eclipse.cdt.examples.dsf.pda/samples/stack.pda new file mode 100644 index 00000000000..c7fa1628b9c --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/samples/stack.pda @@ -0,0 +1,21 @@ +push 5 +:thread_create +exec stack +dec +dup +branch_not_zero thread_create +push finished +output +halt +:stack +push 100 +:inner +dup +output +dup +branch_not_zero descend +return +:descend +dec +call inner +return
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/samples/structures.pda b/dsf/org.eclipse.cdt.examples.dsf.pda/samples/structures.pda new file mode 100644 index 00000000000..b6f7cbf62ab --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/samples/structures.pda @@ -0,0 +1,23 @@ +push one two three +push 1 2 3 +push I II III +var x +var x.a +var x.b +var y +var y.c +var y.d +var y.d.1 +var y.d.2 +var y.d.3 +pop $x +pop $x.a +pop $x.b +pop $y +pop $y.c +pop $y.d +pop $y.d.1 +pop $y.d.2 +pop $y.d.3 +push Done +output
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/PDAPlugin.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/PDAPlugin.java new file mode 100644 index 00000000000..39c74da633e --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/PDAPlugin.java @@ -0,0 +1,212 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 + * Bjorn Freeman-Benson - initial API and implementation + * Wind River Systems - adopted to use with DSF + *******************************************************************************/ +package org.eclipse.cdt.examples.dsf.pda; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.MissingResourceException; +import java.util.ResourceBundle; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Query; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.examples.dsf.pda.launch.PDALaunch; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Plugin; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunch; +import org.osgi.framework.BundleContext; + +/** + * The main plugin class to be used in the desktop. + */ +public class PDAPlugin extends Plugin { + + public static String PLUGIN_ID = "org.eclipse.cdt.examples.dsf.pda"; + + // Debugging flag + public static boolean DEBUG = false; + + //The shared instance. + private static PDAPlugin plugin; + + //Resource bundle. + private ResourceBundle resourceBundle; + + // Bundle context used in registering and retrieving DSF (OSGi) services. + private static BundleContext fContext; + + /** + * Unique identifier for the PDA debug model (value + * <code>pda.debugModel</code>). + */ + public static final String ID_PDA_DEBUG_MODEL = "org.eclipse.cdt.examples.dsf.pda.debugModel"; + + /** + * Name of the string substitution variable that resolves to the + * location of a local Perl executable (value <code>perlExecutable</code>). + */ + public static final String VARIALBE_PERL_EXECUTABLE = "dsfPerlExecutable"; + + /** + * Launch configuration attribute key. Value is a path to a perl + * program. The path is a string representing a full path + * to a perl program in the workspace. + */ + public static final String ATTR_PDA_PROGRAM = ID_PDA_DEBUG_MODEL + ".ATTR_PDA_PROGRAM"; + + /** + * Identifier for the PDA launch configuration type + * (value <code>pda.launchType</code>) + */ + public static final String ID_PDA_LAUNCH_CONFIGURATION_TYPE = "org.eclipse.cdt.examples.dsf.pda.launchType"; + + /** + * The constructor. + */ + public PDAPlugin() { + super(); + plugin = this; + } + + /** + * This method is called upon plug-in activation + */ + @Override + public void start(BundleContext context) throws Exception { + fContext = context; + DEBUG = "true".equals(Platform.getDebugOption(PLUGIN_ID + "/debug")); //$NON-NLS-1$//$NON-NLS-2$ + super.start(context); + } + + /** + * This method is called when the plug-in is stopped + */ + @Override + public void stop(BundleContext context) throws Exception { + shutdownActiveLaunches(); + super.stop(context); + plugin = null; + resourceBundle = null; + fContext = context; + } + + /** + * Returns the shared instance. + */ + public static PDAPlugin getDefault() { + return plugin; + } + + /** + * Returns the string from the plugin's resource bundle, + * or 'key' if not found. + */ + public static String getResourceString(String key) { + ResourceBundle bundle = PDAPlugin.getDefault().getResourceBundle(); + try { + return (bundle != null) ? bundle.getString(key) : key; + } catch (MissingResourceException e) { + return key; + } + } + + /** + * Returns the plugin's resource bundle, + */ + public ResourceBundle getResourceBundle() { + try { + if (resourceBundle == null) + resourceBundle = ResourceBundle.getBundle("org.eclipse.debug.examples.core.pda.DebugCorePluginResources"); + } catch (MissingResourceException x) { + resourceBundle = null; + } + return resourceBundle; + } + + public static BundleContext getBundleContext() { + return fContext; + } + + /** + * Return a <code>java.io.File</code> object that corresponds to the specified + * <code>IPath</code> in the plugin directory, or <code>null</code> if none. + */ + public static File getFileInPlugin(IPath path) { + try { + URL installURL = + new URL(getDefault().getDescriptor().getInstallURL(), path.toString()); + URL localURL = Platform.asLocalURL(installURL); + return new File(localURL.getFile()); + } catch (IOException ioe) { + return null; + } + } + + /** + * Shuts down any active launches. We must shutdown any active sessions + * and services associated with this plugin before this plugin is stopped. + * Any attempts to use the plugins {@link BundleContext} after the plugin + * is shut down will result in exceptions. + */ + private void shutdownActiveLaunches() { + for (ILaunch launch : DebugPlugin.getDefault().getLaunchManager().getLaunches()) { + if (launch instanceof PDALaunch && !((PDALaunch)launch).isShutDown()) { + final PDALaunch pdaLaunch = (PDALaunch)launch; + + Query<Object> launchShutdownQuery = new Query<Object>() { + @Override + protected void execute(DataRequestMonitor<Object> rm) { + pdaLaunch.shutdownServices(rm); + } + }; + + try { + pdaLaunch.getSession().getExecutor().execute(launchShutdownQuery); + } catch (RejectedExecutionException e) { + // We can get this exception if the session is shutdown concurrently + // to this method running. + break; + } + + // The Query.get() method is a synchronous call which blocks until the + // query completes. + try { + launchShutdownQuery.get(); + } catch (InterruptedException e) { + getLog().log(new Status(IStatus.ERROR, PLUGIN_ID, "InterruptedException while shutting down PDA debugger launch " + pdaLaunch, e.getCause())); + } catch (ExecutionException e) { + getLog().log(new Status(IStatus.ERROR, PLUGIN_ID, "Exception while shutting down PDA debugger launch " + pdaLaunch, e.getCause())); + } + } + } + } + + + public static void failRequest(RequestMonitor rm, int code, String message) { + rm.setStatus(new Status(IStatus.ERROR, PLUGIN_ID, code, message, null)); + rm.done(); + } + + public static void debug(String debugString) { + if (DEBUG) { + System.out.println(debugString); + } + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/breakpoints/PDALineBreakpoint.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/breakpoints/PDALineBreakpoint.java new file mode 100644 index 00000000000..8b75267be10 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/breakpoints/PDALineBreakpoint.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 + * Bjorn Freeman-Benson - initial API and implementation + * Wind River Systems - adopted to use with DSF + *******************************************************************************/ +package org.eclipse.cdt.examples.dsf.pda.breakpoints; + +import org.eclipse.cdt.examples.dsf.pda.PDAPlugin; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspaceRunnable; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.debug.core.model.IBreakpoint; +import org.eclipse.debug.core.model.LineBreakpoint; + + +/** + * PDA line breakpoint + * <p> + * This class is identical to the corresponding in PDA debugger implemented in + * org.eclipse.debug.examples. + * </p> + */ +public class PDALineBreakpoint extends LineBreakpoint { + + /** + * Default constructor is required for the breakpoint manager + * to re-create persisted breakpoints. After instantiating a breakpoint, + * the <code>setMarker(...)</code> method is called to restore + * this breakpoint's attributes. + */ + public PDALineBreakpoint() { + } + + /** + * Constructs a line breakpoint on the given resource at the given + * line number. The line number is 1-based (i.e. the first line of a + * file is line number 1). The PDA VM uses 0-based line numbers, + * so this line number translation is done at breakpoint install time. + * + * @param resource file on which to set the breakpoint + * @param lineNumber 1-based line number of the breakpoint + * @throws CoreException if unable to create the breakpoint + */ + public PDALineBreakpoint(final IResource resource, final int lineNumber) throws CoreException { + IWorkspaceRunnable runnable = new IWorkspaceRunnable() { + public void run(IProgressMonitor monitor) throws CoreException { + IMarker marker = resource.createMarker("org.eclipse.cdt.examples.dsf.pda.markerType.lineBreakpoint"); + setMarker(marker); + marker.setAttribute(IBreakpoint.ENABLED, Boolean.TRUE); + marker.setAttribute(IMarker.LINE_NUMBER, lineNumber); + marker.setAttribute(IBreakpoint.ID, getModelIdentifier()); + marker.setAttribute(IMarker.MESSAGE, "Line Breakpoint: " + resource.getName() + " [line: " + lineNumber + "]"); + } + }; + run(getMarkerRule(resource), runnable); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IBreakpoint#getModelIdentifier() + */ + public String getModelIdentifier() { + return PDAPlugin.ID_PDA_DEBUG_MODEL; + } + + /** + * Returns whether this breakpoint is a run-to-line breakpoint + * + * @return whether this breakpoint is a run-to-line breakpoint + */ + public boolean isRunToLineBreakpoint() { + return false; + } + +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/breakpoints/PDAWatchpoint.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/breakpoints/PDAWatchpoint.java new file mode 100644 index 00000000000..a49b96ae37a --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/breakpoints/PDAWatchpoint.java @@ -0,0 +1,173 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 + * Bjorn Freeman-Benson - initial API and implementation + * Wind River Systems - adopted to use with DSF + *******************************************************************************/ +package org.eclipse.cdt.examples.dsf.pda.breakpoints; + +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspaceRunnable; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.debug.core.model.IBreakpoint; +import org.eclipse.debug.core.model.IWatchpoint; + + +/** + * A watchpoint. + * <p> + * This class is identical to the corresponding in PDA debugger implemented in + * org.eclipse.debug.examples. + * </p> + */ +public class PDAWatchpoint extends PDALineBreakpoint implements IWatchpoint { + + // 'read' or 'write' depending on what caused the last suspend for this watchpoint + private String fLastSuspendType; + + // marker attributes + public static final String ACCESS = "ACCESS"; + public static final String MODIFICATION = "MODIFICATION"; + public static final String FUNCTION_NAME = "FUNCTION_NAME"; + public static final String VAR_NAME = "VAR_NAME"; + + /** + * Default constructor is required for the breakpoint manager + * to re-create persisted breakpoints. After instantiating a breakpoint, + * the <code>setMarker(...)</code> method is called to restore + * this breakpoint's attributes. + */ + public PDAWatchpoint() { + } + /** + * Constructs a line breakpoint on the given resource at the given + * line number. The line number is 1-based (i.e. the first line of a + * file is line number 1). The PDA VM uses 0-based line numbers, + * so this line number translation is done at breakpoint install time. + * + * @param resource file on which to set the breakpoint + * @param lineNumber 1-based line number of the breakpoint + * @param functionName function name the variable is defined in + * @param varName variable name that watchpoint is set on + * @param access whether this is an access watchpoint + * @param modification whether this in a modification watchpoint + * @throws CoreException if unable to create the watchpoint + */ + public PDAWatchpoint(final IResource resource, final int lineNumber, final String functionName, final String varName, final boolean access, final boolean modification) throws CoreException { + IWorkspaceRunnable runnable = new IWorkspaceRunnable() { + public void run(IProgressMonitor monitor) throws CoreException { + IMarker marker = resource.createMarker("org.eclipse.cdt.examples.dsf.pda.markerType.watchpoint"); + setMarker(marker); + setEnabled(true); + ensureMarker().setAttribute(IMarker.LINE_NUMBER, lineNumber); + ensureMarker().setAttribute(IBreakpoint.ID, getModelIdentifier()); + setAccess(access); + setModification(modification); + setVariable(functionName, varName); + marker.setAttribute(IMarker.MESSAGE, "Watchpoint: " + resource.getName() + " [line: " + lineNumber + "]"); + } + }; + run(getMarkerRule(resource), runnable); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IWatchpoint#isAccess() + */ + public boolean isAccess() throws CoreException { + return getMarker().getAttribute(ACCESS, true); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IWatchpoint#setAccess(boolean) + */ + public void setAccess(boolean access) throws CoreException { + setAttribute(ACCESS, access); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IWatchpoint#isModification() + */ + public boolean isModification() throws CoreException { + return getMarker().getAttribute(MODIFICATION, true); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IWatchpoint#setModification(boolean) + */ + public void setModification(boolean modification) throws CoreException { + setAttribute(MODIFICATION, modification); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IWatchpoint#supportsAccess() + */ + public boolean supportsAccess() { + return true; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IWatchpoint#supportsModification() + */ + public boolean supportsModification() { + return true; + } + + /** + * Sets the variable and function names the watchpoint is set on. + * + * @param functionName function name + * @param variableName variable name + * @throws CoreException if an exception occurrs setting marker attribtues + */ + protected void setVariable(String functionName, String variableName) throws CoreException { + setAttribute(VAR_NAME, variableName); + setAttribute(FUNCTION_NAME, functionName); + } + + /** + * Returns the name of the variable this watchpoint is set on. + * + * @return the name of the variable this watchpoint is set on + * @throws CoreException if unable to access the attribute + */ + public String getVariableName() throws CoreException { + return getMarker().getAttribute(VAR_NAME, (String)null); + } + + /** + * Returns the name of the function the variable associted with this watchpoint is defined in. + * + * @return the name of the function the variable associted with this watchpoint is defined in + * @throws CoreException if unable to access the attribute + */ + public String getFunctionName() throws CoreException { + return getMarker().getAttribute(FUNCTION_NAME, (String)null); + } + + /** + * Sets the type of event that causes the last suspend event. + * + * @param description one of 'read' or 'write' + */ + public void setSuspendType(String description) { + fLastSuspendType = description; + } + + /** + * Returns the type of event that caused the last suspend. + * + * @return 'read', 'write', or <code>null</code> if undefined + */ + public String getSuspendType() { + return fLastSuspendType; + } + +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/launch/PDALaunch.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/launch/PDALaunch.java new file mode 100644 index 00000000000..87ff4b8362a --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/launch/PDALaunch.java @@ -0,0 +1,258 @@ +/******************************************************************************* + * 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.examples.dsf.pda.launch; + +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DefaultDsfExecutor; +import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Sequence; +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.examples.dsf.pda.PDAPlugin; +import org.eclipse.cdt.examples.dsf.pda.service.PDATerminatedEvent; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.Launch; +import org.eclipse.debug.core.model.ISourceLocator; +import org.eclipse.debug.core.model.ITerminate; + +/** + * The PDA launch object. In general, a DSF-based debugger has to override + * the base launch class in order to supply its own content providers for the + * debug view. Additionally, the PDA launch is used to monitor the state of the + * PDA debugger and to shutdown the DSF services and session belonging to the + * launch. + * <p> + * The PDA launch class mostly contains methods and fields that can be accessed + * on any thread. However, some fields and methods used for managing the DSF + * session need to be synchronized using the DSF executor. + * </p> + */ +@ThreadSafe +public class PDALaunch extends Launch +implements ITerminate +{ + // DSF executor and session. Both are created and shutdown by the launch. + private final DefaultDsfExecutor fExecutor; + private final DsfSession fSession; + + // Objects used to track the status of the DSF session. + private boolean fInitialized = false; + private boolean fShutDown = false; + + @ConfinedToDsfExecutor("getSession().getExecutor()") + private Sequence fInitializationSequence = null; + + /** + * Launch constructor creates the launch for given parameters. The + * constructor also creates a DSF session and an executor, so that + * {@link #getSession()} returns a valid value, however no services + * are initialized yet. + * + * @see Launch + */ + public PDALaunch(ILaunchConfiguration launchConfiguration, String mode, ISourceLocator locator) { + super(launchConfiguration, mode, locator); + + // Create the dispatch queue to be used by debugger control and services + // that belong to this launch + final DefaultDsfExecutor dsfExecutor = new DefaultDsfExecutor(PDAPlugin.ID_PDA_DEBUG_MODEL); + dsfExecutor.prestartCoreThread(); + fExecutor = dsfExecutor; + fSession = DsfSession.startSession(fExecutor, PDAPlugin.ID_PDA_DEBUG_MODEL); + } + + /** + * Returns the DSF services session that belongs to this launch. This + * method will always return a DsfSession object, however if the debugger + * is shut down, the session will no longer active. + */ + public DsfSession getSession() { return fSession; } + + /** + * Initializes the DSF services using the specified parameters. This + * method has to be called on the executor thread in order to avoid + * synchronization issues. + */ + @ConfinedToDsfExecutor("getSession().getExecutor()") + public void initializeServices(String program, final RequestMonitor rm) + { + // Double-check that we're being called in the correct thread. + assert fExecutor.isInExecutorThread(); + + // Check if shutdownServices() was called already, which would be + // highly unusual, but if so we don't need to do anything except set + // the initialized flag. + synchronized(this) { + if (fShutDown) { + fInitialized = true; + return; + } + } + + // Register the launch as listener for services events. + fSession.addServiceEventListener(PDALaunch.this, null); + + // The initialization sequence is stored in a field to allow it to be + // canceled if shutdownServices() is called before the sequence + // completes. + fInitializationSequence = new PDAServicesInitSequence( + getSession(), this, program, + new RequestMonitor(ImmediateExecutor.getInstance(), rm) { + @Override + protected void handleCompleted() { + // Set the initialized flag and check whether the + // shutdown flag is set. Access the flags in a + // synchronized section as these flags can be accessed + // on any thread. + boolean doShutdown = false; + synchronized (this) { + fInitialized = true; + fInitializationSequence = null; + if (fShutDown) { + doShutdown = true; + } + } + + if (doShutdown) { + // If shutdownServices() was already called, start the + // shutdown sequence now. + doShutdown(rm); + } else { + // If there was an error in the startup sequence, + // report the error to the client. + if (getStatus().getSeverity() == IStatus.ERROR) { + rm.setStatus(getStatus()); + } + rm.done(); + } + fireChanged(); + } + }); + + // Finally, execute the sequence. + getSession().getExecutor().execute(fInitializationSequence); + } + + /** + * Event handler for a debugger terminated event. + */ + @DsfServiceEventHandler + public void eventDispatched(PDATerminatedEvent event) { + shutdownServices(new RequestMonitor(ImmediateExecutor.getInstance(), null)); + } + + /** + * Returns whether the DSF service initialization sequence has completed yet. + */ + public synchronized boolean isInitialized() { + return fInitialized; + } + + /** + * Returns whether the DSF services have been set to shut down. + * @return + */ + public synchronized boolean isShutDown() { + return fShutDown; + } + + @Override + public boolean canTerminate() { + return super.canTerminate() && isInitialized() && !isShutDown(); + } + + @Override + public boolean isTerminated() { + return super.isTerminated() || isShutDown(); + } + + + @Override + public void terminate() throws DebugException { + if (isShutDown()) return; + super.terminate(); + } + + /** + * Shuts down the services, the session and the executor associated with + * this launch. + * <p> + * Note: The argument request monitor to this method should NOT use the + * executor that belongs to this launch. By the time the shutdown is + * complete, this executor will not be dispatching anymore and the + * request monitor will never be invoked. Instead callers should use + * the {@link ImmediateExecutor}. + * </p> + * @param rm The request monitor invoked when the shutdown is complete. + */ + @ConfinedToDsfExecutor("getSession().getExecutor()") + public void shutdownServices(final RequestMonitor rm) { + // Check initialize and shutdown flags to determine if the shutdown + // sequence can be called yet. + boolean doShutdown = false; + synchronized (this) { + if (!fInitialized && fInitializationSequence != null) { + // Launch has not yet initialized, try to cancel the + // shutdown sequence. + fInitializationSequence.cancel(false); + } else { + doShutdown = !fShutDown && fInitialized; + } + fShutDown = true; + } + + if (doShutdown) { + doShutdown(rm); + } else { + rm.done(); + } + } + + @ConfinedToDsfExecutor("getSession().getExecutor()") + private void doShutdown(final RequestMonitor rm) { + fExecutor.execute( new PDAServicesShutdownSequence( + fExecutor, fSession.getId(), + new RequestMonitor(fSession.getExecutor(), rm) { + @Override + public void handleCompleted() { + fSession.removeServiceEventListener(PDALaunch.this); + if (!isSuccess()) { + PDAPlugin.getDefault().getLog().log(new MultiStatus( + PDAPlugin.PLUGIN_ID, -1, new IStatus[]{getStatus()}, "Session shutdown failed", null)); //$NON-NLS-1$ + } + // Last order of business, shutdown the dispatch queue. + DsfSession.endSession(fSession); + // endSession takes a full dispatch to distribute the + // session-ended event, finish step only after the dispatch. + fExecutor.shutdown(); + fireTerminate(); + + rm.setStatus(getStatus()); + rm.done(); + } + }) ); + } + + @SuppressWarnings("unchecked") + @Override + public Object getAdapter(Class adapter) { + // Force adapters to be loaded. Otherwise the adapter manager may not find + // the model proxy adapter for DSF-based debug elements. + Platform.getAdapterManager().loadAdapter(this, adapter.getName()); + return super.getAdapter(adapter); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/launch/PDALaunchDelegate.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/launch/PDALaunchDelegate.java new file mode 100644 index 00000000000..a555c94eefd --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/launch/PDALaunchDelegate.java @@ -0,0 +1,161 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 + * Bjorn Freeman-Benson - initial API and implementation + * Wind River Systems - adopted to use with DSF + *******************************************************************************/ +package org.eclipse.cdt.examples.dsf.pda.launch; + +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.service.DsfServicesTracker; +import org.eclipse.cdt.examples.dsf.pda.PDAPlugin; +import org.eclipse.cdt.examples.dsf.pda.service.PDABackend; +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.debug.core.DebugException; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.model.IPersistableSourceLocator; +import org.eclipse.debug.core.model.ISourceLocator; +import org.eclipse.debug.core.model.LaunchConfigurationDelegate; +import org.eclipse.debug.core.sourcelookup.IPersistableSourceLocator2; + + +/** + * Launches PDA program on a PDA interpretter written in Perl + */ +public class PDALaunchDelegate extends LaunchConfigurationDelegate { + + @Override + public ILaunch getLaunch(ILaunchConfiguration configuration, String mode) throws CoreException { + // Need to configure the source locator before creating the launch + // because once the launch is created and added to launch manager, + // the adapters will be created for the whole session, including + // the source lookup adapter. + ISourceLocator locator = getSourceLocator(configuration); + + return new PDALaunch(configuration, mode, locator); + } + + @Override + public boolean buildForLaunch(ILaunchConfiguration configuration, String mode, IProgressMonitor monitor) throws CoreException { + // PDA programs do not require building. + return false; + } + + /** + * Returns a source locator created based on the attributes in the launch configuration. + */ + private ISourceLocator getSourceLocator(ILaunchConfiguration configuration) throws CoreException { + String type = configuration.getAttribute(ILaunchConfiguration.ATTR_SOURCE_LOCATOR_ID, (String)null); + if (type == null) { + type = configuration.getType().getSourceLocatorId(); + } + if (type != null) { + IPersistableSourceLocator locator = DebugPlugin.getDefault().getLaunchManager().newSourceLocator(type); + String memento = configuration.getAttribute(ILaunchConfiguration.ATTR_SOURCE_LOCATOR_MEMENTO, (String)null); + if (memento == null) { + locator.initializeDefaults(configuration); + } else { + if(locator instanceof IPersistableSourceLocator2) + ((IPersistableSourceLocator2)locator).initializeFromMemento(memento, configuration); + else + locator.initializeFromMemento(memento); + } + return locator; + } + return null; + } + + public void launch(ILaunchConfiguration configuration, String mode, ILaunch launch, IProgressMonitor monitor) throws CoreException { + String program = configuration.getAttribute(PDAPlugin.ATTR_PDA_PROGRAM, (String)null); + if (program == null) { + abort("Perl program unspecified.", null); + } + + PDALaunch pdaLaunch = (PDALaunch)launch; + initServices(pdaLaunch, program); + createProcess(pdaLaunch); + } + + /** + * Calls the launch to initialize DSF services for this launch. + */ + private void initServices(final PDALaunch pdaLaunch, final String program) + throws CoreException + { + // Synchronization object to use when waiting for the services initialization. + Query<Object> initQuery = new Query<Object>() { + @Override + protected void execute(DataRequestMonitor<Object> rm) { + pdaLaunch.initializeServices(program, rm); + } + }; + + // Submit the query to the executor. + pdaLaunch.getSession().getExecutor().execute(initQuery); + try { + // Block waiting for query results. + initQuery.get(); + } catch (InterruptedException e1) { + throw new DebugException(new Status(IStatus.ERROR, PDAPlugin.PLUGIN_ID, DebugException.INTERNAL_ERROR, "Interrupted Exception in dispatch thread", e1)); //$NON-NLS-1$ + } catch (ExecutionException e1) { + throw new DebugException(new Status(IStatus.ERROR, PDAPlugin.PLUGIN_ID, DebugException.REQUEST_FAILED, "Error in launch sequence", e1.getCause())); //$NON-NLS-1$ + } + } + + private void createProcess(final PDALaunch pdaLaunch) throws CoreException { + // Synchronization object to use when waiting for the services initialization. + Query<Object[]> initQuery = new Query<Object[]>() { + @Override + protected void execute(DataRequestMonitor<Object[]> rm) { + DsfServicesTracker tracker = new DsfServicesTracker(PDAPlugin.getBundleContext(), pdaLaunch.getSession().getId()); + PDABackend backend = tracker.getService(PDABackend.class); + if (backend == null) { + PDAPlugin.failRequest(rm, IDsfStatusConstants.INVALID_STATE, "PDA Backend service not available"); + return; + } + Object[] retVal = new Object[] { backend.getProcess(), backend.getProcessName() }; + rm.setData(retVal); + rm.done(); + } + }; + + // Submit the query to the executor. + pdaLaunch.getSession().getExecutor().execute(initQuery); + try { + // Block waiting for query results. + Object[] processData = initQuery.get(); + DebugPlugin.newProcess(pdaLaunch, (Process)processData[0], (String)processData[1]); + } catch (InterruptedException e1) { + throw new DebugException(new Status(IStatus.ERROR, PDAPlugin.PLUGIN_ID, DebugException.INTERNAL_ERROR, "Interrupted Exception in dispatch thread", e1)); //$NON-NLS-1$ + } catch (ExecutionException e1) { + throw new DebugException(new Status(IStatus.ERROR, PDAPlugin.PLUGIN_ID, DebugException.REQUEST_FAILED, "Error in launch sequence", e1.getCause())); //$NON-NLS-1$ + } + } + + /** + * Throws an exception with a new status containing the given + * message and optional exception. + * + * @param message error message + * @param e underlying exception + * @throws CoreException + */ + private void abort(String message, Throwable e) throws CoreException { + throw new CoreException(new Status(IStatus.ERROR, PDAPlugin.PLUGIN_ID, 0, message, e)); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/launch/PDAServicesInitSequence.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/launch/PDAServicesInitSequence.java new file mode 100644 index 00000000000..d993cf54276 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/launch/PDAServicesInitSequence.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.examples.dsf.pda.launch; + +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Sequence; +import org.eclipse.cdt.dsf.debug.service.BreakpointsMediator; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.examples.dsf.pda.service.PDABackend; +import org.eclipse.cdt.examples.dsf.pda.service.PDABreakpointAttributeTranslator; +import org.eclipse.cdt.examples.dsf.pda.service.PDABreakpoints; +import org.eclipse.cdt.examples.dsf.pda.service.PDACommandControl; +import org.eclipse.cdt.examples.dsf.pda.service.PDAExpressions; +import org.eclipse.cdt.examples.dsf.pda.service.PDARegisters; +import org.eclipse.cdt.examples.dsf.pda.service.PDARunControl; +import org.eclipse.cdt.examples.dsf.pda.service.PDAStack; + +/** + * The initialization sequence for PDA debugger services. This sequence contains + * the series of steps that are executed to properly initialize the PDA-DSF debug + * session. If any of the individual steps fail, the initialization will abort. + * <p> + * The order in which services are initialized is important. Some services depend + * on other services and they assume that they will be initialized only if those + * services are active. Also the service events are prioritized and their priority + * depends on the order in which the services were initialized. + * </p> + */ +public class PDAServicesInitSequence extends Sequence { + + Step[] fSteps = new Step[] { + new Step() + { + @Override + public void execute(RequestMonitor requestMonitor) { + // Start PDA back end debugger service. + new PDABackend(fSession, fLaunch, fProgram).initialize(requestMonitor); + } + }, + new Step() + { + @Override + public void execute(RequestMonitor requestMonitor) { + // Start PDA command control service. + fCommandControl = new PDACommandControl(fSession); + fCommandControl.initialize(requestMonitor); + } + }, + new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + // Start the run control service. + fRunControl = new PDARunControl(fSession); + fRunControl.initialize(requestMonitor); + } + }, + new Step() { + @Override + public void execute(final RequestMonitor requestMonitor) { + // Start the low-level breakpoint service + new PDABreakpoints(fSession).initialize(new RequestMonitor(getExecutor(), requestMonitor)); + } + }, + new Step() { + @Override + public void execute(final RequestMonitor requestMonitor) { + // Create the breakpoint mediator and start tracking PDA breakpoints. + + final BreakpointsMediator bpmService = new BreakpointsMediator( + fSession, new PDABreakpointAttributeTranslator()); + bpmService.initialize(new RequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleSuccess() { + bpmService.startTrackingBreakpoints(fCommandControl.getContext(), requestMonitor); + } + }); + } + }, + new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + // Start the stack service. + new PDAStack(fSession).initialize(requestMonitor); + } + }, + new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + // Start the service to track expressions. + new PDAExpressions(fSession).initialize(requestMonitor); + } + }, + new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + // Start the service to track expressions. + new PDARegisters(fSession).initialize(requestMonitor); + } + }, + new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + fRunControl.resume(fCommandControl.getContext(), requestMonitor); + } + }, + }; + + // Sequence input parameters, used in initializing services. + private PDALaunch fLaunch; + private DsfSession fSession; + private String fProgram; + + // Service references, initialized when created and used in initializing other services. + private PDACommandControl fCommandControl; + private PDARunControl fRunControl; + + public PDAServicesInitSequence(DsfSession session, PDALaunch launch, String program, RequestMonitor rm) + { + super(session.getExecutor(), rm); + fLaunch = launch; + fSession = session; + fProgram = program; + } + + @Override + public Step[] getSteps() { + return fSteps; + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/launch/PDAServicesShutdownSequence.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/launch/PDAServicesShutdownSequence.java new file mode 100644 index 00000000000..aadd229547e --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/launch/PDAServicesShutdownSequence.java @@ -0,0 +1,142 @@ +/******************************************************************************* + * 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.examples.dsf.pda.launch; + +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Sequence; +import org.eclipse.cdt.dsf.debug.service.BreakpointsMediator; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.IDsfService; +import org.eclipse.cdt.examples.dsf.pda.PDAPlugin; +import org.eclipse.cdt.examples.dsf.pda.service.PDABreakpoints; +import org.eclipse.cdt.examples.dsf.pda.service.PDACommandControl; +import org.eclipse.cdt.examples.dsf.pda.service.PDAExpressions; +import org.eclipse.cdt.examples.dsf.pda.service.PDARegisters; +import org.eclipse.cdt.examples.dsf.pda.service.PDARunControl; +import org.eclipse.cdt.examples.dsf.pda.service.PDAStack; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +/** + * The shutdown sequence for PDA debugger services. This sequence contains + * the series of steps that are executed to properly shutdown the PDA-DSF debug + * session. If any of the individual steps fail, the shutdown will abort. + * <p> + * Services are shut down in the reverse order of initialization. + * </p> + */ +public class PDAServicesShutdownSequence extends Sequence { + + private final Step[] fSteps = new Step[] { + new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + // Initialize services tracker. + assert PDAPlugin.getBundleContext() != null; + fTracker = new DsfServicesTracker(PDAPlugin.getBundleContext(), fSessionId); + requestMonitor.done(); + } + + @Override + public void rollBack(RequestMonitor requestMonitor) { + // In case the shutdown sequence aborts, ensure that the + // tracker is properly disposed. + fTracker.dispose(); + fTracker = null; + requestMonitor.done(); + } + }, + new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + shutdownService(PDAExpressions.class, requestMonitor); + } + }, + new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + shutdownService(PDARegisters.class, requestMonitor); + } + }, + new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + shutdownService(PDAStack.class, requestMonitor); + } + }, + new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + shutdownService(BreakpointsMediator.class, requestMonitor); + } + }, new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + shutdownService(PDABreakpoints.class, requestMonitor); + } + }, + new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + shutdownService(PDARunControl.class, requestMonitor); + } + }, + new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + shutdownService(PDACommandControl.class, requestMonitor); + } + }, + new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + fTracker.dispose(); + fTracker = null; + requestMonitor.done(); + } + } + }; + + private String fSessionId; + private DsfServicesTracker fTracker; + + public PDAServicesShutdownSequence(DsfExecutor executor, String sessionId, RequestMonitor requestMonitor) { + super(executor, requestMonitor); + fSessionId = sessionId; + } + + @Override + public Step[] getSteps() { + return fSteps; + } + + @SuppressWarnings("unchecked") + private void shutdownService(Class clazz, final RequestMonitor requestMonitor) { + IDsfService service = (IDsfService)fTracker.getService(clazz); + if (service != null) { + service.shutdown(new RequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + PDAPlugin.getDefault().getLog().log(getStatus()); + } + requestMonitor.done(); + } + }); + } else { + requestMonitor.setStatus(new Status(IStatus.ERROR, PDAPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, + "Service '" + clazz.getName() + "' not found.", null)); //$NON-NLS-1$//$NON-NLS-2$ + requestMonitor.done(); + } + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDABackend.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDABackend.java new file mode 100644 index 00000000000..b391190894b --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDABackend.java @@ -0,0 +1,320 @@ +/******************************************************************************* + * Copyright (c) 2008 Nokia Corporation. + * All rights reserved. This fProgram and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Nokia - initial version. Sep 28, 2008 + *******************************************************************************/ + +package org.eclipse.cdt.examples.dsf.pda.service; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.UnknownHostException; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.List; + +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Sequence; +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +import org.eclipse.cdt.dsf.concurrent.Sequence.Step; +import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.examples.dsf.pda.PDAPlugin; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.Launch; +import org.osgi.framework.BundleContext; + +/** + * Service that manages the backend process: starting the process + * and monitoring for its shutdown. + */ +public class PDABackend extends AbstractDsfService { + + private int fRequestPort; + private int fEventPort; + + @ThreadSafe + private OutputStream fRequestOutputStream; + @ThreadSafe + private InputStream fRequestInputStream; + @ThreadSafe + private InputStream fEventInputStream; + + private String fProgram; + private Process fBackendProcess; + private String fBackendProcessName; + + /** + * + * @param session + * @param launch - can be null, e.g. for JUnit test. + * @param program - can be a full path or a workspace resource path, must denote an existing file. + */ + public PDABackend(DsfSession session, Launch launch, String program) { + super(session); + + fProgram = program; + } + + @Override + protected BundleContext getBundleContext() { + return PDAPlugin.getBundleContext(); + } + + public Process getProcess() { + return fBackendProcess; + } + + public String getProcessName() { + return fBackendProcessName; + } + + public String getPorgramName() { + return fProgram; + } + + @Override + public void initialize(final RequestMonitor rm) { + super.initialize(new RequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + doInitialize(rm); + } + }); + } + + private void doInitialize(final RequestMonitor requestMonitor) { + + final Sequence.Step[] initializeSteps = new Sequence.Step[] { + new Step() { + // Launch the back end debugger process. + + @Override + public void execute(final RequestMonitor rm) { + + new Job("Start PDA Virtual Machine") { + + @Override + protected IStatus run(IProgressMonitor monitor) { + try { + fBackendProcess = launchPDABackendDebugger(); + } catch (CoreException e) { + rm.setStatus(e.getStatus()); + } + rm.done(); + + return Status.OK_STATUS; + } + }.schedule(); + } + + @Override + public void rollBack(RequestMonitor rm) { + if (fBackendProcess != null) + fBackendProcess.destroy(); + + rm.done(); + } + + }, + new Step() { + @Override + public void execute(final RequestMonitor rm) { + + // To avoid blocking the DSF dispatch thread use a job to initialize communication sockets. + new Job("PDA Socket Initialize") { + @Override + protected IStatus run(IProgressMonitor monitor) { + try { + // give interpreter a chance to start + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + } + Socket socket = new Socket("localhost", fRequestPort); + fRequestOutputStream = socket.getOutputStream(); + fRequestInputStream = socket.getInputStream(); + // give interpreter a chance to open next socket + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + } + + socket = new Socket("localhost", fEventPort); + fEventInputStream = socket.getInputStream(); + + } catch (UnknownHostException e) { + rm.setStatus(new Status( + IStatus.ERROR, PDAPlugin.PLUGIN_ID, REQUEST_FAILED, "Unable to connect to PDA VM", e)); + } catch (IOException e) { + rm.setStatus(new Status( + IStatus.ERROR, PDAPlugin.PLUGIN_ID, REQUEST_FAILED, "Unable to connect to PDA VM", e)); + } + rm.done(); + + return Status.OK_STATUS; + } + }.schedule(); + } + }, + + new Step() { // register the service + @Override + public void execute(RequestMonitor rm) { + // Register this service + register(new String[] { PDABackend.class.getName() }, + new Hashtable<String, String>()); + + rm.done(); + } + }, + }; + + Sequence startupSequence = new Sequence(getExecutor(), requestMonitor) { + @Override public Step[] getSteps() { return initializeSteps; } + }; + getExecutor().execute(startupSequence); + } + + /** + * Returns a free port number on localhost, or -1 if unable to find a free port. + */ + public static int findFreePort() { + ServerSocket socket= null; + try { + socket= new ServerSocket(0); + return socket.getLocalPort(); + } catch (IOException e) { + } finally { + if (socket != null) { + try { + socket.close(); + } catch (IOException e) { + } + } + } + return -1; + } + + private void abort(String message, Throwable e) throws CoreException { + throw new CoreException(new Status(IStatus.ERROR, PDAPlugin.PLUGIN_ID, 0, message, e)); + } + + private Process launchPDABackendDebugger() throws CoreException { + + List<String> commandList = new ArrayList<String>(); + + // Get Java VM path + String javaVMHome = System.getProperty("java.home"); + String javaVMExec = javaVMHome + File.separatorChar + "bin" + File.separatorChar + "java"; + if (File.separatorChar == '\\') { + javaVMExec += ".exe"; + } + File exe = new File(javaVMExec); + if (!exe.exists()) { + abort(MessageFormat.format("Specified java VM executable {0} does not exist.", new Object[]{javaVMExec}), null); + } + + fBackendProcessName = javaVMExec; + + commandList.add(javaVMExec); + + commandList.add("-cp"); + commandList.add(File.pathSeparator + PDAPlugin.getFileInPlugin(new Path("bin"))); + + commandList.add("org.eclipse.cdt.examples.pdavm.PDAVirtualMachine"); + + String absolutePath = fProgram; + + // check if fProgram is already a full path of an existing file + // Note if "fProgram" is workspace resource path like /ProjectName/file.pda, we should not + // change it to absolute path, otherwise the breakpoints in the PDA file won't work. + // See PDABreakpoints.doInsertBreakpoint() for more. + File f = new File(fProgram); + if (! f.exists()) { + // Try to locate it in workspace + IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(fProgram)); + if (file.exists()) + absolutePath = file.getLocation().toPortableString(); + else + abort(MessageFormat.format("PDA program {0} does not exist.", new Object[] {file.getFullPath().toPortableString()}), null); + } + + commandList.add(absolutePath); + + fRequestPort = findFreePort(); + fEventPort = findFreePort(); + + if (fRequestPort == -1 || fEventPort == -1) { + abort("Unable to find free port", null); + } + + // Add debug arguments - i.e. '-debug fRequestPort fEventPort' + commandList.add("-debug"); + commandList.add("" + fRequestPort); + commandList.add("" + fEventPort); + + // Launch the perl process. + String[] commandLine = commandList.toArray(new String[commandList.size()]); + + PDAPlugin.debug("Start PDA Virtual Machine:\n" + commandList); + + Process process = DebugPlugin.exec(commandLine, null); + + return process; + } + + @Override + public void shutdown(final RequestMonitor rm) { + fBackendProcess.destroy(); + + try { + if (fRequestInputStream != null) + fRequestInputStream.close(); + if (fRequestOutputStream != null) + fRequestOutputStream.close(); + if (fEventInputStream != null) + fEventInputStream.close(); + } catch (IOException e) { + // ignore + } + + unregister(); + rm.done(); + } + + /* + * =========== Following are PDA debugger specific ==================== + * + * Caller should make sure these are called after the PDABackend is initialized. + */ + public OutputStream getRequestOutputStream() { + return fRequestOutputStream; + } + + public InputStream getRequestInputStream() { + return fRequestInputStream; + } + + public InputStream getEventInputStream() { + return fEventInputStream; + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDABreakpointAttributeTranslator.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDABreakpointAttributeTranslator.java new file mode 100644 index 00000000000..7bdb29b3887 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDABreakpointAttributeTranslator.java @@ -0,0 +1,135 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.cdt.dsf.debug.service.BreakpointsMediator; +import org.eclipse.cdt.dsf.debug.service.IBreakpointAttributeTranslator; +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointDMContext; +import org.eclipse.cdt.examples.dsf.pda.PDAPlugin; +import org.eclipse.cdt.examples.dsf.pda.breakpoints.PDALineBreakpoint; +import org.eclipse.cdt.examples.dsf.pda.breakpoints.PDAWatchpoint; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.model.IBreakpoint; + +/** + * Translator between {@link PDALineBreakpoint} object attributes and + * attributes used by the {@link PDABreakpoints} service. + * <p> + * The attribute translator is used by the standard {@link BreakpointsMediator} + * service to map between platform breakpoint attributes and target-side DSF + * breakpoint attributes. Thus, this object encapsulates the model-specific + * functionality of synchronizing target side and IDE-side breakpoint objects. + * </p> + */ +public class PDABreakpointAttributeTranslator implements IBreakpointAttributeTranslator { + + // Arrays of common attributes between the two breakpoint types. These + // attributes can be copied directly without translation. + private static final String[] fgPDALineBreakpointAttributes = { + IBreakpoint.ENABLED, + IMarker.LINE_NUMBER, + }; + private static final String[] fgPDAWatchpointAttributes = { + IBreakpoint.ENABLED, + PDAWatchpoint.FUNCTION_NAME, + PDAWatchpoint.VAR_NAME, + PDAWatchpoint.ACCESS, + PDAWatchpoint.MODIFICATION + }; + + // PDA breakpoints translator doesn't keep any state and it doesn't + // need to initialize or clean up. + public void initialize(BreakpointsMediator mediator) { + } + + public void dispose() { + } + + public List<Map<String, Object>> getBreakpointAttributes(IBreakpoint bp, boolean bpManagerEnabled) + throws CoreException + { + Map<String, Object> attrs = new HashMap<String, Object>(); + + // Check that the marker exists and retrieve its attributes. + // Due to accepted race conditions, the breakpiont marker may become null + // while this method is being invoked. In this case throw an exception + // and let the caller handle it. + IMarker marker = bp.getMarker(); + if (marker == null || !marker.exists()) { + throw new DebugException(new Status(IStatus.ERROR, PDAPlugin.PLUGIN_ID, DebugException.REQUEST_FAILED, "Breakpoint marker does not exist", null)); + } + // Suppress cast warning: platform is still on Java 1.3 + @SuppressWarnings("unchecked") + Map<String, Object> platformBpAttrs = marker.getAttributes(); + + // Copy breakpoint attributes. + if (bp instanceof PDAWatchpoint) { + attrs.put(PDABreakpoints.ATTR_BREAKPOINT_TYPE, PDABreakpoints.PDA_WATCHPOINT); + + copyAttributes(platformBpAttrs, attrs, fgPDAWatchpointAttributes); + } else if (bp instanceof PDALineBreakpoint) { + attrs.put(PDABreakpoints.ATTR_BREAKPOINT_TYPE, PDABreakpoints.PDA_LINE_BREAKPOINT); + attrs.put(PDABreakpoints.ATTR_PROGRAM_PATH, marker.getResource().getFullPath().toString()); + + copyAttributes(platformBpAttrs, attrs, fgPDALineBreakpointAttributes); + } + + // If the breakpoint manager is disabled, override the enabled attribute. + if (!bpManagerEnabled) { + attrs.put(IBreakpoint.ENABLED, false); + } + + // The breakpoint mediator allows for multiple target-side breakpoints + // to be created for each IDE breakpoint. Although in case of PDA this + // feature is never used, we still have to return a list of attributes. + List<Map<String, Object>> retVal = new ArrayList<Map<String, Object>>(1); + retVal.add(attrs); + return retVal; + } + + private void copyAttributes(Map<String, Object> srcMap, Map<String, Object> destMap, String[] attrs) { + for (String attr : attrs) { + if (srcMap.containsKey(attr)) { + destMap.put(attr, srcMap.get(attr)); + } + } + } + + public boolean canUpdateAttributes(IBreakpointDMContext bp, Map<String, Object> delta) { + // PDA debugger only allows updating of the action property of the watchpoint. + // All other breakpoint updates will require a re-installation. + if (bp instanceof PDAWatchpoint) { + Map<String, Object> deltaCopy = new HashMap<String, Object>(delta); + deltaCopy.remove(PDAWatchpoint.ACCESS); + deltaCopy.remove(PDAWatchpoint.MODIFICATION); + return !deltaCopy.isEmpty(); + } + return false; + } + + public boolean supportsBreakpoint(IBreakpoint bp) { + return bp.getModelIdentifier().equals(PDAPlugin.ID_PDA_DEBUG_MODEL); + } + + public void updateBreakpointStatus(IBreakpoint bp) { + // PDA breakpoints do not support status reporting + } + +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDABreakpoints.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDABreakpoints.java new file mode 100644 index 00000000000..0b219c95106 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDABreakpoints.java @@ -0,0 +1,403 @@ +/******************************************************************************* + * Copyright (c) 2007 Ericsson 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 - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.examples.dsf.pda.service; + +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Map; +import java.util.Set; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.AbstractDMContext; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IBreakpoints; +import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.examples.dsf.pda.PDAPlugin; +import org.eclipse.cdt.examples.dsf.pda.breakpoints.PDAWatchpoint; +import org.eclipse.cdt.examples.dsf.pda.service.commands.PDAClearBreakpointCommand; +import org.eclipse.cdt.examples.dsf.pda.service.commands.PDACommandResult; +import org.eclipse.cdt.examples.dsf.pda.service.commands.PDASetBreakpointCommand; +import org.eclipse.cdt.examples.dsf.pda.service.commands.PDAWatchCommand; +import org.eclipse.core.resources.IMarker; +import org.eclipse.debug.core.model.IBreakpoint; +import org.osgi.framework.BundleContext; + +/** + * Initial breakpoint service implementation. + * Implements the IBreakpoints interface. + */ +public class PDABreakpoints extends AbstractDsfService implements IBreakpoints +{ + /** + * Context representing a PDA line breakpoint. In PDA debugger, since there is only + * one file being debugged at a time, a breakpoint is uniquely identified using the + * line number only. + */ + @Immutable + private static class BreakpointDMContext extends AbstractDMContext implements IBreakpointDMContext { + + final Integer fLine; + + public BreakpointDMContext(String sessionId, PDAVirtualMachineDMContext commandControlCtx, Integer line) { + super(sessionId, new IDMContext[] { commandControlCtx }); + fLine = line; + } + + @Override + public boolean equals(Object obj) { + return baseEquals(obj) && (fLine.equals(((BreakpointDMContext) obj).fLine)); + } + + @Override + public int hashCode() { + return baseHashCode() + fLine.hashCode(); + } + + @Override + public String toString() { + return baseToString() + ".breakpoint(" + fLine + ")"; //$NON-NLS-1$//$NON-NLS-2$*/ + } + } + + /** + * Context representing a watch point. In PDA debugger, a watchpoint is + * uniquely identified using the function and variable. + */ + @Immutable + private static class WatchpointDMContext extends AbstractDMContext implements IBreakpointDMContext { + final String fFunction; + final String fVariable; + + public WatchpointDMContext(String sessionId, PDAVirtualMachineDMContext commandControlCtx, String function, + String variable) + { + super(sessionId, new IDMContext[] { commandControlCtx }); + fFunction = function; + fVariable = variable; + } + + @Override + public boolean equals(Object obj) { + if (baseEquals(obj)) { + WatchpointDMContext watchpointCtx = (WatchpointDMContext)obj; + return fFunction.equals(watchpointCtx.fFunction) && fVariable.equals(watchpointCtx.fVariable); + } + return false; + } + + @Override + public int hashCode() { + return baseHashCode() + fFunction.hashCode() + fVariable.hashCode(); + } + + @Override + public String toString() { + return baseToString() + ".watchpoint(" + fFunction + "::" + fVariable + ")"; + } + } + + // Attribute names + public static final String ATTR_BREAKPOINT_TYPE = PDAPlugin.PLUGIN_ID + ".pdaBreakpointType"; //$NON-NLS-1$ + public static final String PDA_LINE_BREAKPOINT = "breakpoint"; //$NON-NLS-1$ + public static final String PDA_WATCHPOINT = "watchpoint"; //$NON-NLS-1$ + public static final String ATTR_PROGRAM_PATH = PDAPlugin.PLUGIN_ID + ".pdaProgramPath"; //$NON-NLS-1$ + + // Services + private PDACommandControl fCommandControl; + + // Breakpoints currently installed + private Set<IBreakpointDMContext> fBreakpoints = new HashSet<IBreakpointDMContext>(); + + /** + * The service constructor + * + * @param session The debugging session this service belongs to. + */ + public PDABreakpoints(DsfSession session) { + super(session); + } + + @Override + public void initialize(final RequestMonitor rm) { + super.initialize(new RequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + doInitialize(rm); + } + }); + } + + private void doInitialize(final RequestMonitor rm) { + // Get the services references + fCommandControl = getServicesTracker().getService(PDACommandControl.class); + + // Register this service + register(new String[] { IBreakpoints.class.getName(), PDABreakpoints.class.getName() }, + new Hashtable<String, String>()); + + rm.done(); + } + + @Override + public void shutdown(final RequestMonitor rm) { + unregister(); + rm.done(); + } + + @Override + protected BundleContext getBundleContext() { + return PDAPlugin.getBundleContext(); + } + + public void getBreakpoints(final IBreakpointsTargetDMContext context, final DataRequestMonitor<IBreakpointDMContext[]> rm) { + // Validate the context + if (!fCommandControl.getContext().equals(context)) { + PDAPlugin.failRequest(rm, INVALID_HANDLE, "Invalid breakpoints target context"); + return; + } + + rm.setData(fBreakpoints.toArray(new IBreakpointDMContext[fBreakpoints.size()])); + rm.done(); + } + + public void getBreakpointDMData(IBreakpointDMContext dmc, DataRequestMonitor<IBreakpointDMData> rm) { + PDAPlugin.failRequest(rm, NOT_SUPPORTED, "Retrieving breakpoint data is not supported"); + } + + public void insertBreakpoint(IBreakpointsTargetDMContext context, Map<String, Object> attributes, + DataRequestMonitor<IBreakpointDMContext> rm) + { + Boolean enabled = (Boolean)attributes.get(IBreakpoint.ENABLED); + if (enabled != null && !enabled.booleanValue()) { + // If the breakpoint is disabled, just fail the request. + PDAPlugin.failRequest(rm, REQUEST_FAILED, "Breakpoint is disabled"); + } else { + String type = (String) attributes.get(ATTR_BREAKPOINT_TYPE); + + if (PDA_LINE_BREAKPOINT.equals(type)) { + // Retrieve the PDA program context from the context given in the + // argument. This service is typically only called by the + // breakpoints mediator, which was called with the program context + // in the services initialization sequence. So checking if + // programCtx != null is mostly a formality. + PDAVirtualMachineDMContext programCtx = DMContexts.getAncestorOfType(context, PDAVirtualMachineDMContext.class); + if (programCtx != null) { + doInsertBreakpoint(programCtx, attributes, rm); + } else { + PDAPlugin.failRequest(rm, INVALID_HANDLE, "Unknown breakpoint type"); + } + } + else if (PDA_WATCHPOINT.equals(type)) { + doInsertWatchpoint(attributes, rm); + } + else { + PDAPlugin.failRequest(rm, REQUEST_FAILED, "Unknown breakpoint type"); + } + } + } + + private void doInsertBreakpoint(PDAVirtualMachineDMContext programCtx, final Map<String, Object> attributes, final DataRequestMonitor<IBreakpointDMContext> rm) + { + // Compare the program path in the breakpoint with the path in the PDA + // program context. Only insert the breakpoint if the program matches. + String program = (String)attributes.get(ATTR_PROGRAM_PATH); + if (!programCtx.getProgram().equals(program)) { + PDAPlugin.failRequest(rm, REQUEST_FAILED, "Invalid file name"); + return; + } + + // Retrieve the line. + Integer line = (Integer)attributes.get(IMarker.LINE_NUMBER); + if (line == null) { + PDAPlugin.failRequest(rm, REQUEST_FAILED, "No breakpoint line specified"); + return; + } + + // Create a new breakpoint context object and check that it's not + // installed already. PDA can only track a single breakpoint at a + // given line, attempting to set the second breakpoint should fail. + final BreakpointDMContext breakpointCtx = + new BreakpointDMContext(getSession().getId(), fCommandControl.getContext(), line); + if (fBreakpoints.contains(breakpointCtx)) { + PDAPlugin.failRequest(rm, REQUEST_FAILED, "Breakpoint already set"); + return; + } + + // Add the new breakpoint context to the list of known breakpoints. + // Adding it here, before the set command is completed will prevent + // a possibility of a second breakpoint being installed in the same + // location while this breakpoint is being processed. It will also + // allow the breakpoint to be removed or updated even while it is + // still being processed here. + fBreakpoints.add(breakpointCtx); + fCommandControl.queueCommand( + new PDASetBreakpointCommand(fCommandControl.getContext(), line, false), + new DataRequestMonitor<PDACommandResult>(getExecutor(), rm) { + @Override + protected void handleSuccess() { + rm.setData(breakpointCtx); + rm.done(); + } + + @Override + protected void handleFailure() { + // If inserting of the breakpoint failed, remove it from + // the set of installed breakpoints. + fBreakpoints.remove(breakpointCtx); + super.handleFailure(); + } + }); + } + + private void doInsertWatchpoint(final Map<String, Object> attributes, final DataRequestMonitor<IBreakpointDMContext> rm) + { + String function = (String)attributes.get(PDAWatchpoint.FUNCTION_NAME); + if (function == null) { + PDAPlugin.failRequest(rm, REQUEST_FAILED, "No function specified"); + return; + } + + String variable = (String)attributes.get(PDAWatchpoint.VAR_NAME); + if (variable == null) { + PDAPlugin.failRequest(rm, REQUEST_FAILED, "No variable specified"); + return; + } + + Boolean isAccess = (Boolean)attributes.get(PDAWatchpoint.ACCESS); + isAccess = isAccess != null ? isAccess : Boolean.FALSE; + + Boolean isModification = (Boolean)attributes.get(PDAWatchpoint.MODIFICATION); + isModification = isModification != null ? isModification : Boolean.FALSE; + + // Create a new watchpoint context object and check that it's not + // installed already. PDA can only track a single watchpoint for a given + // function::variable, attempting to set the second breakpoint should fail. + final WatchpointDMContext watchpointCtx = + new WatchpointDMContext(getSession().getId(), fCommandControl.getContext(), function, variable); + if (fBreakpoints.contains(watchpointCtx)) { + PDAPlugin.failRequest(rm, REQUEST_FAILED, "Watchpoint already set"); + return; + } + + // Determine the watch operation to perform. + PDAWatchCommand.WatchOperation watchOperation = PDAWatchCommand.WatchOperation.NONE; + if (isAccess && isModification) { + watchOperation = PDAWatchCommand.WatchOperation.BOTH; + } else if (isAccess) { + watchOperation = PDAWatchCommand.WatchOperation.READ; + } else if (isModification) { + watchOperation = PDAWatchCommand.WatchOperation.WRITE; + } + + // Add the new breakpoint context to the list of known breakpoints. + // Adding it here, before the set command is completed will prevent + // a possibility of a second breakpoint being installed in the same + // location while this breakpoint is being processed. It will also + // allow the breakpoint to be removed or updated even while it is + // still being processed here. + fBreakpoints.add(watchpointCtx); + fCommandControl.queueCommand( + new PDAWatchCommand(fCommandControl.getContext(), function, variable, watchOperation), + new DataRequestMonitor<PDACommandResult>(getExecutor(), rm) { + @Override + protected void handleSuccess() { + rm.setData(watchpointCtx); + rm.done(); + } + + @Override + protected void handleFailure() { + // Since the command failed, we need to remove the breakpoint from + // the existing breakpoint set. + fBreakpoints.remove(watchpointCtx); + super.handleFailure(); + } + }); + } + + public void removeBreakpoint(IBreakpointDMContext bpCtx, RequestMonitor rm) { + if (!fBreakpoints.contains(bpCtx)) { + PDAPlugin.failRequest(rm, REQUEST_FAILED, "Breakpoint already removed"); + return; + } + + if (bpCtx instanceof BreakpointDMContext) { + doRemoveBreakpoint((BreakpointDMContext)bpCtx, rm); + } else if (bpCtx instanceof WatchpointDMContext) { + doRemoveWatchpoint((WatchpointDMContext)bpCtx, rm); + } else { + PDAPlugin.failRequest(rm, INVALID_HANDLE, "Invalid breakpoint"); + } + } + + private void doRemoveBreakpoint(BreakpointDMContext bpCtx, RequestMonitor rm) { + // Remove the breakpoint from the table right away, so that even when + // the remove is being processed, a new breakpoint can be created at the same + // location. + fBreakpoints.remove(bpCtx); + + fCommandControl.queueCommand( + new PDAClearBreakpointCommand(fCommandControl.getContext(), bpCtx.fLine), + new DataRequestMonitor<PDACommandResult>(getExecutor(), rm)); + } + + private void doRemoveWatchpoint(WatchpointDMContext bpCtx, RequestMonitor rm) { + fBreakpoints.remove(bpCtx); + + // Watchpoints are cleared using the same command, but with a "no watch" operation + fCommandControl.queueCommand( + new PDAWatchCommand( + fCommandControl.getContext(), bpCtx.fFunction, bpCtx.fVariable, PDAWatchCommand.WatchOperation.NONE), + new DataRequestMonitor<PDACommandResult>(getExecutor(), rm)); + } + + public void updateBreakpoint(final IBreakpointDMContext bpCtx, Map<String, Object> attributes, final RequestMonitor rm) { + if (!fBreakpoints.contains(bpCtx)) { + PDAPlugin.failRequest(rm, REQUEST_FAILED, "Breakpoint not installed"); + return; + } + + if (bpCtx instanceof BreakpointDMContext) { + PDAPlugin.failRequest(rm, NOT_SUPPORTED, "Modifying PDA breakpoints is not supported"); + } else if (bpCtx instanceof WatchpointDMContext) { + WatchpointDMContext wpCtx = (WatchpointDMContext)bpCtx; + if (!wpCtx.fFunction.equals(attributes.get(PDAWatchpoint.FUNCTION_NAME)) || + !wpCtx.fVariable.equals(attributes.get(PDAWatchpoint.VAR_NAME)) ) + { + PDAPlugin.failRequest(rm, REQUEST_FAILED, "Cannot modify watchpoint function or variable"); + return; + } + + // PDA debugger can only track one watchpoint in the same location, + // so we can simply remove the existing context from the set and + // call insert again. + fBreakpoints.remove(bpCtx); + doInsertWatchpoint( + attributes, + new DataRequestMonitor<IBreakpointDMContext>(getExecutor(), rm) { + @Override + protected void handleSuccess() { + // The inserted watchpoint context will equal the + // current context. + assert bpCtx.equals(getData()); + rm.done(); + } + }); + } else { + PDAPlugin.failRequest(rm, INVALID_HANDLE, "Invalid breakpoint"); + } + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDACommandControl.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDACommandControl.java new file mode 100644 index 00000000000..bfa653ba01a --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDACommandControl.java @@ -0,0 +1,520 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +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.concurrent.ThreadSafe; +import org.eclipse.cdt.dsf.debug.service.command.ICommand; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControl; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; +import org.eclipse.cdt.dsf.debug.service.command.ICommandListener; +import org.eclipse.cdt.dsf.debug.service.command.ICommandResult; +import org.eclipse.cdt.dsf.debug.service.command.ICommandToken; +import org.eclipse.cdt.dsf.debug.service.command.IEventListener; +import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.examples.dsf.pda.PDAPlugin; +import org.eclipse.cdt.examples.dsf.pda.service.commands.AbstractPDACommand; +import org.eclipse.cdt.examples.dsf.pda.service.commands.PDACommandResult; +import org.eclipse.cdt.examples.dsf.pda.service.commands.PDAExitCommand; +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.osgi.framework.BundleContext; + + +/** + * Service that handles communication with a PDA debugger back end. + */ +public class PDACommandControl extends AbstractDsfService implements ICommandControlService { + + // Structure used to store command information in services internal queues. + private static class CommandHandle { + final private ICommandToken fToken; + final private AbstractPDACommand<PDACommandResult> fCommand; + final private DataRequestMonitor<PDACommandResult> fRequestMonitor; + + CommandHandle(ICommandToken token, AbstractPDACommand<PDACommandResult> c, DataRequestMonitor<PDACommandResult> rm) { + fToken = token; + fCommand = c; + fRequestMonitor = rm; + } + } + + private PDABackend fBackend; + + // Queue of commands waiting to be sent to the debugger. As long as commands + // are in this queue, they can still be removed by clients. + private final List<CommandHandle> fCommandQueue = new LinkedList<CommandHandle>(); + + // Queue of commands that are being sent to the debugger. This queue is read + // by the send job, so as soon as commands are inserted into this queue, they can + // be considered as sent. + @ThreadSafe + private final BlockingQueue<CommandHandle> fTxCommands = new LinkedBlockingQueue<CommandHandle>(); + + // Flag indicating that the PDA debugger started + private boolean fStarted = false; + + // Flag indicating that the PDA debugger has been disconnected + @ThreadSafe + private boolean fTerminated = false; + + // Data Model context of this command control. + private PDAVirtualMachineDMContext fDMContext; + + // Synchronous listeners for commands and events. + private final List<ICommandListener> fCommandListeners = new ArrayList<ICommandListener>(); + private final List<IEventListener> fEventListeners = new ArrayList<IEventListener>(); + + // Sockets for communicating with PDA debugger + @ThreadSafe + private PrintWriter fRequestWriter; + @ThreadSafe + private BufferedReader fRequestReader; + @ThreadSafe + private BufferedReader fEventReader; + + // Jobs servicing the sockets. + private EventDispatchJob fEventDispatchJob; + private CommandSendJob fCommandSendJob; + + /** + * Command control constructor. + * @param session The DSF session that this service is a part of. + */ + public PDACommandControl(DsfSession session) { + super(session); + } + + @Override + public void initialize(final RequestMonitor rm) { + // Call the super-class to perform initialization first. + super.initialize( new RequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + doInitialize(rm); + } + }); + } + + private void doInitialize(final RequestMonitor rm) { + fBackend = getServicesTracker().getService(PDABackend.class); + + // Create the control's data model context. + fDMContext = new PDAVirtualMachineDMContext(getSession().getId(), fBackend.getPorgramName()); + + // Add a listener for PDA events to track the started/terminated state. + addEventListener(new IEventListener() { + public void eventReceived(Object output) { + if ("started 1".equals(output)) { + setStarted(); + } else if ("terminated".equals(output)) { + setTerminated(); + } + } + }); + + // Get intput/output streams from the backend service. + // + fRequestWriter = new PrintWriter(fBackend.getRequestOutputStream()); + fRequestReader = new BufferedReader(new InputStreamReader(fBackend.getRequestInputStream())); + fEventReader = new BufferedReader(new InputStreamReader(fBackend.getEventInputStream())); + + fEventDispatchJob = new EventDispatchJob(); + fEventDispatchJob.schedule(); + + fCommandSendJob = new CommandSendJob(); + fCommandSendJob.schedule(); + + // Register the service with OSGi as the last step in initialization of + // the service. + register( + new String[]{ ICommandControl.class.getName(), PDACommandControl.class.getName() }, + new Hashtable<String,String>()); + + rm.done(); + } + + @Override + public void shutdown(final RequestMonitor requestMonitor) { + // Unregister the service first, so that clients may no longer gain access to it. + unregister(); + + if (!isTerminated()) { + // If the debugger is still connected, send it the exit command. + terminate(new RequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleCompleted() { + // Mark the command control as terminated. + setTerminated(); + + // Ignore any error resulting from the exit command. + // Errors will most likely result if the PDA process is + // already terminated. + requestMonitor.done(); + } + }); + } else { + requestMonitor.done(); + } + } + + @Override + protected BundleContext getBundleContext() { + return PDAPlugin.getBundleContext(); + } + + /** + * Job that services the send command queue. + */ + private class CommandSendJob extends Job { + CommandSendJob() { + super("PDA Command Send"); + setSystem(true); + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + while (!isTerminated()) { + synchronized (fTxCommands) { + try { + // Remove command from send queue. + final CommandHandle commandHandle = fTxCommands.take(); + + // Send the request to PDA + fRequestWriter.println(commandHandle.fCommand.getRequest()); + fRequestWriter.flush(); + + try { + // wait for reply + final String response = fRequestReader.readLine(); + + // Process the reply in the executor thread. + try { + getExecutor().execute(new DsfRunnable() { + public void run() { + processCommandDone(commandHandle, response); + } + }); + } catch (RejectedExecutionException e) { + // Acceptable race condition may see the session shut down + // while we're waiting for command response. Still complete + // the request monitor. + assert isTerminated(); + assert isTerminated(); + PDAPlugin.failRequest(commandHandle.fRequestMonitor, REQUEST_FAILED, "Command control shut down."); + } + } catch (final IOException e) { + // Process error it in the executor thread + try { + getExecutor().execute(new DsfRunnable() { + public void run() { + processCommandException(commandHandle, e); + } + }); + } catch (RejectedExecutionException re) { + // Acceptable race condition... see above + assert isTerminated(); + PDAPlugin.failRequest(commandHandle.fRequestMonitor, REQUEST_FAILED, "Command control shut down."); + } + } + } catch (InterruptedException e) { + break; // Shutting down. + } + } + } + return Status.OK_STATUS; + } + + } + + /** + * Job that services the PDA event socket. + */ + class EventDispatchJob extends Job { + + public EventDispatchJob() { + super("PDA Event Listner"); + setSystem(true); + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + while (!isTerminated()) { + try { + // Wait for an event. + final String event = fEventReader.readLine(); + if (event != null) { + try { + // Process the event in executor thread. + getExecutor().execute(new DsfRunnable() { + public void run() { + processEventReceived(event); + } + }); + } catch (RejectedExecutionException e) {} + } else { + break; + } + } catch (IOException e) { + break; + } + } + if (!isTerminated()) { + // Exception from the event socket is an indicator that the PDA debugger + // has exited. Call setTerminated() in executor thread. + try { + getExecutor().execute(new DsfRunnable() { + public void run() { + setTerminated(); + } + }); + } catch (RejectedExecutionException e) {} + } + return Status.OK_STATUS; + } + + } + + public <V extends ICommandResult> ICommandToken queueCommand(final ICommand<V> command, DataRequestMonitor<V> rm) { + ICommandToken token = new ICommandToken() { + public ICommand<?> getCommand() { + return command; + } + }; + + if (command instanceof AbstractPDACommand<?>) { + // Cast from command with "<V extends ICommandResult>" to a more concrete + // type to use internally in the command control. + @SuppressWarnings("unchecked") + AbstractPDACommand<PDACommandResult> pdaCommand = (AbstractPDACommand<PDACommandResult>)command; + + // Similarly, cast the request monitor to a more concrete type. + @SuppressWarnings("unchecked") + DataRequestMonitor<PDACommandResult> pdaRM = (DataRequestMonitor<PDACommandResult>)rm; + + // Add the command to the queue and notify command listeners. + fCommandQueue.add( new CommandHandle(token, pdaCommand, pdaRM) ); + for (ICommandListener listener : fCommandListeners) { + listener.commandQueued(token); + } + + // In a separate dispatch cycle. This allows command listeners to respond to the + // command queued event. + getExecutor().execute(new DsfRunnable() { + public void run() { + processQueues(); + } + }); + } else { + PDAPlugin.failRequest(rm, INTERNAL_ERROR, "Unrecognized command: " + command); + } + return token; + } + + public void removeCommand(ICommandToken token) { + // Removes given command from the queue and notify the listeners + for (Iterator<CommandHandle> itr = fCommandQueue.iterator(); itr.hasNext();) { + CommandHandle handle = itr.next(); + if (token.equals(handle.fToken)) { + itr.remove(); + for (ICommandListener listener : fCommandListeners) { + listener.commandRemoved(token); + } + } + } + } + + public void addCommandListener(ICommandListener processor) { + fCommandListeners.add(processor); + } + + public void removeCommandListener(ICommandListener processor) { + fCommandListeners.remove(processor); + } + + public void addEventListener(IEventListener processor) { + fEventListeners.add(processor); + } + + public void removeEventListener(IEventListener processor) { + fEventListeners.remove(processor); + } + + private void processCommandDone(CommandHandle handle, String response) { + // Trace to debug output. + PDAPlugin.debug("R: " + response); + + PDACommandResult result = null; + + if (response.startsWith("error:")) { + // Create a generic result with the error response + result = new PDACommandResult(response); + + // Set the error status to the request monitor. + handle.fRequestMonitor.setStatus(new Status( + IStatus.ERROR, PDAPlugin.PLUGIN_ID, + IDsfStatusConstants.REQUEST_FAILED, response, null)); + } else { + // Given the PDA response string, create the result using the command + // that was sent. + result = handle.fCommand.createResult(response); + + // Set the result to the request monitor and return to sender. + // Note: as long as PDA sends some response, a PDA command will never + // return an error. + handle.fRequestMonitor.setData(result); + } + handle.fRequestMonitor.done(); + + // Notify listeners of the response + for (ICommandListener listener : fCommandListeners) { + listener.commandDone(handle.fToken, result); + } + + // Process next command in queue. + processQueues(); + } + + + private void processCommandException(CommandHandle handle, Throwable exception) { + + // If sending a command resulted in an exception, notify the client. + handle.fRequestMonitor.setStatus(new Status( + IStatus.ERROR, PDAPlugin.PLUGIN_ID, REQUEST_FAILED, "Exception reading request response", exception)); + handle.fRequestMonitor.done(); + + // Notify listeners also. + for (ICommandListener listener : fCommandListeners) { + listener.commandDone(handle.fToken, null); + } + } + + private void processEventReceived(String event) { + // Notify the listeners only. + PDAPlugin.debug("E: " + event); + for (IEventListener listener : fEventListeners) { + listener.eventReceived(event); + } + } + + private synchronized void processQueues() { + if (isTerminated()) { + // If the PDA debugger is terminated. Return all submitted commands + // with an error. + for (CommandHandle handle : fCommandQueue) { + handle.fRequestMonitor.setStatus(new Status( + IStatus.ERROR, PDAPlugin.PLUGIN_ID, INVALID_STATE, "Command control is terminated", null)); + handle.fRequestMonitor.done(); + } + fCommandQueue.clear(); + } else if (fStarted && fTxCommands.isEmpty() && !fCommandQueue.isEmpty()) { + // Process the queues if: + // - the PDA debugger has started, + // - there are no pending commands in the send queue, + // - and there are commands waiting to be sent. + CommandHandle handle = fCommandQueue.remove(0); + fTxCommands.add(handle); + PDAPlugin.debug("C: " + handle.fCommand.getRequest()); + for (ICommandListener listener : fCommandListeners) { + listener.commandSent(handle.fToken); + } + } + } + + /** + * Return the PDA Debugger top-level Data Model context. + * @see PDAVirtualMachineDMContext + */ + @ThreadSafe + public PDAVirtualMachineDMContext getContext() { + return fDMContext; + } + + public String getId() { + return fBackend.getPorgramName(); + } + + private void setStarted() { + // Mark the command control as started and ready to process commands. + fStarted = true; + + // Process any commands which may have been queued before the + processQueues(); + + // Issue a data model event. + getSession().dispatchEvent(new PDAStartedEvent(getContext()), getProperties()); + } + + /** + * Returns whether the PDA debugger has started and is processing commands. + */ + public boolean isActive() { + return fStarted && !isTerminated(); + } + + + @ThreadSafe + private synchronized void setTerminated() { + // Set terminated may be called more than once: by event listener thread, + // by the terminate command, etc, so protect against sending events multiple + // times. + if (!fTerminated) { + fTerminated = true; + + // Process any waiting commands, they all should return with an error. + processQueues(); + + // Issue a data model event. + getSession().dispatchEvent(new PDATerminatedEvent(getContext()), getProperties()); + } + } + + /** + * Returns whether the PDA debugger has been terminated. + */ + @ThreadSafe + public synchronized boolean isTerminated() { + return fTerminated; + } + + /** + * Sends a command to PDA debugger to terminate. + */ + public void terminate(RequestMonitor rm) { + if (!isTerminated()) { + queueCommand( + new PDAExitCommand(fDMContext), + new DataRequestMonitor<PDACommandResult>(getExecutor(), rm)); + } else { + // If already terminated, indicate success. + rm.done(); + } + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDAExpressions.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDAExpressions.java new file mode 100644 index 00000000000..d7991483bc7 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDAExpressions.java @@ -0,0 +1,554 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service; + +import java.util.Hashtable; +import java.util.Map; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.AbstractDMContext; +import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent; +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.IExpressions; +import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMContext; +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.IStack.IFrameDMContext; +import org.eclipse.cdt.dsf.debug.service.command.CommandCache; +import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.examples.dsf.pda.PDAPlugin; +import org.eclipse.cdt.examples.dsf.pda.service.commands.PDAChildrenCommand; +import org.eclipse.cdt.examples.dsf.pda.service.commands.PDACommandResult; +import org.eclipse.cdt.examples.dsf.pda.service.commands.PDAListResult; +import org.eclipse.cdt.examples.dsf.pda.service.commands.PDASetVarCommand; +import org.eclipse.cdt.examples.dsf.pda.service.commands.PDAVarCommand; +import org.osgi.framework.BundleContext; + +/** + * + */ +public class PDAExpressions extends AbstractDsfService implements ICachingService, IExpressions { + + @Immutable + private static class ExpressionDMContext extends AbstractDMContext implements IExpressionDMContext { + + final private String fExpression; + + ExpressionDMContext(String sessionId, IFrameDMContext frameDmc, String expressin) { + super(sessionId, new IDMContext[] { frameDmc }); + fExpression = expressin; + } + + public String getExpression() { + return fExpression; + } + + @Override + public boolean equals(Object other) { + return super.baseEquals(other) && ((ExpressionDMContext)other).fExpression.equals(fExpression); + } + + @Override + public int hashCode() { + return super.baseHashCode() + fExpression.hashCode(); + } + + @Override + public String toString() { + return baseToString() + ".expression(" + fExpression + ")"; + } + } + + /** + * PDA expressions are simply variables. Only the variable name + * is relevant for its data. + */ + @Immutable + private static class ExpressionDMData implements IExpressionDMData { + + final private String fExpression; + + public ExpressionDMData(String expression) { + fExpression = expression; + } + + public BasicType getBasicType() { + return BasicType.basic; + } + + public String getEncoding() { + return null; + } + + public Map<String, Integer> getEnumerations() { + return null; + } + + public String getName() { + return fExpression; + } + + public IRegisterDMContext getRegister() { + return null; + } + + public String getStringValue() { + return null; + } + + public String getTypeId() { + return null; + } + + public String getTypeName() { + return null; + } + + } + + // @see #createExpression() + @Immutable + private static class InvalidExpressionDMContext extends AbstractDMContext implements IExpressionDMContext { + final private String fExpression; + + public InvalidExpressionDMContext(String sessionId, IDMContext parent, String expr) { + super(sessionId, new IDMContext[] { parent }); + fExpression = expr; + } + + @Override + public boolean equals(Object other) { + return super.baseEquals(other) && + fExpression == null + ? ((InvalidExpressionDMContext) other).getExpression() == null + : fExpression.equals(((InvalidExpressionDMContext) other).getExpression()); + } + + @Override + public int hashCode() { + return fExpression == null ? super.baseHashCode() : super.baseHashCode() ^ fExpression.hashCode(); + } + + @Override + public String toString() { + return baseToString() + ".invalid_expr[" + fExpression + "]"; + } + + public String getExpression() { + return fExpression; + } + } + + @Immutable + private static class ExpressionChangedDMEvent extends AbstractDMEvent<IExpressionDMContext> + implements IExpressionChangedDMEvent + { + ExpressionChangedDMEvent(IExpressionDMContext expression) { + super(expression); + } + } + + + private PDACommandControl fCommandControl; + private PDAStack fStack; + + private CommandCache fCommandCache; + + public PDAExpressions(DsfSession session) { + super(session); + } + + @Override + protected BundleContext getBundleContext() { + return PDAPlugin.getBundleContext(); + } + + @Override + public void initialize(final RequestMonitor rm) { + super.initialize( + new RequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + doInitialize(rm); + }}); + } + + private void doInitialize(final RequestMonitor rm) { + fCommandControl = getServicesTracker().getService(PDACommandControl.class); + fStack = getServicesTracker().getService(PDAStack.class); + fCommandCache = new CommandCache(getSession(), fCommandControl); + + getSession().addServiceEventListener(this, null); + + register(new String[]{IExpressions.class.getName(), PDAExpressions.class.getName()}, new Hashtable<String,String>()); + + rm.done(); + } + + @Override + public void shutdown(final RequestMonitor rm) { + getSession().removeServiceEventListener(this); + fCommandCache.reset(); + super.shutdown(rm); + } + + public void canWriteExpression(IExpressionDMContext expressionContext, DataRequestMonitor<Boolean> rm) { + rm.setData(true); + rm.done(); + } + + public IExpressionDMContext createExpression(IDMContext ctx, String expression) { + // Create an expression based on the given context and string expression. + PDAThreadDMContext threadCtx = DMContexts.getAncestorOfType(ctx, PDAThreadDMContext.class); + if (threadCtx != null) { + // The PDA debugger can only evaluate variables as expressions and only + // in context of a frame, so if a frame is not given, create a top-level frame. + IFrameDMContext frameCtx = DMContexts.getAncestorOfType(ctx, IFrameDMContext.class); + if (frameCtx == null) { + frameCtx = fStack.getFrameDMContext(threadCtx, 0); + } + + return new ExpressionDMContext(getSession().getId(), frameCtx, expression); + } + + // If the thread cannot be found in context, return an "invalid" + // expression context, because a null return value is not allowed. + // Evaluating an invalid expression context will always yield an + // error. + return new InvalidExpressionDMContext(getSession().getId(), ctx, expression); + } + + public void getBaseExpressions(IExpressionDMContext exprContext, DataRequestMonitor<IExpressionDMContext[]> rm) { + PDAPlugin.failRequest(rm, NOT_SUPPORTED, "Not supported"); + } + + public void getExpressionAddressData(IExpressionDMContext dmc, DataRequestMonitor<IExpressionDMAddress> rm) { + PDAPlugin.failRequest(rm, NOT_SUPPORTED, "Not supported"); + } + + public void getExpressionData(final IExpressionDMContext exprCtx, final DataRequestMonitor<IExpressionDMData> rm) { + // Since expression data doesn't contain any more information than the + // context, it doesn't require any debugger commmands. + if (exprCtx instanceof ExpressionDMContext) { + rm.setData(new ExpressionDMData(exprCtx.getExpression())); + rm.done(); + } else { + PDAPlugin.failRequest(rm, INVALID_HANDLE, "Invalid expression context " + exprCtx); + } + } + + public void getSubExpressionCount(final IExpressionDMContext exprCtx, final DataRequestMonitor<Integer> rm) { + if (exprCtx instanceof ExpressionDMContext) { + final PDAThreadDMContext threadCtx = DMContexts.getAncestorOfType(exprCtx, PDAThreadDMContext.class); + final IFrameDMContext frameCtx = DMContexts.getAncestorOfType(exprCtx, IFrameDMContext.class); + + // First retrieve the stack depth, needed to properly calculate + // the frame index that is used by the PDAVarCommand. + fStack.getStackDepth( + frameCtx, 0, + new DataRequestMonitor<Integer>(getExecutor(), rm) { + @Override + protected void handleSuccess() { + // Calculate the frame index. + int frameId = getData() - frameCtx.getLevel() - 1; + + // Send the command to evaluate the variable. + fCommandCache.execute( + new PDAChildrenCommand(threadCtx, frameId, exprCtx.getExpression()), + new DataRequestMonitor<PDAListResult>(getExecutor(), rm) { + @Override + protected void handleSuccess() { + rm.setData(getData().fValues.length); + rm.done(); + } + }); + } + }); + } else { + PDAPlugin.failRequest(rm, INVALID_HANDLE, "Invalid context"); + } + } + + public void getSubExpressions(IExpressionDMContext exprCtx, DataRequestMonitor<IExpressionDMContext[]> rm) { + getSubExpressions(exprCtx, -1, -1, rm); + } + + public void getSubExpressions(final IExpressionDMContext exprCtx, final int startIndexArg, final int lengthArg, + final DataRequestMonitor<IExpressionDMContext[]> rm) + { + if (exprCtx instanceof ExpressionDMContext) { + final PDAThreadDMContext threadCtx = DMContexts.getAncestorOfType(exprCtx, PDAThreadDMContext.class); + final IFrameDMContext frameCtx = DMContexts.getAncestorOfType(exprCtx, IFrameDMContext.class); + + // First retrieve the stack depth, needed to properly calculate + // the frame index that is used by the PDAVarCommand. + fStack.getStackDepth( + frameCtx, 0, + new DataRequestMonitor<Integer>(getExecutor(), rm) { + @Override + protected void handleSuccess() { + // Calculate the frame index. + int frameId = getData() - frameCtx.getLevel() - 1; + + // Send the command to evaluate the variable. + fCommandCache.execute( + new PDAChildrenCommand(threadCtx, frameId, exprCtx.getExpression()), + new DataRequestMonitor<PDAListResult>(getExecutor(), rm) { + @Override + protected void handleSuccess() { + int start = startIndexArg > 0 ? startIndexArg : 0; + int end = lengthArg > 0 ? (start + lengthArg) : getData().fValues.length; + IExpressionDMContext[] contexts = new IExpressionDMContext[end - start]; + for (int i = start; i < end && i < getData().fValues.length; i++) { + contexts[i] = new ExpressionDMContext( + getSession().getId(), frameCtx, getData().fValues[i]); + } + rm.setData(contexts); + rm.done(); + } + }); + } + }); + } else { + PDAPlugin.failRequest(rm, INVALID_HANDLE, "Invalid context"); + } + } + + public void getAvailableFormats(IFormattedDataDMContext dmc, final DataRequestMonitor<String[]> rm) { + getFormattedExpressionValue( + new FormattedValueDMContext(this, dmc, NATURAL_FORMAT), + new DataRequestMonitor<FormattedValueDMData>(getExecutor(), rm) { + @Override + protected void handleSuccess() { + try { + Integer.parseInt(getData().getFormattedValue()); + rm.setData(new String[] { NATURAL_FORMAT, STRING_FORMAT, HEX_FORMAT, DECIMAL_FORMAT, OCTAL_FORMAT, BINARY_FORMAT }); + rm.done(); + } catch (NumberFormatException e) { + rm.setData(new String[] { NATURAL_FORMAT, STRING_FORMAT }); + rm.done(); + } + } + + @Override + protected void handleErrorOrWarning() { + rm.setData(new String[] { NATURAL_FORMAT, STRING_FORMAT }); + rm.done(); + } + }); + } + + public FormattedValueDMContext getFormattedValueContext(IFormattedDataDMContext exprCtx, String formatId) { + // Creates a context that can be used to retrieve a formatted value. + return new FormattedValueDMContext(this, exprCtx, formatId); + } + + public void getFormattedExpressionValue(FormattedValueDMContext formattedCtx, + final DataRequestMonitor<FormattedValueDMData> rm) + { + final String formatId = formattedCtx.getFormatID(); + final ExpressionDMContext exprCtx = DMContexts.getAncestorOfType(formattedCtx, ExpressionDMContext.class); + if (exprCtx != null) { + final PDAThreadDMContext threadCtx = DMContexts.getAncestorOfType(exprCtx, PDAThreadDMContext.class); + final IFrameDMContext frameCtx = DMContexts.getAncestorOfType(exprCtx, IFrameDMContext.class); + + // First retrieve the stack depth, needed to properly calculate + // the frame index that is used by the PDAVarCommand. + fStack.getStackDepth( + frameCtx, 0, + new DataRequestMonitor<Integer>(getExecutor(), rm) { + @Override + protected void handleSuccess() { + // Calculate the frame index. + int frameId = getData() - frameCtx.getLevel() - 1; + + // Send the command to evaluate the variable. + fCommandCache.execute( + new PDAVarCommand(threadCtx, frameId, exprCtx.getExpression()), + new DataRequestMonitor<PDACommandResult>(getExecutor(), rm) { + @Override + protected void handleSuccess() { + if (NATURAL_FORMAT.equals(formatId) || STRING_FORMAT.equals(formatId)) { + rm.setData(new FormattedValueDMData(getData().fResponseText)); + rm.done(); + } else { + int result; + try { + int intResult = Integer.parseInt(getData().fResponseText); + String formattedResult = ""; + if (HEX_FORMAT.equals(formatId)) { + formattedResult = Integer.toHexString(intResult); + StringBuffer prefix = new StringBuffer("0x"); + for (int i = 0; i < 8 - formattedResult.length(); i++) { + prefix.append('0'); + } + prefix.append(formattedResult); + formattedResult = prefix.toString(); + } else if (OCTAL_FORMAT.equals(formatId)) { + formattedResult = Integer.toOctalString(intResult); + StringBuffer prefix = new StringBuffer("0c"); + for (int i = 0; i < 16 - formattedResult.length(); i++) { + prefix.append('0'); + } + prefix.append(formattedResult); + formattedResult = prefix.toString(); + } else if (BINARY_FORMAT.equals(formatId)) { + formattedResult = Integer.toBinaryString(intResult); + StringBuffer prefix = new StringBuffer("0b"); + for (int i = 0; i < 32 - formattedResult.length(); i++) { + prefix.append('0'); + } + prefix.append(formattedResult); + formattedResult = prefix.toString(); + } else if (DECIMAL_FORMAT.equals(formatId)) { + formattedResult = Integer.toString(intResult); + } else { + PDAPlugin.failRequest(rm, INVALID_HANDLE, "Invalid format"); + } + rm.setData(new FormattedValueDMData(formattedResult)); + rm.done(); + } catch (NumberFormatException e) { + PDAPlugin.failRequest(rm, REQUEST_FAILED, "Cannot format value"); + } + } + } + }); + } + }); + } else { + PDAPlugin.failRequest(rm, INVALID_HANDLE, "Invalid expression context " + formattedCtx); + } + } + + + public void writeExpression(final IExpressionDMContext exprCtx, final String exprValue, String formatId, + final RequestMonitor rm) + { + writeExpression(exprCtx, exprValue, formatId, true, rm); + } + + /** + * Method to write an expression, with an additional parameter to suppress + * issuing of the expression changed event. + * @see IExpressions#writeExpression(org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext, String, String, RequestMonitor) + */ + public void writeExpression(final IExpressionDMContext exprCtx, String formattedExprValue, String formatId, + final boolean sendEvent, final RequestMonitor rm) + { + String value = null; + try { + int intValue = 0; + if (HEX_FORMAT.equals(formatId)) { + if (formattedExprValue.startsWith("0x")) formattedExprValue = formattedExprValue.substring(2); + value = Integer.toString( Integer.parseInt(formattedExprValue, 16) ); + } else if (DECIMAL_FORMAT.equals(formatId)) { + value = Integer.toString( Integer.parseInt(formattedExprValue, 10) ); + } else if (OCTAL_FORMAT.equals(formatId)) { + if (formattedExprValue.startsWith("0c")) formattedExprValue = formattedExprValue.substring(2); + value = Integer.toString( Integer.parseInt(formattedExprValue, 8) ); + } else if (BINARY_FORMAT.equals(formatId)) { + if (formattedExprValue.startsWith("0b")) formattedExprValue = formattedExprValue.substring(2); + value = Integer.toString( Integer.parseInt(formattedExprValue, 2) ); + } + } catch (NumberFormatException e) { + PDAPlugin.failRequest(rm, INVALID_HANDLE, "Value not formatted properly"); + return; + } + + final String exprValue = value != null ? value : formattedExprValue; + + + if (exprCtx instanceof ExpressionDMContext) { + final PDAThreadDMContext threadCtx = DMContexts.getAncestorOfType(exprCtx, PDAThreadDMContext.class); + final IFrameDMContext frameCtx = DMContexts.getAncestorOfType(exprCtx, IFrameDMContext.class); + + // Similarly to retrieving the variable, retrieve the + // stack depth first. + fStack.getStackDepth( + frameCtx, 0, + new DataRequestMonitor<Integer>(getExecutor(), rm) { + @Override + protected void handleSuccess() { + // Calculate the frame index. + int frameId = getData() - frameCtx.getLevel() - 1; + + // Send the "write" command to PDA debugger + fCommandCache.execute( + new PDASetVarCommand( threadCtx, frameId, exprCtx.getExpression(), exprValue), + new DataRequestMonitor<PDACommandResult>(getExecutor(), rm) { + @Override + protected void handleSuccess() { + if (sendEvent) { + getSession().dispatchEvent(new ExpressionChangedDMEvent(exprCtx), getProperties()); + } + // An expression changed, clear the cache corresponding to + // this event. Since the var evaluate commands, use the thread + // context, we have to clear all the cache entries for that thread. + fCommandCache.reset(DMContexts.getAncestorOfType(exprCtx, PDAThreadDMContext.class)); + rm.done(); + } + }); + } + }); + } else { + PDAPlugin.failRequest(rm, INVALID_HANDLE, "Invalid expression context " + exprCtx); + } + } + + @SuppressWarnings("unchecked") + @Deprecated + public void getModelData(IDMContext dmc, DataRequestMonitor<?> rm) { + if (dmc instanceof IExpressionDMContext) { + getExpressionData((IExpressionDMContext) dmc, (DataRequestMonitor<IExpressionDMData>) rm); + } else if (dmc instanceof FormattedValueDMContext) { + getFormattedExpressionValue((FormattedValueDMContext) dmc, (DataRequestMonitor<FormattedValueDMData>) rm); + } else { + PDAPlugin.failRequest(rm, INVALID_HANDLE, "Unknown DMC type"); + } + } + + @DsfServiceEventHandler + public void eventDispatched(IResumedDMEvent e) { + // Mark the cache as not available, so that data retrieval commands + // will fail. Also reset the cache unless it was a step command. + fCommandCache.setContextAvailable(e.getDMContext(), false); + if (!e.getReason().equals(StateChangeReason.STEP)) { + fCommandCache.reset(e.getDMContext()); + } + } + + + @DsfServiceEventHandler + public void eventDispatched(ISuspendedDMEvent e) { + // Enable sending commands to target and clear the cache. + fCommandCache.setContextAvailable(e.getDMContext(), true); + fCommandCache.reset(e.getDMContext()); + } + + @DsfServiceEventHandler + public void eventDispatched(ExpressionChangedDMEvent e) { + // An expression changed, clear the cache corresponding to + // this event. Since the var evaluate commands, use the thread + // context, we have to clear all the cache entries for that thread. + fCommandCache.reset(DMContexts.getAncestorOfType(e.getDMContext(), PDAThreadDMContext.class)); + } + + public void flushCache(IDMContext context) { + fCommandCache.reset(context); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDARegisters.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDARegisters.java new file mode 100644 index 00000000000..6a1132a08b1 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDARegisters.java @@ -0,0 +1,556 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service; + +import java.math.BigInteger; +import java.util.Hashtable; +import java.util.Map; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.AbstractDMContext; +import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent; +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; +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; +import org.eclipse.cdt.dsf.debug.service.command.CommandCache; +import org.eclipse.cdt.dsf.debug.service.command.IEventListener; +import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.examples.dsf.pda.PDAPlugin; +import org.eclipse.cdt.examples.dsf.pda.service.commands.PDABitField; +import org.eclipse.cdt.examples.dsf.pda.service.commands.PDAGroupsCommand; +import org.eclipse.cdt.examples.dsf.pda.service.commands.PDAListResult; +import org.eclipse.cdt.examples.dsf.pda.service.commands.PDARegister; +import org.eclipse.cdt.examples.dsf.pda.service.commands.PDARegistersCommand; +import org.eclipse.cdt.examples.dsf.pda.service.commands.PDARegistersCommandResult; +import org.osgi.framework.BundleContext; + +/** + * + */ +public class PDARegisters extends AbstractDsfService + implements IRegisters, IEventListener, ICachingService +{ + + private static class RegisterGroupDMContext extends AbstractDMContext implements IRegisterGroupDMContext { + final private String fName; + + public RegisterGroupDMContext(String sessionId, PDAVirtualMachineDMContext dmc, String groupName) { + super(sessionId, new IDMContext[] { dmc }); + fName = groupName; + } + + @Override + public boolean equals(Object other) { + return ((super.baseEquals(other)) && + (((RegisterGroupDMContext) other).fName.equals(fName))); + } + + @Override + public int hashCode() { return super.baseHashCode() + fName.hashCode(); } + + @Override + public String toString() { return baseToString() + ".group[" + fName + "]"; } //$NON-NLS-1$ //$NON-NLS-2$ + } + + private static class RegisterDMContext extends AbstractDMContext implements IRegisterDMContext { + final private PDARegister fRegister; + + public RegisterDMContext(String sessionId, PDAThreadDMContext thread, RegisterGroupDMContext group, PDARegister reg) { + super(sessionId, new IDMContext[] { thread, group }); + fRegister = reg; + } + + @Override + public boolean equals(Object other) { + return ((super.baseEquals(other)) && + (((RegisterDMContext) other).fRegister.fName.equals(fRegister.fName))); + } + + @Override + public int hashCode() { return super.baseHashCode() + fRegister.fName.hashCode(); } + @Override + public String toString() { return baseToString() + ".register[" + fRegister.fName + "]"; } //$NON-NLS-1$ //$NON-NLS-2$ + } + + /* + * Support class used to construct BitField DMCs. + */ + private static class BitFieldDMContext extends AbstractDMContext implements IBitFieldDMContext { + + final private PDABitField fBitField; + + public BitFieldDMContext(String sessionId, RegisterDMContext reg, PDABitField bitField) { + super(sessionId, new IDMContext[] { reg }); + + fBitField = bitField; + } + + /* + * Required common manipulation routines. + */ + @Override + public boolean equals(Object other) { + if (other instanceof BitFieldDMContext) { + BitFieldDMContext dmc = (BitFieldDMContext) other; + return( (super.baseEquals(other)) && + (dmc.fBitField.fName.equals(fBitField.fName))); + } else { + return false; + } + } + + @Override + public int hashCode() { return super.baseHashCode() + fBitField.fName.hashCode(); } + + @Override + public String toString() { return baseToString() + ".bitfield[" + fBitField.fName + "]"; } //$NON-NLS-1$ //$NON-NLS-2$ + } + + private static class RegisterGroupDMData implements IRegisterGroupDMData { + + final private String fName; + + public RegisterGroupDMData(String name) { + fName = name; + } + public String getName() { return fName; } + public String getDescription() { return "Description of the " + fName + " register group"; } + } + + private static class RegisterDMData implements IRegisterDMData { + + final private PDARegister fRegister; + + public RegisterDMData(PDARegister reg) { + fRegister = reg; + } + + public boolean isReadable() { return true; } + public boolean isReadOnce() { return false; } + public boolean isWriteable() { return fRegister.fWritable; } + public boolean isWriteOnce() { return false; } + public boolean hasSideEffects() { return false; } + public boolean isVolatile() { return true; } + + public boolean isFloat() { return false; } + public String getName() { return fRegister.fName; } + public String getDescription() { return "Description of the " + fRegister.fName + " register"; } + } + + private static class Mnemonic implements IMnemonic { + Mnemonic(String name, String value, int numBits) { + fName = name; + fValue = new BigInteger(value); + fNumBits = numBits; + } + final private String fName; + final private BigInteger fValue; + final private int fNumBits; + + public String getShortName() { return fName; } + public String getLongName() { return fName; } + + public BigInteger getValue() { return fValue; } + public int getBitCount() { return fNumBits; } + + @Override + public boolean equals( Object element ) { + if ( element instanceof Mnemonic ) { + Mnemonic mnem = (Mnemonic) element; + return ( mnem.fName.equals( fName ) ) && + ( mnem.fValue.equals( fValue ) ) && + ( mnem.fNumBits == fNumBits ); + } + return false ; + } + } + + private class BitFieldDMData implements IBitFieldDMData { + + final private PDABitField fBitField; + final private IBitGroup[] fBitGroups; + final private Mnemonic[] fMnemonics; + final private BigInteger fValue; + final private Mnemonic fMnemonicValue; + + public BitFieldDMData(PDABitField bitField, String value) { + fBitField = bitField; + fValue = new BigInteger(value); + + fBitGroups = new IBitGroup[] { + new IBitGroup() { + public int startBit() { return fBitField.fOffset; } + public int bitCount() { return fBitField.fCount; } + } + }; + + fMnemonics = new Mnemonic[fBitField.fMnemonics.size()]; + Mnemonic mnemonicValue = null; + int i = 0; + for (Map.Entry<String, String> mnemonicEntry : fBitField.fMnemonics.entrySet()) { + fMnemonics[i] = new Mnemonic(mnemonicEntry.getKey(), mnemonicEntry.getValue(), fBitField.fCount); + if (fValue.equals(fMnemonics[i].fValue)) { + mnemonicValue = fMnemonics[i]; + } + i++; + } + fMnemonicValue = mnemonicValue; + } + + + public IBitGroup[] getBitGroup() { return fBitGroups; } + public IMnemonic[] getMnemonics() { return fMnemonics; } + + public boolean isZeroBasedNumbering() { return true; } + public boolean isZeroBitLeftMost() { return true; } + public boolean isReadable() { return true; } + public boolean isReadOnce() { return false; } + public boolean isWriteable() { return true; } + public boolean isWriteOnce() { return false; } + public boolean hasSideEffects() { return false; } + public boolean isFloat() { return false; } + + public String getName() { return fBitField.fName; } + public String getDescription() { return "Description of the " + fBitField.fName + " bit field"; } + + public IMnemonic getCurrentMnemonicValue() { return fMnemonicValue; } + } + + private static class RegisterChangedDMEvent extends AbstractDMEvent<IRegisterDMContext> implements IRegisterChangedDMEvent { + RegisterChangedDMEvent(IRegisterDMContext registerDmc) { + super(registerDmc); + } + } + + private PDACommandControl fCommandControl; + private PDAExpressions fExpressions; + private CommandCache fNamesCache; + + public PDARegisters(DsfSession session) + { + super(session); + } + + @Override + protected BundleContext getBundleContext() + { + return PDAPlugin.getBundleContext(); + } + + @Override + public void initialize(final RequestMonitor requestMonitor) { + super.initialize( + new RequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleSuccess() { + doInitialize(requestMonitor); + }}); + } + + private void doInitialize(RequestMonitor requestMonitor) { + + fCommandControl = getServicesTracker().getService(PDACommandControl.class); + fExpressions = getServicesTracker().getService(PDAExpressions.class); + + // Create the cache to store the register definitions. This cache + // only needs to be reset upon the "registers" event and is available + // all the time. + fNamesCache = new CommandCache(getSession(), fCommandControl); + fNamesCache.setContextAvailable(fCommandControl.getContext(), true); + + // Add the register service as a listener to PDA events, to catch + // the "registers" events from the command control. + fCommandControl.addEventListener(this); + + // Sign up so we see events. We use these events to decide how to manage + // any local caches we are providing as well as the lower level register + // cache we create to get/set registers on the target. + getSession().addServiceEventListener(this, null); + + // Make ourselves known so clients can use us. + register(new String[]{IRegisters.class.getName(), PDARegisters.class.getName()}, new Hashtable<String,String>()); + + requestMonitor.done(); + } + + @Override + public void shutdown(RequestMonitor requestMonitor) + { + unregister(); + fCommandControl.removeEventListener(this); + getSession().removeServiceEventListener(this); + super.shutdown(requestMonitor); + } + + public void getRegisterGroups(IDMContext ctx, final DataRequestMonitor<IRegisterGroupDMContext[]> rm ) { + final PDAVirtualMachineDMContext dmc = DMContexts.getAncestorOfType(ctx, PDAVirtualMachineDMContext.class); + if (dmc == null) { + PDAPlugin.failRequest(rm, INVALID_HANDLE, "Container context not found"); //$NON-NLS-1$ + return; + } + + fNamesCache.execute( + new PDAGroupsCommand(dmc), + new DataRequestMonitor<PDAListResult>(getExecutor(), rm) { + @Override + protected void handleSuccess() { + IRegisterGroupDMContext[] groups = new IRegisterGroupDMContext[getData().fValues.length]; + for (int i = 0; i < getData().fValues.length; i++) { + groups[i] = new RegisterGroupDMContext(getSession().getId(), dmc, getData().fValues[i]); + } + rm.setData(groups); + rm.done(); + }; + }); + } + + public void getRegisters(final IDMContext ctx, final DataRequestMonitor<IRegisterDMContext[]> rm) { + final PDAThreadDMContext execDmc = DMContexts.getAncestorOfType(ctx, PDAThreadDMContext.class); + if ( execDmc == null ) { + PDAPlugin.failRequest(rm, INVALID_HANDLE , "Thread context not found"); //$NON-NLS-1$ + return; + } + + final RegisterGroupDMContext groupDmc = DMContexts.getAncestorOfType(ctx, RegisterGroupDMContext.class); + if ( groupDmc == null ) { + PDAPlugin.failRequest(rm, INVALID_HANDLE , "Group context not found"); //$NON-NLS-1$ + return; + } + + fNamesCache.execute( + new PDARegistersCommand(execDmc, groupDmc != null ? groupDmc.fName : null), + new DataRequestMonitor<PDARegistersCommandResult>(getExecutor(), rm) { + @Override + protected void handleSuccess() { + IRegisterDMContext[] groups = new IRegisterDMContext[getData().fRegisters.length]; + for (int i = 0; i < getData().fRegisters.length; i++) { + groups[i] = new RegisterDMContext(getSession().getId(), execDmc, groupDmc, getData().fRegisters[i]); + } + rm.setData(groups); + rm.done(); + }; + }); + + } + + public void getBitFields( IDMContext dmc , DataRequestMonitor<IBitFieldDMContext[]> rm ) { + + RegisterDMContext registerDmc = DMContexts.getAncestorOfType(dmc, RegisterDMContext.class); + + if ( registerDmc == null ) { + PDAPlugin.failRequest(rm,INVALID_HANDLE, "No register in context: " + dmc) ; //$NON-NLS-1$ + return; + } + + PDABitField[] bitFields = registerDmc.fRegister.fBitFields; + BitFieldDMContext[] bitFieldDMCs = new BitFieldDMContext[bitFields.length]; + + for (int i = 0; i < bitFields.length; i++) { + bitFieldDMCs[i] = new BitFieldDMContext(getSession().getId(), registerDmc, bitFields[i]); + } + + rm.setData(bitFieldDMCs) ; + rm.done(); + } + + public void writeRegister(final IRegisterDMContext regCtx, String regValue, String formatId, final RequestMonitor rm) { + if (regCtx instanceof RegisterDMContext) { + IExpressionDMContext exprCtx = createRegisterExpressionDmc( (RegisterDMContext)regCtx ); + fExpressions.writeExpression( + exprCtx, regValue, formatId, false, + new RequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + generateRegisterChangedEvent( (RegisterDMContext)regCtx ); + rm.done(); + } + }); + + } else { + PDAPlugin.failRequest(rm, INVALID_HANDLE, "Invalid context"); //$NON-NLS-1$ + } + + } + + public void writeBitField(final IBitFieldDMContext bitFieldCtx, String bitFieldValue, String formatId, final RequestMonitor rm) { + if (bitFieldCtx instanceof BitFieldDMContext) { + IExpressionDMContext exprCtx = createBitFieldExpressionDmc( (BitFieldDMContext)bitFieldCtx ); + fExpressions.writeExpression( + exprCtx, bitFieldValue, formatId, false, + new RequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + generateRegisterChangedEvent( + DMContexts.getAncestorOfType(bitFieldCtx, RegisterDMContext.class) ); + rm.done(); + } + }); + } else { + PDAPlugin.failRequest(rm, INVALID_HANDLE, "Invalid context"); //$NON-NLS-1$ + } + } + + public void writeBitField(IBitFieldDMContext bitFieldCtx, IMnemonic mnemonic, RequestMonitor rm) { + if (mnemonic instanceof Mnemonic) { + writeBitField(bitFieldCtx, ((Mnemonic)mnemonic).fValue.toString(), NATURAL_FORMAT, rm); + } else { + PDAPlugin.failRequest(rm, INVALID_HANDLE, "Invalid mnemonic"); //$NON-NLS-1$ + } + } + + public void getAvailableFormats(IFormattedDataDMContext dmc, DataRequestMonitor<String[]> rm) { + IExpressionDMContext exprCtx = null; + if ( dmc instanceof RegisterDMContext ) { + exprCtx = createRegisterExpressionDmc((RegisterDMContext)dmc); + } else if ( dmc instanceof BitFieldDMContext ) { + exprCtx = createBitFieldExpressionDmc((BitFieldDMContext)dmc); + } + if (exprCtx != null) { + fExpressions.getAvailableFormats(exprCtx, rm); + } else { + throw new IllegalArgumentException("Invalid register/bit field context " + dmc); + } + } + + public FormattedValueDMContext getFormattedValueContext(IFormattedDataDMContext dmc, String formatId) { + IExpressionDMContext exprCtx = null; + if ( dmc instanceof RegisterDMContext ) { + exprCtx = createRegisterExpressionDmc((RegisterDMContext)dmc); + } else if ( dmc instanceof BitFieldDMContext ) { + exprCtx = createBitFieldExpressionDmc((BitFieldDMContext)dmc); + } + if (exprCtx != null) { + return fExpressions.getFormattedValueContext(exprCtx, formatId); + } else { + throw new IllegalArgumentException("Invalid register/bit field context " + dmc); + } + } + + public void findRegisterGroup(IDMContext ctx, String name, DataRequestMonitor<IRegisterGroupDMContext> rm) { + PDAPlugin.failRequest(rm, NOT_SUPPORTED, "Finding context not supported"); //$NON-NLS-1$ + } + + public void findRegister(IDMContext ctx, String name, DataRequestMonitor<IRegisterDMContext> rm) { + PDAPlugin.failRequest(rm, NOT_SUPPORTED, "Finding context not supported"); //$NON-NLS-1$ + } + + public void findBitField(IDMContext ctx, String name, DataRequestMonitor<IBitFieldDMContext> rm) { + PDAPlugin.failRequest(rm, NOT_SUPPORTED, "Finding context not supported"); //$NON-NLS-1$ + } + + @SuppressWarnings("unchecked") + public void getModelData(IDMContext dmc, DataRequestMonitor<?> rm) { + /* + * This is the method which is called when actual results need to be returned. We + * can be called either with a service DMC for which we return ourselves or we can + * be called with the DMC's we have handed out. If the latter is the case then we + * data mine by talking to the Debug Engine. + */ + + if (dmc instanceof RegisterGroupDMContext) { + getRegisterGroupData((RegisterGroupDMContext)dmc, (DataRequestMonitor<IRegisterGroupDMData>)rm); + } else if (dmc instanceof RegisterDMContext) { + getRegisterData((RegisterDMContext)dmc, (DataRequestMonitor<IRegisterDMData>)rm); + } else if (dmc instanceof BitFieldDMContext) { + getBitFieldData((BitFieldDMContext) dmc, (DataRequestMonitor<IBitFieldDMData>)rm); + } else if (dmc instanceof FormattedValueDMContext) { + getFormattedExpressionValue((FormattedValueDMContext)dmc, (DataRequestMonitor<FormattedValueDMData>)rm); + } else { + PDAPlugin.failRequest(rm, INVALID_HANDLE, "Unknown DMC type"); //$NON-NLS-1$ + } + } + + public void getFormattedExpressionValue(FormattedValueDMContext dmc, DataRequestMonitor<FormattedValueDMData> rm) { + fExpressions.getFormattedExpressionValue(dmc, rm); + } + + public void getRegisterGroupData(IRegisterGroupDMContext regGroupDmc, DataRequestMonitor<IRegisterGroupDMData> rm) { + if (regGroupDmc instanceof RegisterGroupDMContext) { + rm.setData(new RegisterGroupDMData( ((RegisterGroupDMContext)regGroupDmc).fName )); + rm.done(); + } else { + PDAPlugin.failRequest(rm, INVALID_HANDLE, "Invalid context"); //$NON-NLS-1$ + } + } + + public void getRegisterData(IRegisterDMContext regDmc , DataRequestMonitor<IRegisterDMData> rm) { + if (regDmc instanceof RegisterDMContext) { + rm.setData(new RegisterDMData( ((RegisterDMContext)regDmc).fRegister )); + rm.done(); + } else { + PDAPlugin.failRequest(rm, INVALID_HANDLE, "Invalid context"); //$NON-NLS-1$ + } + } + + public void getBitFieldData(IBitFieldDMContext dmc, final DataRequestMonitor<IBitFieldDMData> rm) { + if ( !(dmc instanceof BitFieldDMContext) ) { + PDAPlugin.failRequest(rm, INVALID_HANDLE, "Invalid context"); //$NON-NLS-1$ + } + final BitFieldDMContext bitFieldDmc = (BitFieldDMContext) dmc; + + IExpressionDMContext bitFieldExprDmc = createBitFieldExpressionDmc(bitFieldDmc); + FormattedValueDMContext formattedBitFieldDmc = + fExpressions.getFormattedValueContext(bitFieldExprDmc, NATURAL_FORMAT); + fExpressions.getFormattedExpressionValue( + formattedBitFieldDmc, + new DataRequestMonitor<FormattedValueDMData>(getExecutor(), rm) { + @Override + protected void handleSuccess() { + rm.setData(new BitFieldDMData(bitFieldDmc.fBitField, getData().getFormattedValue())); + rm.done(); + } + }); + } + + private IExpressionDMContext createRegisterExpressionDmc(RegisterDMContext dmc) { + return fExpressions.createExpression(dmc, "$" + dmc.fRegister.fName); + } + + private IExpressionDMContext createBitFieldExpressionDmc(BitFieldDMContext dmc) { + RegisterDMContext regDmc = DMContexts.getAncestorOfType(dmc, RegisterDMContext.class); + return fExpressions.createExpression(dmc, "$" + regDmc.fRegister.fName + "." + dmc.fBitField.fName); + } + + + @DsfServiceEventHandler + public void eventDispatched(IRunControl.IResumedDMEvent e) { + } + + @DsfServiceEventHandler + public void eventDispatched( + IRunControl.ISuspendedDMEvent e) { + } + + @DsfServiceEventHandler + public void eventDispatched(final IRegisters.IRegisterChangedDMEvent e) { + } + + private void generateRegisterChangedEvent(RegisterDMContext dmc ) { + getSession().dispatchEvent(new RegisterChangedDMEvent(dmc), getProperties()); + } + + public void eventReceived(Object output) { + if (!(output instanceof String)) return; + if ("registers".equals(output)) { + fNamesCache.reset(); + } + } + + public void flushCache(IDMContext context) { + fExpressions.flushCache(context); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDARunControl.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDARunControl.java new file mode 100644 index 00000000000..29772539cdb --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDARunControl.java @@ -0,0 +1,672 @@ +/******************************************************************************* + * 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 - Modified for handling of multiple threads + *******************************************************************************/ +package org.eclipse.cdt.examples.dsf.pda.service; + +import java.util.Arrays; +import java.util.Hashtable; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent; +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.command.IEventListener; +import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.examples.dsf.pda.PDAPlugin; +import org.eclipse.cdt.examples.dsf.pda.service.commands.AbstractPDACommand; +import org.eclipse.cdt.examples.dsf.pda.service.commands.PDACommandResult; +import org.eclipse.cdt.examples.dsf.pda.service.commands.PDAResumeCommand; +import org.eclipse.cdt.examples.dsf.pda.service.commands.PDAStepCommand; +import org.eclipse.cdt.examples.dsf.pda.service.commands.PDAStepReturnCommand; +import org.eclipse.cdt.examples.dsf.pda.service.commands.PDAVMResumeCommand; +import org.eclipse.cdt.examples.dsf.pda.service.commands.PDAVMSuspendCommand; +import org.osgi.framework.BundleContext; + + +/** + * Service for monitoring and controlling execution state of the DPA + * program. + * <p> + * This service depends on the {@link PDACommandControl} service. + * It must be initialized before this service is initialized. + * </p> + */ +public class PDARunControl extends AbstractDsfService + implements IRunControl, IEventListener +{ + // Implementation note about tracking execution state: + // This class implements event handlers for the events that are generated by + // this service itself. When the event is dispatched, these handlers will + // be called first, before any of the clients. These handlers update the + // service's internal state information to make them consistent with the + // events being issued. Doing this in the handlers as opposed to when + // the events are generated, guarantees that the state of the service will + // always be consistent with the events. + // The purpose of this pattern is to allow clients that listen to service + // events and track service state, to be perfectly in sync with the service + // state. + + static final private IExecutionDMContext[] EMPTY_TRIGGERING_CONTEXTS_ARRAY = new IExecutionDMContext[0]; + + @Immutable + private static class ThreadResumedEvent extends AbstractDMEvent<IExecutionDMContext> + implements IResumedDMEvent + { + private final StateChangeReason fReason; + + ThreadResumedEvent(PDAThreadDMContext ctx, StateChangeReason reason) { + super(ctx); + fReason = reason; + } + + public StateChangeReason getReason() { + return fReason; + } + + @Override + public String toString() { + return "THREAD RESUMED: " + getDMContext() + " (" + fReason + ")"; + } + } + + @Immutable + private static class VMResumedEvent extends AbstractDMEvent<IExecutionDMContext> + implements IContainerResumedDMEvent + { + private final StateChangeReason fReason; + + VMResumedEvent(PDAVirtualMachineDMContext ctx, StateChangeReason reason) { + super(ctx); + fReason = reason; + } + + public StateChangeReason getReason() { + return fReason; + } + + public IExecutionDMContext[] getTriggeringContexts() { + return EMPTY_TRIGGERING_CONTEXTS_ARRAY; + } + + @Override + public String toString() { + return "VM RESUMED: (" + fReason + ")"; + } + } + + @Immutable + private static class ThreadSuspendedEvent extends AbstractDMEvent<IExecutionDMContext> + implements ISuspendedDMEvent + { + private final StateChangeReason fReason; + + ThreadSuspendedEvent(PDAThreadDMContext ctx, StateChangeReason reason) { + super(ctx); + fReason = reason; + } + + public StateChangeReason getReason() { + return fReason; + } + + @Override + public String toString() { + return "THREAD SUSPENDED: " + getDMContext() + " (" + fReason + ")"; + } + } + + @Immutable + private static class VMSuspendedEvent extends AbstractDMEvent<IExecutionDMContext> + implements IContainerSuspendedDMEvent + { + private final StateChangeReason fReason; + + final private IExecutionDMContext[] fTriggeringThreads; + + VMSuspendedEvent(PDAVirtualMachineDMContext ctx, PDAThreadDMContext threadCtx, StateChangeReason reason) { + super(ctx); + fReason = reason; + if (threadCtx != null) { + fTriggeringThreads = new IExecutionDMContext[] { threadCtx }; + } else { + fTriggeringThreads = EMPTY_TRIGGERING_CONTEXTS_ARRAY; + } + } + + public StateChangeReason getReason() { + return fReason; + } + + public IExecutionDMContext[] getTriggeringContexts() { + return fTriggeringThreads; + } + + + @Override + public String toString() { + return "THREAD SUSPENDED: " + getDMContext() + + " (" + fReason + + ", trigger = " + Arrays.asList(fTriggeringThreads) + + ")"; + } + } + + @Immutable + private static class ExecutionDMData implements IExecutionDMData { + private final StateChangeReason fReason; + ExecutionDMData(StateChangeReason reason) { + fReason = reason; + } + public StateChangeReason getStateChangeReason() { return fReason; } + } + + private static class ThreadStartedEvent extends AbstractDMEvent<IExecutionDMContext> + implements IStartedDMEvent + { + ThreadStartedEvent(PDAThreadDMContext threadCtx) { + super(threadCtx); + } + } + + private static class ThreadExitedEvent extends AbstractDMEvent<IExecutionDMContext> + implements IExitedDMEvent + { + ThreadExitedEvent(PDAThreadDMContext threadCtx) { + super(threadCtx); + } + } + + // Services + private PDACommandControl fCommandControl; + + // Reference to the virtual machine data model context + private PDAVirtualMachineDMContext fDMContext; + + // VM state flags + private boolean fVMSuspended = true; + private boolean fVMResumePending = false; + private boolean fVMSuspendPending = false; + private boolean fVMStepping = false; + private StateChangeReason fVMStateChangeReason; + + // Threads' state data + private static class ThreadInfo { + final PDAThreadDMContext fContext; + boolean fSuspended = false; + boolean fResumePending = false; + boolean fSuspendPending = false; + boolean fStepping = false; + StateChangeReason fStateChangeReason = StateChangeReason.UNKNOWN; + + ThreadInfo(PDAThreadDMContext context) { + fContext = context; + } + + @Override + public String toString() { + return fContext.toString() + " (" + + (fSuspended ? "SUSPENDED, " : "SUSPENDED, ") + + fStateChangeReason + + (fResumePending ? ", RESUME_PENDING, " : "") + + (fSuspendPending ? ", SUSPEND_PENDING, " : "") + + (fStepping ? ", SUSPEND_PENDING, " : "") + + ")"; + + } + } + + private Map<Integer, ThreadInfo> fThreads = new LinkedHashMap<Integer, ThreadInfo>(); + + + public PDARunControl(DsfSession session) { + super(session); + } + + @Override + protected BundleContext getBundleContext() { + return PDAPlugin.getBundleContext(); + } + + @Override + public void initialize(final RequestMonitor rm) { + super.initialize( + new RequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + doInitialize(rm); + }}); + } + + private void doInitialize(final RequestMonitor rm) { + // Cache a reference to the command control and the virtual machine context + fCommandControl = getServicesTracker().getService(PDACommandControl.class); + fDMContext = fCommandControl.getContext(); + + // Create the main thread context. + fThreads.put( + 1, + new ThreadInfo(new PDAThreadDMContext(getSession().getId(), fDMContext, 1))); + + // Add the run control service as a listener to PDA events, to catch + // suspended/resumed/started/exited events from the command control. + fCommandControl.addEventListener(this); + + // Add the run control service as a listener to service events as well, + // in order to process our own suspended/resumed/started/exited events. + getSession().addServiceEventListener(this, null); + + // Register the service with OSGi + register(new String[]{IRunControl.class.getName(), PDARunControl.class.getName()}, new Hashtable<String,String>()); + + rm.done(); + } + + @Override + public void shutdown(final RequestMonitor rm) { + fCommandControl.removeEventListener(this); + getSession().removeServiceEventListener(this); + super.shutdown(rm); + } + + @Deprecated + @SuppressWarnings("unchecked") + public void getModelData(IDMContext dmc, DataRequestMonitor<?> rm) { + // The getModelData() is deprecated and clients are expected to switch + // to getExecutionData() and other data retrieve methods directly. + // However the UI cache still uses it for now. + if (dmc instanceof IExecutionDMContext) { + getExecutionData((IExecutionDMContext)dmc, (DataRequestMonitor<IExecutionDMData>)rm); + } else { + PDAPlugin.failRequest(rm, INVALID_HANDLE, "Unknown DMC type"); + } + } + + public void eventReceived(Object output) { + if (!(output instanceof String)) return; + String event = (String)output; + + int nameEnd = event.indexOf(' '); + nameEnd = nameEnd == -1 ? event.length() : nameEnd; + String eventName = event.substring(0, nameEnd); + + PDAThreadDMContext thread = null; + StateChangeReason reason = StateChangeReason.UNKNOWN; + if (event.length() > nameEnd + 1) { + if ( Character.isDigit(event.charAt(nameEnd + 1)) ) { + int threadIdEnd = event.indexOf(' ', nameEnd + 1); + threadIdEnd = threadIdEnd == -1 ? event.length() : threadIdEnd; + try { + int threadId = Integer.parseInt(event.substring(nameEnd + 1, threadIdEnd)); + if (fThreads.containsKey(threadId)) { + thread = fThreads.get(threadId).fContext; + } else { + // In case where a suspended event follows directly a + // started event, a thread may not be in the list of + // known threads yet. In this case create the + // thread context based on the ID. + thread = new PDAThreadDMContext(getSession().getId(), fDMContext, threadId); + } + } catch (NumberFormatException e) {} + if (threadIdEnd + 1 < event.length()) { + reason = parseStateChangeReason(event.substring(threadIdEnd + 1)); + } + } else { + reason = parseStateChangeReason(event.substring(nameEnd + 1)); + } + } + + // Handle PDA debugger suspended/resumed events and issue the + // corresponding Data Model events. Do not update the state + // information until we start dispatching the service events. + IDMEvent<?> dmEvent = null; + if (eventName.equals("suspended") && thread != null) { + dmEvent = new ThreadSuspendedEvent(thread, reason); + } else if (eventName.equals("resumed") && thread != null) { + dmEvent = new ThreadResumedEvent(thread, reason); + } else if (event.startsWith("vmsuspended")) { + dmEvent = new VMSuspendedEvent(fDMContext, thread, reason); + } else if (event.startsWith("vmresumed")) { + dmEvent = new VMResumedEvent(fDMContext, reason); + } else if (event.startsWith("started") && thread != null) { + dmEvent = new ThreadStartedEvent(thread); + } else if (event.startsWith("exited") && thread != null) { + dmEvent = new ThreadExitedEvent(thread); + } + + if (dmEvent != null) { + getSession().dispatchEvent(dmEvent, getProperties()); + } + } + + private StateChangeReason parseStateChangeReason(String reasonString) { + if (reasonString.startsWith("breakpoint") || reasonString.startsWith("watch")) { + return StateChangeReason.BREAKPOINT; + } else if (reasonString.equals("step") || reasonString.equals("drop")) { + return StateChangeReason.STEP; + } else if (reasonString.equals("client")) { + return StateChangeReason.USER_REQUEST; + } else if (reasonString.startsWith("event")) { + return StateChangeReason.SIGNAL; + } else { + return StateChangeReason.UNKNOWN; + } + + } + + @DsfServiceEventHandler + public void eventDispatched(ThreadResumedEvent e) { + ThreadInfo info = fThreads.get(((PDAThreadDMContext)e.getDMContext()).getID()); + if (info != null) { + info.fSuspended = false; + info.fResumePending = false; + info.fStateChangeReason = e.getReason(); + info.fStepping = e.getReason().equals(StateChangeReason.STEP); + } + } + + + @DsfServiceEventHandler + public void eventDispatched(VMResumedEvent e) { + fVMSuspended = false; + fVMResumePending = false; + fVMStateChangeReason = e.getReason(); + fVMStepping = e.getReason().equals(StateChangeReason.STEP); + for (ThreadInfo info : fThreads.values()) { + info.fSuspended = false; + info.fStateChangeReason = StateChangeReason.CONTAINER; + info.fStepping = false; + } + } + + @DsfServiceEventHandler + public void eventDispatched(ThreadSuspendedEvent e) { + ThreadInfo info = fThreads.get(((PDAThreadDMContext)e.getDMContext()).getID()); + if (info != null) { + info.fSuspended = true; + info.fSuspendPending = false; + info.fStateChangeReason = e.getReason(); + info.fStepping = e.getReason().equals(StateChangeReason.STEP); + } + } + + + @DsfServiceEventHandler + public void eventDispatched(VMSuspendedEvent e) { + fVMStateChangeReason = e.getReason(); + fVMSuspendPending = false; + fVMSuspended = true; + fVMStepping = false; + List<IExecutionDMContext> triggeringContexts = Arrays.asList(e.getTriggeringContexts()); + for (ThreadInfo info : fThreads.values()) { + info.fSuspended = true; + info.fStateChangeReason = triggeringContexts.contains(info.fContext) + ? StateChangeReason.STEP : StateChangeReason.CONTAINER; + info.fStepping = false; + } + } + + @DsfServiceEventHandler + public void eventDispatched(ThreadStartedEvent e) { + PDAThreadDMContext threadCtx = (PDAThreadDMContext)e.getDMContext(); + fThreads.put(threadCtx.getID(), new ThreadInfo(threadCtx)); + } + + @DsfServiceEventHandler + public void eventDispatched(ThreadExitedEvent e) { + PDAThreadDMContext threadCtx = (PDAThreadDMContext)e.getDMContext(); + fThreads.remove(threadCtx.getID()); + } + + public void canResume(IExecutionDMContext context, DataRequestMonitor<Boolean> rm) { + rm.setData(doCanResume(context)); + rm.done(); + } + + private boolean doCanResume(IExecutionDMContext context) { + if (context instanceof PDAThreadDMContext) { + PDAThreadDMContext threadContext = (PDAThreadDMContext)context; + // Threads can be resumed only if the VM is not suspended. + if (!fVMSuspended) { + ThreadInfo state = fThreads.get(threadContext.getID()); + if (state != null) { + return state.fSuspended && !state.fResumePending; + } + } + } else { + return fVMSuspended && !fVMResumePending; + } + return false; + } + + private boolean doCanStep(IExecutionDMContext context, StepType stepType) { + if (stepType == StepType.STEP_OVER || stepType == StepType.STEP_RETURN) { + if (context instanceof PDAThreadDMContext) { + PDAThreadDMContext threadContext = (PDAThreadDMContext)context; + // Only threads can be stepped. But they can be stepped + // while the VM is suspended or when just the thread is suspended. + ThreadInfo state = fThreads.get(threadContext.getID()); + if (state != null) { + return state.fSuspended && !state.fResumePending; + } + } + } + return false; + } + + public void canSuspend(IExecutionDMContext context, DataRequestMonitor<Boolean> rm) { + rm.setData(doCanSuspend(context)); + rm.done(); + } + + private boolean doCanSuspend(IExecutionDMContext context) { + if (context instanceof PDAThreadDMContext) { + PDAThreadDMContext threadContext = (PDAThreadDMContext)context; + // Threads can be resumed only if the VM is not suspended. + if (!fVMSuspended) { + ThreadInfo state = fThreads.get(threadContext.getID()); + if (state != null) { + return !state.fSuspended && state.fSuspendPending; + } + } + } else { + return !fVMSuspended && !fVMSuspendPending; + } + return false; + } + + public boolean isSuspended(IExecutionDMContext context) { + if (context instanceof PDAThreadDMContext) { + PDAThreadDMContext threadContext = (PDAThreadDMContext)context; + // Threads can be resumed only if the VM is not suspended. + if (!fVMSuspended) { + ThreadInfo state = fThreads.get(threadContext.getID()); + if (state != null) { + return state.fSuspended; + } + } + } + return fVMSuspended; + } + + public boolean isStepping(IExecutionDMContext context) { + if (!isSuspended(context)) { + if (context instanceof PDAThreadDMContext) { + PDAThreadDMContext threadContext = (PDAThreadDMContext)context; + // Threads can be resumed only if the VM is not suspended. + if (!fVMStepping) { + ThreadInfo state = fThreads.get(threadContext.getID()); + if (state != null) { + return state.fStepping; + } + } + } + return fVMStepping; + } + return false; + } + + public void resume(IExecutionDMContext context, final RequestMonitor rm) { + assert context != null; + + if (doCanResume(context)) { + if (context instanceof PDAThreadDMContext) { + final PDAThreadDMContext threadCtx = (PDAThreadDMContext)context; + fThreads.get(threadCtx.getID()).fResumePending = true; + fCommandControl.queueCommand( + new PDAResumeCommand(threadCtx), + new DataRequestMonitor<PDACommandResult>(getExecutor(), rm) { + @Override + protected void handleFailure() { + ThreadInfo threadState = fThreads.get(threadCtx.getID()); + if (threadState != null) { + threadState.fResumePending = false; + } + super.handleFailure(); + } + } + ); + } else { + fVMResumePending = true; + fCommandControl.queueCommand( + new PDAVMResumeCommand(fDMContext), + new DataRequestMonitor<PDACommandResult>(getExecutor(), rm) { + @Override + protected void handleFailure() { + fVMResumePending = false; + super.handleFailure(); + } + } + ); + } + } else { + PDAPlugin.failRequest(rm, INVALID_STATE, "Given context: " + context + ", is already running."); + } + } + + public void suspend(IExecutionDMContext context, final RequestMonitor rm){ + assert context != null; + + if (doCanSuspend(context)) { + if (context instanceof PDAThreadDMContext) { + final PDAThreadDMContext threadCtx = (PDAThreadDMContext)context; + fThreads.get(threadCtx.getID()).fSuspendPending = true; + fCommandControl.queueCommand( + new PDAVMSuspendCommand(fDMContext), + new DataRequestMonitor<PDACommandResult>(getExecutor(), rm) { + @Override + protected void handleFailure() { + ThreadInfo threadState = fThreads.get(threadCtx.getID()); + if (threadState != null) { + threadState.fSuspendPending = false; + } + super.handleFailure(); + } + } + ); + } else { + fVMSuspendPending = true; + fCommandControl.queueCommand( + new PDAVMSuspendCommand(fDMContext), + new DataRequestMonitor<PDACommandResult>(getExecutor(), rm) { + @Override + protected void handleFailure() { + fVMSuspendPending = false; + super.handleFailure(); + } + } + ); + } + } else { + PDAPlugin.failRequest(rm, IDsfStatusConstants.INVALID_STATE, "Given context: " + context + ", is already suspended."); + } + } + + public void canStep(IExecutionDMContext context, StepType stepType, DataRequestMonitor<Boolean> rm) { + rm.setData(doCanStep(context, stepType)); + rm.done(); + } + + public void step(IExecutionDMContext context, StepType stepType, final RequestMonitor rm) { + assert context != null; + + if (doCanStep(context, stepType)) { + final PDAThreadDMContext threadCtx = (PDAThreadDMContext)context; + final boolean vmWasSuspneded = fVMSuspended; + + if (vmWasSuspneded) { + fVMResumePending = true; + } else { + fThreads.get(threadCtx.getID()).fResumePending = true; + } + + AbstractPDACommand<PDACommandResult> stepCommand = + stepType == StepType.STEP_RETURN + ? new PDAStepReturnCommand(threadCtx) + : new PDAStepCommand(threadCtx); + + + fCommandControl.queueCommand( + stepCommand, + new DataRequestMonitor<PDACommandResult>(getExecutor(), rm) { + @Override + protected void handleFailure() { + // If the step command failed, we no longer + // expect to receive a resumed event. + if (vmWasSuspneded) { + fVMResumePending = false; + } else { + ThreadInfo threadState = fThreads.get(threadCtx.getID()); + if (threadState != null) { + threadState.fResumePending = false; + } + } + } + }); + + } else { + PDAPlugin.failRequest(rm, INVALID_STATE, "Cannot resume context"); + return; + } + } + + public void getExecutionContexts(final IContainerDMContext containerDmc, final DataRequestMonitor<IExecutionDMContext[]> rm) { + IExecutionDMContext[] threads = new IExecutionDMContext[fThreads.size()]; + int i = 0; + for (ThreadInfo info : fThreads.values()) { + threads[i++] = info.fContext; + } + rm.setData(threads); + rm.done(); + } + + public void getExecutionData(IExecutionDMContext dmc, DataRequestMonitor<IExecutionDMData> rm) { + if (dmc instanceof PDAThreadDMContext) { + ThreadInfo info = fThreads.get(((PDAThreadDMContext)dmc).getID()); + if (info == null) { + PDAPlugin.failRequest(rm, INVALID_HANDLE, "Unknown DMC type"); + return; + } + rm.setData( new ExecutionDMData(info.fStateChangeReason) ); + } else { + rm.setData( new ExecutionDMData(fVMStateChangeReason) ); + } + rm.done(); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDAStack.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDAStack.java new file mode 100644 index 00000000000..8e2058bf162 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDAStack.java @@ -0,0 +1,478 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service; + +import java.util.Hashtable; + +import org.eclipse.cdt.core.IAddress; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.AbstractDMContext; +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.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.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.command.CommandCache; +import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.examples.dsf.pda.PDAPlugin; +import org.eclipse.cdt.examples.dsf.pda.service.commands.PDAFrame; +import org.eclipse.cdt.examples.dsf.pda.service.commands.PDAFrameCommand; +import org.eclipse.cdt.examples.dsf.pda.service.commands.PDAFrameCommandResult; +import org.eclipse.cdt.examples.dsf.pda.service.commands.PDAStackCommand; +import org.eclipse.cdt.examples.dsf.pda.service.commands.PDAStackCommandResult; +import org.eclipse.cdt.examples.dsf.pda.service.commands.PDAStackDepthCommand; +import org.eclipse.cdt.examples.dsf.pda.service.commands.PDAStackDepthCommandResult; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.osgi.framework.BundleContext; + +/** + * Service for retrieving PDA debugger stack data. + * <p> + * This service depends on the {@link PDACommandControl} service and the + * {@link IRunControl} service. These services must be initialized before + * this service is initialized. + * </p> + */ +public class PDAStack extends AbstractDsfService implements IStack2, ICachingService { + + /** + * PDA stack frame contains only the stack frame level. It is only + * used as an index into the frame data returned by the PDA debugger. + */ + @Immutable + private static class FrameDMContext extends AbstractDMContext implements IFrameDMContext { + + final private int fLevel; + + FrameDMContext(String sessionId, PDAThreadDMContext execDmc, int level) { + super(sessionId, new IDMContext[] { execDmc }); + fLevel = level; + } + + public int getLevel() { return fLevel; } + + @Override + public boolean equals(Object other) { + return super.baseEquals(other) && ((FrameDMContext)other).fLevel == fLevel; + } + + @Override + public int hashCode() { + return super.baseHashCode() ^ fLevel; + } + + @Override + public String toString() { + return baseToString() + ".frame[" + fLevel + "]"; //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + /** + * Frame data based on the PDAFrame object returned by the PDA debugger. + */ + @Immutable + private static class FrameDMData implements IFrameDMData { + + final private PDAFrame fFrame; + + FrameDMData(PDAFrame frame) { + fFrame = frame; + } + + public String getFile() { + return fFrame.fFilePath.lastSegment(); + } + + public String getFunction() { + return fFrame.fFunction; + } + + public int getLine() { + return fFrame.fLine + 1; + } + + public int getColumn() { + return 0; + } + + public IAddress getAddress() { + return null; + } + } + + /** + * Context representing a variable in a given stack frame. + */ + @Immutable + private static class VariableDMContext extends AbstractDMContext implements IVariableDMContext { + + final private String fVariable; + + VariableDMContext(String sessionId, FrameDMContext frameCtx, String variable) { + super(sessionId, new IDMContext[] { frameCtx }); + fVariable = variable; + } + + String getVariable() { return fVariable; } + + @Override + public boolean equals(Object other) { + return super.baseEquals(other) && ((VariableDMContext)other).fVariable.equals(fVariable); + } + + @Override + public int hashCode() { + return super.baseHashCode() + fVariable.hashCode(); + } + + @Override + public String toString() { + return baseToString() + ".variable(" + fVariable + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + /** + * PDA variable data, only supports returning the variable name. + */ + @Immutable + private static class VariableDMData implements IVariableDMData { + + final private String fVariable; + + VariableDMData(String variable) { + fVariable = variable; + } + + public String getName() { + return fVariable; + } + + public String getValue() { + return null; + } + } + + // Services that this service depends on. + private PDACommandControl fCommandControl; + private IRunControl fRunControl; + + // Command cache + private CommandCache fCommandCache; + + public PDAStack(DsfSession session) { + super(session); + } + + @Override + protected BundleContext getBundleContext() { + return PDAPlugin.getBundleContext(); + } + + @Override + public void initialize(final RequestMonitor rm) { + super.initialize( + new RequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + doInitialize(rm); + }}); + } + + private void doInitialize(final RequestMonitor rm) { + // Initialize service references that stack service depends on + fCommandControl = getServicesTracker().getService(PDACommandControl.class); + fRunControl = getServicesTracker().getService(IRunControl.class); + + // Create the commands cache + fCommandCache = new CommandCache(getSession(), fCommandControl); + + // Register to listen for run control events, to clear cache accordingly. + getSession().addServiceEventListener(this, null); + + // Register stack service with OSGi + register(new String[]{IStack.class.getName(), PDAStack.class.getName()}, new Hashtable<String,String>()); + + rm.done(); + } + + @Override + public void shutdown(final RequestMonitor rm) { + getSession().removeServiceEventListener(this); + fCommandCache.reset(); + super.shutdown(rm); + } + + + public void getArguments(IFrameDMContext frameCtx, DataRequestMonitor<IVariableDMContext[]> rm) { + PDAPlugin.failRequest(rm, IDsfStatusConstants.NOT_SUPPORTED, "PDA debugger does not support function arguments."); + } + + public void getFrameData(final IFrameDMContext frameCtx, final DataRequestMonitor<IFrameDMData> rm) { + final PDAThreadDMContext threadCtx = + DMContexts.getAncestorOfType(frameCtx, PDAThreadDMContext.class); + + if (threadCtx == null) { + rm.setStatus(new Status(IStatus.ERROR, PDAPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context" + frameCtx, null)); + rm.done(); + return; + } + + getStackDepth( + threadCtx, -1, + new DataRequestMonitor<Integer>(getExecutor(), rm) { + @Override + protected void handleSuccess() { + // PDAFrame array is ordered highest to lowest. We need to + // calculate the index based on frame level. + int frameNum = getData() - frameCtx.getLevel() - 1; + if (frameNum < 0) { + PDAPlugin.failRequest(rm, IDsfStatusConstants.INVALID_HANDLE, "Invalid frame level " + frameCtx); + return; + } + + // Execute the PDA stack command, or retrieve the result from cache if already available. + fCommandCache.execute( + new PDAFrameCommand(threadCtx, frameNum), + new DataRequestMonitor<PDAFrameCommandResult>(getExecutor(), rm) { + @Override + protected void handleSuccess() { + // Create the frame data object based on the corresponding PDAFrame + rm.setData(new FrameDMData(getData().fFrame)); + rm.done(); + } + }); + } + }); + } + + + public void getFrames(IDMContext context, final DataRequestMonitor<IFrameDMContext[]> rm) { + // Can only create stack frames for an execution context as a parent, + // however the argument context is a generic context type, so it could + // be an execution context, a frame, a variable, etc. Search the + // hierarchy of the argument context to find the execution one. + final PDAThreadDMContext threadCtx = + DMContexts.getAncestorOfType(context, PDAThreadDMContext.class); + + if (threadCtx == null) { + rm.setStatus(new Status(IStatus.ERROR, PDAPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context" + context, null)); + rm.done(); + return; + } + + // Execute the stack command and create the corresponding frame contexts. + getStackDepth( + context, -1, + new DataRequestMonitor<Integer>(getExecutor(), rm) { + @Override + protected void handleSuccess() { + IFrameDMContext[] frameCtxs = new IFrameDMContext[getData()]; + for (int i = 0; i < getData(); i++) { + frameCtxs[i] = new FrameDMContext(getSession().getId(), threadCtx, i); + } + rm.setData(frameCtxs); + rm.done(); + } + }); + } + + public void getFrames(IDMContext context, final int startIndex, final int endIndex, final DataRequestMonitor<IFrameDMContext[]> rm) { + // Validate index range. + assert startIndex >=0 && (endIndex < 0 || startIndex <= endIndex); + + final PDAThreadDMContext threadCtx = + DMContexts.getAncestorOfType(context, PDAThreadDMContext.class); + + if (threadCtx == null) { + rm.setStatus(new Status(IStatus.ERROR, PDAPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context" + context, null)); + rm.done(); + return; + } + + // Execute the stack command and create the corresponding frame contexts. + getStackDepth( + context, -1, + new DataRequestMonitor<Integer>(getExecutor(), rm) { + @Override + protected void handleSuccess() { + int numFrames = endIndex < 0 + ? (getData() - startIndex) + : Math.min(endIndex + 1, getData()) - startIndex; + IFrameDMContext[] frameCtxs = new IFrameDMContext[numFrames]; + for (int i = 0; i < numFrames; i++) { + frameCtxs[i] = new FrameDMContext(getSession().getId(), threadCtx, startIndex + i); + } + rm.setData(frameCtxs); + rm.done(); + } + }); + } + + public void getLocals(IFrameDMContext context, final DataRequestMonitor<IVariableDMContext[]> rm) { + if (!(context instanceof FrameDMContext)) { + rm.setStatus(new Status(IStatus.ERROR, PDAPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context" + context, null)); + rm.done(); + return; + } + final FrameDMContext frameCtx = (FrameDMContext)context; + + final PDAThreadDMContext threadCtx = + DMContexts.getAncestorOfType(frameCtx, PDAThreadDMContext.class); + + if (threadCtx == null) { + rm.setStatus(new Status(IStatus.ERROR, PDAPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context" + frameCtx, null)); + rm.done(); + return; + } + + fCommandCache.execute( + new PDAStackCommand(threadCtx), + new DataRequestMonitor<PDAStackCommandResult>(getExecutor(), rm) { + @Override + protected void handleSuccess() { + // Find the correct PDAFrame + int frameId = getData().fFrames.length - frameCtx.getLevel() - 1; + if (frameId < 0) { + PDAPlugin.failRequest(rm, IDsfStatusConstants.INVALID_HANDLE, "Invalid frame level " + frameCtx); + return; + } + PDAFrame pdaFrame = getData().fFrames[frameId]; + + // Create variable contexts for all variables in frame. + IVariableDMContext[] variableCtxs = new IVariableDMContext[pdaFrame.fVariables.length]; + for (int i = 0; i < pdaFrame.fVariables.length; i++) { + variableCtxs[i] = new VariableDMContext(getSession().getId(), frameCtx, pdaFrame.fVariables[i]); + } + rm.setData(variableCtxs); + rm.done(); + } + }); + + } + + public void getStackDepth(IDMContext context, final int maxDepth, final DataRequestMonitor<Integer> rm) { + final PDAThreadDMContext threadCtx = + DMContexts.getAncestorOfType(context, PDAThreadDMContext.class); + + if (threadCtx == null) { + rm.setStatus(new Status(IStatus.ERROR, PDAPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context" + context, null)); + rm.done(); + return; + } + + // Execute stack command and return the data's size. + fCommandCache.execute( + new PDAStackDepthCommand(threadCtx), + new DataRequestMonitor<PDAStackDepthCommandResult>(getExecutor(), rm) { + @Override + protected void handleSuccess() { + int depth= getData().fDepth; + if (maxDepth > 0 && maxDepth < depth) { + depth = maxDepth; + } + rm.setData(depth); + rm.done(); + } + }); + } + + public void getTopFrame(IDMContext context, final DataRequestMonitor<IFrameDMContext> rm) { + // Can only create stack frames for an execution context as a parent, + // however the argument context is a generic context type, so it could + // be an execution context, a frame, a variable, etc. Search the + // hierarchy of the argument context to find the execution one. + final PDAThreadDMContext execCtx = DMContexts.getAncestorOfType(context, PDAThreadDMContext.class); + if (execCtx == null) { + PDAPlugin.failRequest(rm, IDsfStatusConstants.INVALID_HANDLE, "Invalid context " + context); + return; + } + + // Since the frame context only contain the level, there's no need to + // call the PDA debugger. Simply create a context for level 0. + rm.setData(new FrameDMContext(getSession().getId(), execCtx, 0)); + rm.done(); + } + + public void getVariableData(IVariableDMContext variableCtx, DataRequestMonitor<IVariableDMData> rm) { + if ( !(variableCtx instanceof VariableDMContext) ) { + PDAPlugin.failRequest(rm, IDsfStatusConstants.INVALID_HANDLE, "Invalid context " + variableCtx); + return; + } + + // The variable data doen't contain a value. So there's no need to + // go to the back end to retrieve it. + String variable = ((VariableDMContext)variableCtx).getVariable(); + + rm.setData(new VariableDMData(variable)); + rm.done(); + } + + public boolean isStackAvailable(IDMContext context) { + // Stack is available if the program is suspended or stepping. + IExecutionDMContext execCtx = DMContexts.getAncestorOfType(context, IExecutionDMContext.class); + return execCtx != null && (fRunControl.isSuspended(execCtx) || (fRunControl.isStepping(execCtx))); + } + + @SuppressWarnings("unchecked") + @Deprecated + public void getModelData(IDMContext dmc, DataRequestMonitor<?> rm) { + // The getModelData() is deprecated and clients are expected to switch + // to getExecutionData() and other data retrieve methods directly. + // However the UI cache still uses it for now. + if (dmc instanceof IFrameDMContext) { + getFrameData((IFrameDMContext)dmc, (DataRequestMonitor<IFrameDMData>)rm); + } else if (dmc instanceof IVariableDMContext) { + getVariableData((IVariableDMContext)dmc, (DataRequestMonitor<IVariableDMData>)rm); + } else { + PDAPlugin.failRequest(rm, IDsfStatusConstants.INVALID_HANDLE, "Unknown context type"); + } + } + + /** + * Returns a frame context for the given thread and level; + */ + public IFrameDMContext getFrameDMContext(PDAThreadDMContext thread, int level) { + return new FrameDMContext(getSession().getId(), thread, level); + } + + @DsfServiceEventHandler + public void eventDispatched(IResumedDMEvent e) { + // Mark the cache as not available, so that stack commands will + // fail. Also reset the cache unless it was a step command. + fCommandCache.setContextAvailable(e.getDMContext(), false); + if (!e.getReason().equals(StateChangeReason.STEP)) { + fCommandCache.reset(e.getDMContext()); + } + } + + + @DsfServiceEventHandler + public void eventDispatched(ISuspendedDMEvent e) { + // Enable sending commands to target and clear the cache. + fCommandCache.setContextAvailable(e.getDMContext(), true); + fCommandCache.reset(e.getDMContext()); + } + + public void flushCache(IDMContext context) { + fCommandCache.reset(context); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDAStartedEvent.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDAStartedEvent.java new file mode 100644 index 00000000000..36af8dc52ff --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDAStartedEvent.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service; + +import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IStartedDMEvent; + +/** + * Event issued when the PDA debugger is started. + */ +public class PDAStartedEvent extends AbstractDMEvent<IExecutionDMContext> + implements IStartedDMEvent +{ + PDAStartedEvent(PDAVirtualMachineDMContext context) { + super(context); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDATerminatedEvent.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDATerminatedEvent.java new file mode 100644 index 00000000000..cd17e2c7da0 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDATerminatedEvent.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service; + +import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExitedDMEvent; + +/** + * Event issued when the PDA debugger exits. + */ +public class PDATerminatedEvent extends AbstractDMEvent<IExecutionDMContext> + implements IExitedDMEvent +{ + PDATerminatedEvent(PDAVirtualMachineDMContext context) { + super(context); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDAThreadDMContext.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDAThreadDMContext.java new file mode 100644 index 00000000000..c7fde0b2a76 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDAThreadDMContext.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service; + +import org.eclipse.cdt.dsf.datamodel.AbstractDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; + +/** + * Context representing a PDA thread. + */ +public class PDAThreadDMContext extends AbstractDMContext + implements IExecutionDMContext +{ + final private Integer fID; + + public PDAThreadDMContext(String sessionId, PDAVirtualMachineDMContext vmCtx, int id) { + super(sessionId, new IDMContext[] { vmCtx }); + fID = id; + } + + public int getID() { + return fID; + } + + @Override + public String toString() { + return super.baseToString() + ".thread[" + fID + "]"; + } + + @Override + public int hashCode() { + return baseHashCode() + fID.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return baseEquals(obj) && ((PDAThreadDMContext)obj).fID.equals(fID); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDAVirtualMachineDMContext.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDAVirtualMachineDMContext.java new file mode 100644 index 00000000000..f42b286241b --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/PDAVirtualMachineDMContext.java @@ -0,0 +1,92 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service; + +import org.eclipse.cdt.dsf.datamodel.AbstractDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.runtime.PlatformObject; + +/** + * Top-level Data Model context for the PDA debugger representing the while PDA + * virtual machine. + * <p> + * The PDA debugger is a single-threaded application. Therefore this + * top level context implements IExecutionDMContext directly, hence this + * context can be used to call the IRunControl service to perform run + * control opreations. + * </p> + * <p> + * Also, the PDA debugger allows setting breakpoints in scope of the + * whole program only, so this context can be used with the breakpoints + * service to install/remove breakpoints. + * </p> + * <p> + * Note: There should only be one instance of PDAVirtualMachineDMContext + * created by each PDA command control, so its equals method defaults to using + * instance comparison. + * </p> + */ +public class PDAVirtualMachineDMContext extends PlatformObject + implements ICommandControlDMContext, IContainerDMContext, IBreakpointsTargetDMContext +{ + final static IDMContext[] EMPTY_PARENTS_ARRAY = new IDMContext[0]; + + final private String fSessionId; + final private String fProgram; + + public PDAVirtualMachineDMContext(String sessionId, String program) { + fSessionId = sessionId; + fProgram = program; + } + + public String getSessionId() { + return fSessionId; + } + + public String getProgram() { + return fProgram; + } + + public IDMContext[] getParents() { + return EMPTY_PARENTS_ARRAY; + } + + @Override + public String toString() { + return "pda[" + getSessionId() + "]"; + } + + public String getCommandControlId() { + return getProgram(); + } + + /** + * @see AbstractDMContext#getAdapter(Class) + */ + @Override + @SuppressWarnings("unchecked") + public Object getAdapter(Class adapterType) { + Object retVal = null; + DsfSession session = DsfSession.getSession(fSessionId); + if (session != null) { + retVal = session.getModelAdapter(adapterType); + } + if (retVal == null) { + retVal = super.getAdapter(adapterType); + } + return retVal; + } + +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/AbstractPDACommand.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/AbstractPDACommand.java new file mode 100644 index 00000000000..cd63bd8f7a6 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/AbstractPDACommand.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service.commands; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.command.ICommand; +import org.eclipse.cdt.dsf.debug.service.command.ICommandResult; + +/** + * Base class for PDA commands. The PDA commands consist of a text request and + * a context. Since the PDA debugger protocol is stateless, the context is only + * needed to satisfy the ICommand interface. + */ +@Immutable +abstract public class AbstractPDACommand<V extends PDACommandResult> implements ICommand<V> { + + final private IDMContext fContext; + final private String fRequest; + + public AbstractPDACommand(IDMContext context, String request) { + fContext = context; + fRequest = request; + } + + public IDMContext getContext() { + return fContext; + } + + public ICommand<? extends ICommandResult> coalesceWith(ICommand<? extends ICommandResult> command) { + return null; + } + + /** + * Returns the request to be sent to PDA. + */ + public String getRequest() { + return fRequest; + } + + /** + * Returns the command result based on the given PDA response. This command + * uses the class type parameter as the return type to allow the compiler to + * enforce the correct command result. This class must be implemented by + * each command to create the concrete result type. + */ + abstract public V createResult(String resultText); + + @Override + public boolean equals(Object obj) { + if (obj instanceof AbstractPDACommand) { + AbstractPDACommand<?> cmd = (AbstractPDACommand<?>)obj; + return fContext.equals(cmd.fContext) && fRequest.equals(cmd.fRequest); + } + return false; + } + + @Override + public int hashCode() { + return fContext.hashCode() + fRequest.hashCode(); + } + +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDABitField.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDABitField.java new file mode 100644 index 00000000000..6daee81d696 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDABitField.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service.commands; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.StringTokenizer; + +import org.eclipse.cdt.dsf.concurrent.Immutable; + +/** + * Object representing a bit field in the stack command results. + * + * @see PDARegistersCommand + */ +@Immutable +public class PDABitField { + + final public String fName; + final public int fOffset; + final public int fCount; + final public Map<String, String> fMnemonics; + + PDABitField(String bitFieldString) { + StringTokenizer st = new StringTokenizer(bitFieldString, " "); + + fName = st.nextToken(); + fOffset = Integer.parseInt(st.nextToken()); + fCount = Integer.parseInt(st.nextToken()); + + fMnemonics = new LinkedHashMap<String, String>(0); + while (st.hasMoreTokens()) { + fMnemonics.put(st.nextToken(), st.nextToken()); + } + } +}
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAChildrenCommand.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAChildrenCommand.java new file mode 100644 index 00000000000..fabfc7dd3bc --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAChildrenCommand.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service.commands; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.examples.dsf.pda.service.PDAThreadDMContext; + +/** + * Retrieves data stack information + * + * <pre> + * C: children {thread_id} {frame_id} {variable_name} + * R: {child variable 1}|{child variable 2}|{child variable 3}|...| + * + * Errors: + * error: invalid thread + * </pre> + */ +@Immutable +public class PDAChildrenCommand extends AbstractPDACommand<PDAListResult> { + + public PDAChildrenCommand(PDAThreadDMContext thread, int frameId, String name ) { + super(thread, "children " + thread.getID() + " " + frameId + " " + name); + } + + @Override + public PDAListResult createResult(String resultText) { + return new PDAListResult(resultText); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAClearBreakpointCommand.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAClearBreakpointCommand.java new file mode 100644 index 00000000000..2550af75deb --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAClearBreakpointCommand.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service.commands; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.examples.dsf.pda.service.PDAVirtualMachineDMContext; + +/** + * Clears any breakpoint set on given line + * + * <pre> + * C: clear {line} + * R: ok + * </pre> + + */ +@Immutable +public class PDAClearBreakpointCommand extends AbstractPDACommand<PDACommandResult> { + + public PDAClearBreakpointCommand(PDAVirtualMachineDMContext context, int line) { + super(context, "clear " + (line - 1)); + } + + @Override + public PDACommandResult createResult(String resultText) { + return new PDACommandResult(resultText); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDACommandResult.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDACommandResult.java new file mode 100644 index 00000000000..47fefea7c25 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDACommandResult.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service.commands; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.debug.service.command.ICommand; +import org.eclipse.cdt.dsf.debug.service.command.ICommandResult; + + +/** + * Basic command result object. This command result simply allows access to the + * PDA response. Sub-classes may override to optionally parse the response text + * and return higher-level objects. + */ +@Immutable +public class PDACommandResult implements ICommandResult { + + final public String fResponseText; + + public PDACommandResult(String response) { + fResponseText = response; + } + + public <V extends ICommandResult> V getSubsetResult(ICommand<V> command) { + return null; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof PDACommandResult) { + PDACommandResult result = (PDACommandResult)obj; + return fResponseText.equals(result.fResponseText); + } + return false; + } + + @Override + public int hashCode() { + return fResponseText.hashCode(); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDADataCommand.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDADataCommand.java new file mode 100644 index 00000000000..bc9adc3f500 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDADataCommand.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service.commands; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.examples.dsf.pda.service.PDAThreadDMContext; + +/** + * Retrieves data stack information + * + * <pre> + * C: data {thread_id} + * R: {value 1}|{value 2}|{value 3}|...| + * + * Errors: + * error: invalid thread + * </pre> + */ +@Immutable +public class PDADataCommand extends AbstractPDACommand<PDAListResult> { + + public PDADataCommand(PDAThreadDMContext thread) { + super(thread, "data " + thread.getID()); + } + + @Override + public PDAListResult createResult(String resultText) { + return new PDAListResult(resultText); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDADropFrameCommand.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDADropFrameCommand.java new file mode 100644 index 00000000000..43566d175f7 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDADropFrameCommand.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service.commands; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.examples.dsf.pda.service.PDAThreadDMContext; + +/** + * Returns from the current frame without executing the rest of instructions. + * + * <pre> + * If VM running: + * C: drop {thread_id} + * R: ok + * E: resumed {thread_id} drop + * E: suspended {thread_id} drop + * + * If VM suspended: + * C: drop {thread_id} + * R: ok + * E: vmresumed drop + * E: vmsuspended {thread_id} drop + * + * Errors: + * error: invalid thread + * </pre> + */ +@Immutable +public class PDADropFrameCommand extends AbstractPDACommand<PDACommandResult> { + + public PDADropFrameCommand(PDAThreadDMContext thread) { + super(thread, "drop " + thread.getID()); + } + + @Override + public PDACommandResult createResult(String resultText) { + return new PDACommandResult(resultText); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAEvalCommand.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAEvalCommand.java new file mode 100644 index 00000000000..9a7fea67f33 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAEvalCommand.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service.commands; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.examples.dsf.pda.service.PDAThreadDMContext; + +/** + * Sets what events cause the execution to stop. + * + * <pre> + * C: eval {thread_id} {instruction}%20{parameter}|{instruction}%20{parameter}|... + * R: ok + * E: resumed {thread_id} client + * E: evalresult result + * E: suspended {thread_id} eval + * + * Errors: + * error: invalid thread + * error: cannot evaluate while vm is suspended + * error: thread running + * </pre> + * + * Where event_name could be <code>unimpinstr</code> or <code>nosuchlabel</code>. + */ +@Immutable +public class PDAEvalCommand extends AbstractPDACommand<PDACommandResult> { + + public PDAEvalCommand(PDAThreadDMContext thread, String operation) { + super(thread, "eval " + thread.getID() + " " + operation); + } + + @Override + public PDACommandResult createResult(String resultText) { + return new PDACommandResult(resultText); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAEventStopCommand.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAEventStopCommand.java new file mode 100644 index 00000000000..3ca751f4fc2 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAEventStopCommand.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service.commands; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.examples.dsf.pda.service.PDAVirtualMachineDMContext; + +/** + * Sets what events cause the execution to stop. + * + * <pre> + * C: eventstop {event_name} {0|1} + * R: ok + * ... + * E: suspended event {event_name} + * </pre> + * + * Where event_name could be <code>unimpinstr</code> or <code>nosuchlabel</code>. + */ +@Immutable +public class PDAEventStopCommand extends AbstractPDACommand<PDACommandResult> { + + public enum Event { UNIMPINSTR, NOSUCHLABEL }; + + public PDAEventStopCommand(PDAVirtualMachineDMContext context, Event event, boolean enable) { + super(context, + "eventstop " + + (event == Event.UNIMPINSTR ? "unimpinstr " : "nosuchlabel ") + + (enable ? "1" : "0")); + } + + @Override + public PDACommandResult createResult(String resultText) { + return new PDACommandResult(resultText); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAExitCommand.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAExitCommand.java new file mode 100644 index 00000000000..7bc192f753e --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAExitCommand.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service.commands; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.examples.dsf.pda.service.PDAVirtualMachineDMContext; + +/** + * Instructs the debugger to exit. + * + * <pre> + * C: exit + * R: ok + * </pre> + */ +@Immutable +public class PDAExitCommand extends AbstractPDACommand<PDACommandResult> { + + public PDAExitCommand(PDAVirtualMachineDMContext context) { + super(context, "exit"); + } + + @Override + public PDACommandResult createResult(String resultText) { + return new PDACommandResult(resultText); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAFrame.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAFrame.java new file mode 100644 index 00000000000..9b6b0e230d4 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAFrame.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service.commands; + +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; + +/** + * Object representing a frame in the stack command results. + * + * @see PDAStackCommand + */ +@Immutable +public class PDAFrame { + + final public IPath fFilePath; + final public int fLine; + final public String fFunction; + final public String[] fVariables; + + PDAFrame(String frameString) { + StringTokenizer st = new StringTokenizer(frameString, "|"); + + fFilePath = new Path(st.nextToken()); + fLine = Integer.parseInt(st.nextToken()); + fFunction = st.nextToken(); + + List<String> variablesList = new ArrayList<String>(); + while (st.hasMoreTokens()) { + variablesList.add(st.nextToken()); + } + fVariables = variablesList.toArray(new String[variablesList.size()]); + } +}
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAFrameCommand.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAFrameCommand.java new file mode 100644 index 00000000000..ee62c5cfb2c --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAFrameCommand.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service.commands; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.examples.dsf.pda.service.PDAThreadDMContext; + +/** + * Retrieves command stack frame information + * + * <pre> + * C: stack {thread_id} {frame_number} + * R: {file}|{line}|{function}|{var_1}|{var_2}|... + * + * Errors: + * error: invalid thread + * </pre> + */ +@Immutable +public class PDAFrameCommand extends AbstractPDACommand<PDAFrameCommandResult> { + + public PDAFrameCommand(PDAThreadDMContext thread, int frameNum) { + super(thread, "frame " + thread.getID() + " " + frameNum); + } + + @Override + public PDAFrameCommandResult createResult(String resultText) { + return new PDAFrameCommandResult(resultText); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAFrameCommandResult.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAFrameCommandResult.java new file mode 100644 index 00000000000..e430d20ac77 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAFrameCommandResult.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.examples.dsf.pda.service.commands; + +import org.eclipse.cdt.dsf.concurrent.Immutable; + + +/** + * @see PDAFrameCommand + */ +@Immutable +public class PDAFrameCommandResult extends PDACommandResult { + + /** + * Frame data return by the frame command. + */ + final public PDAFrame fFrame; + + PDAFrameCommandResult(String response) { + super(response); + fFrame = new PDAFrame(response); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAGroupsCommand.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAGroupsCommand.java new file mode 100644 index 00000000000..1a70dd9e19c --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAGroupsCommand.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service.commands; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.examples.dsf.pda.service.PDAVirtualMachineDMContext; + +/** + * Retrieves register groups information + * + * <pre> + * C: groups + * R: {group 1}|{group 2}|{group 3}|...| + * </pre> + */ +@Immutable +public class PDAGroupsCommand extends AbstractPDACommand<PDAListResult> { + + public PDAGroupsCommand(PDAVirtualMachineDMContext context) { + super(context, "groups"); + } + + @Override + public PDAListResult createResult(String resultText) { + return new PDAListResult(resultText); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAListResult.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAListResult.java new file mode 100644 index 00000000000..64519d72727 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAListResult.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service.commands; + +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +import org.eclipse.cdt.dsf.concurrent.Immutable; + + +/** + * @see PDADataCommand + */ +@Immutable +public class PDAListResult extends PDACommandResult { + + final public String[] fValues; + + PDAListResult(String response) { + super(response); + StringTokenizer st = new StringTokenizer(response, "|"); + List<String> valuesList = new ArrayList<String>(); + + while (st.hasMoreTokens()) { + String token = st.nextToken(); + if (token.length() != 0) { + valuesList.add(token); + } + } + + fValues = new String[valuesList.size()]; + for (int i = 0; i < valuesList.size(); i++) { + fValues[i] = valuesList.get(i); + } + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAPopDataCommand.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAPopDataCommand.java new file mode 100644 index 00000000000..d2a7cae6215 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAPopDataCommand.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service.commands; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.examples.dsf.pda.service.PDAThreadDMContext; + +/** + * Pops the top value from the data stack + * + * <pre> + * C: popdata {thread_id} + * R: ok + * + * Errors: + * error: invalid thread + * </pre> + */ +@Immutable +public class PDAPopDataCommand extends AbstractPDACommand<PDACommandResult> { + + public PDAPopDataCommand(PDAThreadDMContext thread) { + super(thread, "popdata " + thread.getID()); + } + + @Override + public PDACommandResult createResult(String resultText) { + return new PDACommandResult(resultText); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAPushDataCommand.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAPushDataCommand.java new file mode 100644 index 00000000000..4237b5596e3 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAPushDataCommand.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service.commands; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.examples.dsf.pda.service.PDAThreadDMContext; + +/** + * Pushes the given value on top of the data stack. + * + * <pre> + * C: pushdata {thread_id} {value} + * R: ok + * + * Errors: + * error: invalid thread + * </pre> + */ +@Immutable +public class PDAPushDataCommand extends AbstractPDACommand<PDACommandResult> { + + public PDAPushDataCommand(PDAThreadDMContext thread, int value) { + super(thread, "pushdata " + thread.getID() + " " + value); + } + + @Override + public PDACommandResult createResult(String resultText) { + return new PDACommandResult(resultText); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDARegister.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDARegister.java new file mode 100644 index 00000000000..a76d9ec754f --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDARegister.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service.commands; + +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +import org.eclipse.cdt.dsf.concurrent.Immutable; + +/** + * Object representing a register in the registers command results. + * + * @see PDARCommand + */ +@Immutable +public class PDARegister { + + final public String fName; + final public boolean fWritable; + final public PDABitField[] fBitFields; + + PDARegister(String regString) { + StringTokenizer st = new StringTokenizer(regString, "|"); + + String regInfo = st.nextToken(); + StringTokenizer regSt = new StringTokenizer(regInfo, " "); + fName = regSt.nextToken(); + fWritable = Boolean.parseBoolean(regSt.nextToken()); + + List<PDABitField> bitFieldsList = new ArrayList<PDABitField>(); + while (st.hasMoreTokens()) { + bitFieldsList.add(new PDABitField(st.nextToken())); + } + fBitFields = bitFieldsList.toArray(new PDABitField[bitFieldsList.size()]); + } +}
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDARegistersCommand.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDARegistersCommand.java new file mode 100644 index 00000000000..ed72790b7a7 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDARegistersCommand.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service.commands; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.examples.dsf.pda.service.PDAThreadDMContext; + +/** + * Retrieves registers definition information + * + * <pre> + * C: registers {group name} + * R: {register name} {true|false}|{bit field name} {start bit} {bit count} {mnemonic 1} {mnemonic 2} ...#{register name} ... + * </pre> + */ +@Immutable +public class PDARegistersCommand extends AbstractPDACommand<PDARegistersCommandResult> { + + public PDARegistersCommand(PDAThreadDMContext context, String group) { + super(context, "registers " + group); + } + + @Override + public PDARegistersCommandResult createResult(String resultText) { + return new PDARegistersCommandResult(resultText); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDARegistersCommandResult.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDARegistersCommandResult.java new file mode 100644 index 00000000000..7a25ae85149 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDARegistersCommandResult.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service.commands; + +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +import org.eclipse.cdt.dsf.concurrent.Immutable; + + +/** + * @see PDARegistersCommand + */ +@Immutable +public class PDARegistersCommandResult extends PDACommandResult { + + /** + * Array of registers returned by the registers commands. + */ + final public PDARegister[] fRegisters; + + PDARegistersCommandResult(String response) { + super(response); + StringTokenizer st = new StringTokenizer(response, "#"); + List<PDARegister> regList = new ArrayList<PDARegister>(); + + while (st.hasMoreTokens()) { + regList.add(new PDARegister(st.nextToken())); + } + fRegisters = regList.toArray(new PDARegister[regList.size()]); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAResumeCommand.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAResumeCommand.java new file mode 100644 index 00000000000..4dfc585754f --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAResumeCommand.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service.commands; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.examples.dsf.pda.service.PDAThreadDMContext; + +/** + * Resumes the execution of a single thread. Can be issued only if the virtual + * machine is running. + * + * <pre> + * C: resume {thread_id} + * R: ok + * E: resumed {thread_id} client + * + * Errors: + * error: invalid thread + * error: cannot resume thread when vm is suspended + * error: thread already running + * </pre> + */ +@Immutable +public class PDAResumeCommand extends AbstractPDACommand<PDACommandResult> { + + public PDAResumeCommand(PDAThreadDMContext thread) { + super(thread, "resume " + thread.getID()); + } + + @Override + public PDACommandResult createResult(String resultText) { + return new PDACommandResult(resultText); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDASetBreakpointCommand.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDASetBreakpointCommand.java new file mode 100644 index 00000000000..53d62a2f4f6 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDASetBreakpointCommand.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service.commands; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.examples.dsf.pda.service.PDAVirtualMachineDMContext; + +/** + * Sets a breakpoint at given line + * + * <pre> + * Suspend a single thread: + * C: set {line_number} 0 + * R: ok + * C: resume {thread_id} + * E: resumed {thread_id} client + * E: suspended {thread_id} breakpoint line_number + * + * Suspend the VM: + * C: set {line_number} 1 + * R: ok + * C: vmresume + * E: vmresumed client + * E: vmsuspended {thread_id} breakpoint line_number + * </pre> + */ +@Immutable +public class PDASetBreakpointCommand extends AbstractPDACommand<PDACommandResult> { + + public PDASetBreakpointCommand(PDAVirtualMachineDMContext context, int line, boolean stopVM) { + super(context, + "set " + + (line - 1) + " " + + (stopVM ? "1" : "0")); + } + + @Override + public PDACommandResult createResult(String resultText) { + return new PDACommandResult(resultText); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDASetDataCommand.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDASetDataCommand.java new file mode 100644 index 00000000000..e6b54aacd52 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDASetDataCommand.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service.commands; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.examples.dsf.pda.service.PDAThreadDMContext; + +/** + * Sets a data value in the data stack at the given location + * + * <pre> + * C: setdata {thread_id} {index} {value} + * R: ok + * + * Errors: + * error: invalid thread + * </pre> + */ +@Immutable +public class PDASetDataCommand extends AbstractPDACommand<PDACommandResult> { + + public PDASetDataCommand(PDAThreadDMContext thread, int index, String value) { + super(thread, "setdata " + thread.getID() + " " + index + " " + value); + } + + @Override + public PDACommandResult createResult(String resultText) { + return new PDACommandResult(resultText); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDASetVarCommand.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDASetVarCommand.java new file mode 100644 index 00000000000..2cab2417249 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDASetVarCommand.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service.commands; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.examples.dsf.pda.service.PDAThreadDMContext; + +/** + * Sets a variable value + * + * <pre> + * C: setvar {thread_id} {frame_number} {variable} {value} + * R: ok + * + * Errors: + * error: invalid thread + * </pre> + */ +@Immutable +public class PDASetVarCommand extends AbstractPDACommand<PDACommandResult> { + + public PDASetVarCommand(PDAThreadDMContext thread, int frame, String variable, String value) { + super(thread, "setvar " + thread.getID() + " " + frame + " " + variable + " " + value); + } + + @Override + public PDACommandResult createResult(String resultText) { + return new PDACommandResult(resultText); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAStackCommand.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAStackCommand.java new file mode 100644 index 00000000000..57e90b8979e --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAStackCommand.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service.commands; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.examples.dsf.pda.service.PDAThreadDMContext; + +/** + * Retrieves command stack information + * + * <pre> + * C: stack {thread_id} + * R: {file}|{line}|{function}|{var_1}|{var_2}|...#{file}|{line}|{function}|{var_1}|{var_2}|...#... + * + * Errors: + * error: invalid thread + * </pre> + */ +@Immutable +public class PDAStackCommand extends AbstractPDACommand<PDAStackCommandResult> { + + public PDAStackCommand(PDAThreadDMContext thread) { + super(thread, "stack " + thread.getID()); + } + + @Override + public PDAStackCommandResult createResult(String resultText) { + return new PDAStackCommandResult(resultText); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAStackCommandResult.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAStackCommandResult.java new file mode 100644 index 00000000000..d7e1a0c256c --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAStackCommandResult.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service.commands; + +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +import org.eclipse.cdt.dsf.concurrent.Immutable; + + +/** + * @see PDAStackCommand + */ +@Immutable +public class PDAStackCommandResult extends PDACommandResult { + + /** + * Array of frames return by the stack commands. The frames are ordered + * with the highest-level frame first. + */ + final public PDAFrame[] fFrames; + + PDAStackCommandResult(String response) { + super(response); + StringTokenizer st = new StringTokenizer(response, "#"); + List<PDAFrame> framesList = new ArrayList<PDAFrame>(); + + while (st.hasMoreTokens()) { + framesList.add(new PDAFrame(st.nextToken())); + } + fFrames = framesList.toArray(new PDAFrame[framesList.size()]); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAStackDepthCommand.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAStackDepthCommand.java new file mode 100644 index 00000000000..225f5c36a0f --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAStackDepthCommand.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service.commands; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.examples.dsf.pda.service.PDAThreadDMContext; + +/** + * Retrieves command stack depth + * + * <pre> + * C: stackdepth {thread_id} + * R: {depth} + * + * Errors: + * error: invalid thread + * </pre> + */ +@Immutable +public class PDAStackDepthCommand extends AbstractPDACommand<PDAStackDepthCommandResult> { + + public PDAStackDepthCommand(PDAThreadDMContext thread) { + super(thread, "stackdepth " + thread.getID()); + } + + @Override + public PDAStackDepthCommandResult createResult(String resultText) { + return new PDAStackDepthCommandResult(resultText); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAStackDepthCommandResult.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAStackDepthCommandResult.java new file mode 100644 index 00000000000..d2e1b452990 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAStackDepthCommandResult.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.examples.dsf.pda.service.commands; + +import org.eclipse.cdt.dsf.concurrent.Immutable; + + +/** + * @see PDADataCommand + */ +@Immutable +public class PDAStackDepthCommandResult extends PDACommandResult { + + final public int fDepth; + + PDAStackDepthCommandResult(String response) { + super(response); + int depth = 1; // default to something that won't cause NPEs + try { + depth = Integer.parseInt(response); + } catch (NumberFormatException e) {} + fDepth = depth; + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAStepCommand.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAStepCommand.java new file mode 100644 index 00000000000..3fc8514bd82 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAStepCommand.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service.commands; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.examples.dsf.pda.service.PDAThreadDMContext; + +/** + * Executes next instruction + * + * <pre> + * If VM running: + * C: step {thread_id} + * R: ok + * E: resumed {thread_id} client + * E: suspended {thread_id} step + * + * If VM suspended: + * C: step {thread_id} + * R: ok + * E: vmresumed client + * E: vmsuspended {thread_id} step + * + * Errors: + * error: invalid thread + * </pre> + */ +@Immutable +public class PDAStepCommand extends AbstractPDACommand<PDACommandResult> { + + public PDAStepCommand(PDAThreadDMContext thread) { + super(thread, "step " + thread.getID()); + } + + @Override + public PDACommandResult createResult(String resultText) { + return new PDACommandResult(resultText); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAStepReturnCommand.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAStepReturnCommand.java new file mode 100644 index 00000000000..9a45f8782c1 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAStepReturnCommand.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service.commands; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.examples.dsf.pda.service.PDAThreadDMContext; + +/** + * Executes instructions until the current subroutine is finished + * + * <pre> + * If VM running: + * C: stepreturn {thread_id} + * R: ok + * E: resumed {thread_id} client + * E: suspended {thread_id} step + * + * If VM suspended: + * C: stepreturn {thread_id} + * R: ok + * E: vmresumed client + * E: vmsuspended {thread_id} step + * + * Errors: + * error: invalid thread + * </pre> + */ +@Immutable +public class PDAStepReturnCommand extends AbstractPDACommand<PDACommandResult> { + + public PDAStepReturnCommand(PDAThreadDMContext thread) { + super(thread, "stepreturn " + thread.getID()); + } + + @Override + public PDACommandResult createResult(String resultText) { + return new PDACommandResult(resultText); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDASuspendCommand.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDASuspendCommand.java new file mode 100644 index 00000000000..cdc125985bf --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDASuspendCommand.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service.commands; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.examples.dsf.pda.service.PDAThreadDMContext; + +/** + * Suspends execution of a single thread. Can be issued only if the virtual + * machine is running. + * + * <pre> + * C: suspend {thread_id} + * R: ok + * E: suspended {thread_id} client + * + * Errors: + * error: invalid thread + error: vm already suspended + * error: thread already suspended + * </pre> + */ +@Immutable +public class PDASuspendCommand extends AbstractPDACommand<PDACommandResult> { + + public PDASuspendCommand(PDAThreadDMContext thread) { + super(thread, "suspend " + thread.getID()); + } + + @Override + public PDACommandResult createResult(String resultText) { + return new PDACommandResult(resultText); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAVMResumeCommand.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAVMResumeCommand.java new file mode 100644 index 00000000000..48ff4108455 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAVMResumeCommand.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service.commands; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.examples.dsf.pda.service.PDAVirtualMachineDMContext; + +/** + * Resumes the execution of the whole virtual machine + * + * <pre> + * C: vmresume + * R: ok + * E: vmresumed client + * + * Errors: + * error: vm already running + * </pre> + */ +@Immutable +public class PDAVMResumeCommand extends AbstractPDACommand<PDACommandResult> { + + public PDAVMResumeCommand(PDAVirtualMachineDMContext context) { + super(context, "vmresume"); + } + + @Override + public PDACommandResult createResult(String resultText) { + return new PDACommandResult(resultText); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAVMSuspendCommand.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAVMSuspendCommand.java new file mode 100644 index 00000000000..11259a8f9fc --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAVMSuspendCommand.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service.commands; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.examples.dsf.pda.service.PDAVirtualMachineDMContext; + +/** + * Suspends the execution of the whole virtual machine + * + * <pre> + * C: vmsuspend + * R: ok + * E: vmsuspended client + * + * Errors: + * error: thread already suspended + * </pre> + */ +@Immutable +public class PDAVMSuspendCommand extends AbstractPDACommand<PDACommandResult> { + + public PDAVMSuspendCommand(PDAVirtualMachineDMContext context) { + super(context, "vmsuspend"); + } + + @Override + public PDACommandResult createResult(String resultText) { + return new PDACommandResult(resultText); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAVarCommand.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAVarCommand.java new file mode 100644 index 00000000000..a6c15e1a45b --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAVarCommand.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service.commands; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.examples.dsf.pda.service.PDAThreadDMContext; + +/** + * Retrieves variable value + * + * <pre> + * C: var {thread_id} {frame_number} {variable_name} + * R: {variable_value} + * + * Errors: + * error: invalid thread + * error: variable undefined + * </pre> + */ +@Immutable +public class PDAVarCommand extends AbstractPDACommand<PDACommandResult> { + + public PDAVarCommand(PDAThreadDMContext thread, int frameId, String name) { + super(thread, "var " + thread.getID() + " " + frameId + " " + name); + } + + @Override + public PDACommandResult createResult(String resultText) { + return new PDACommandResult(resultText); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAWatchCommand.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAWatchCommand.java new file mode 100644 index 00000000000..5bc90bcbb02 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/service/commands/PDAWatchCommand.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * 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.examples.dsf.pda.service.commands; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.examples.dsf.pda.service.PDAVirtualMachineDMContext; + +/** + * Sets a watchpoint on a given variable + * + * <pre> + * C: watch {function}::{variable_name} {watch_operation} + * R: ok + * C: vmresume + * R: vmresumed client + * E: vmsuspended {thread_id} watch {watch_operation} {function}::{variable_name} + * </pre> + */ +@Immutable +public class PDAWatchCommand extends AbstractPDACommand<PDACommandResult> { + + public enum WatchOperation { READ, WRITE, BOTH, NONE }; + + private static int getWatchOperationCode(WatchOperation operation) { + switch (operation) { + case READ: + return 1; + case WRITE: + return 2; + case BOTH: + return 3; + default: + return 0; + } + } + + public PDAWatchCommand(PDAVirtualMachineDMContext context, String function, String variable, WatchOperation operation) { + super(context, "watch " + function+ "::" + variable + " " + getWatchOperationCode(operation)); + } + + @Override + public PDACommandResult createResult(String resultText) { + return new PDACommandResult(resultText); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/sourcelookup/PDASourceLookupDirector.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/sourcelookup/PDASourceLookupDirector.java new file mode 100644 index 00000000000..750ce9701c2 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/sourcelookup/PDASourceLookupDirector.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 + * Bjorn Freeman-Benson - initial API and implementation + * Wind River Systems - adopted to use with DSF + *******************************************************************************/ +package org.eclipse.cdt.examples.dsf.pda.sourcelookup; + +import org.eclipse.debug.core.sourcelookup.AbstractSourceLookupDirector; + +/** + * PDA source lookup director. + */ +public class PDASourceLookupDirector extends AbstractSourceLookupDirector { + public void initializeParticipants() { + // No need to add participants here, the source display adapter will + // add the participant with the correct session ID. + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/sourcelookup/PDASourcePathComputerDelegate.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/sourcelookup/PDASourcePathComputerDelegate.java new file mode 100644 index 00000000000..6977bc29c96 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/examples/dsf/pda/sourcelookup/PDASourcePathComputerDelegate.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 + * Bjorn Freeman-Benson - initial API and implementation + * Wind River Systems - adopted to use with DSF + *******************************************************************************/ +package org.eclipse.cdt.examples.dsf.pda.sourcelookup; + +import org.eclipse.cdt.examples.dsf.pda.PDAPlugin; +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.Path; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.sourcelookup.ISourceContainer; +import org.eclipse.debug.core.sourcelookup.ISourcePathComputerDelegate; +import org.eclipse.debug.core.sourcelookup.containers.FolderSourceContainer; +import org.eclipse.debug.core.sourcelookup.containers.ProjectSourceContainer; +import org.eclipse.debug.core.sourcelookup.containers.WorkspaceSourceContainer; + + +/** + * Computes the default source lookup path for a PDA launch configuration. + * The default source lookup path is the folder or project containing + * the PDA program being launched. If the program is not specified, the workspace + * is searched by default. + * <p> + * This class is identical to the corresponding in PDA debugger implemented in + * org.eclipse.debug.examples. + * </p> + */ +public class PDASourcePathComputerDelegate implements ISourcePathComputerDelegate { + + public ISourceContainer[] computeSourceContainers(ILaunchConfiguration configuration, IProgressMonitor monitor) throws CoreException { + String path = configuration.getAttribute(PDAPlugin.ATTR_PDA_PROGRAM, (String)null); + ISourceContainer sourceContainer = null; + if (path != null) { + IResource resource = ResourcesPlugin.getWorkspace().getRoot().findMember(new Path(path)); + if (resource != null) { + IContainer container = resource.getParent(); + if (container.getType() == IResource.PROJECT) { + sourceContainer = new ProjectSourceContainer((IProject)container, false); + } else if (container.getType() == IResource.FOLDER) { + sourceContainer = new FolderSourceContainer(container, false); + } + } + } + if (sourceContainer == null) { + sourceContainer = new WorkspaceSourceContainer(); + } + return new ISourceContainer[]{sourceContainer}; + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/service/command/BasicTests.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/service/command/BasicTests.java new file mode 100644 index 00000000000..51557bbe9e5 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/service/command/BasicTests.java @@ -0,0 +1,134 @@ +/******************************************************************************* + * 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.tests.dsf.pda.service.command; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.ExecutionException; + +import junit.framework.Assert; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.Query; +import org.eclipse.cdt.dsf.debug.service.command.ICommand; +import org.eclipse.cdt.dsf.debug.service.command.ICommandListener; +import org.eclipse.cdt.dsf.debug.service.command.ICommandResult; +import org.eclipse.cdt.dsf.debug.service.command.ICommandToken; +import org.eclipse.cdt.examples.dsf.pda.service.commands.PDACommandResult; +import org.eclipse.core.runtime.CoreException; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * + */ +public class BasicTests extends CommandControlTestsBase { + + @BeforeClass + public static void setProgram() { + fProgram = "samples/example.pda"; + } + + @Test + public void testCommandListener() throws CoreException, InterruptedException, ExecutionException { + + class CommandInfo { + CommandInfo(ICommand<?> command, ICommandResult result) { fCommand = command; fResult = result; } + ICommand<?> fCommand; + ICommandResult fResult; + } + + class CommandListener implements ICommandListener { + + List<CommandInfo> fDoneCommands = new LinkedList<CommandInfo>(); + List<CommandInfo> fQueuedCommands = new LinkedList<CommandInfo>(); + List<CommandInfo> fRemovedCommands = new LinkedList<CommandInfo>(); + List<CommandInfo> fSentCommands = new LinkedList<CommandInfo>(); + + public void commandDone(ICommandToken token, ICommandResult result) { + fDoneCommands.add(new CommandInfo(token.getCommand(), result)); + } + public void commandQueued(ICommandToken token) { + fQueuedCommands.add(new CommandInfo(token.getCommand(), null)); + } + public void commandRemoved(ICommandToken token) { + fRemovedCommands.add(new CommandInfo(token.getCommand(), null)); + } + public void commandSent(ICommandToken token) { + fSentCommands.add(new CommandInfo(token.getCommand(), null)); + } + + void reset() { + fDoneCommands.clear(); + fQueuedCommands.clear(); + fRemovedCommands.clear(); + fSentCommands.clear(); + } + } + + final CommandListener listener = new CommandListener(); + fExecutor.execute(new DsfRunnable() { + public void run() { + fCommandControl.addCommandListener(listener); + } + }); + + final PDATestCommand testCommand = new PDATestCommand(fCommandControl.getContext(), "data 1"); + + // Test sending the command and checking all listeners were called. + Query<PDACommandResult> sendCommandQuery = new Query<PDACommandResult>() { + @Override + protected void execute(DataRequestMonitor<PDACommandResult> rm) { + fCommandControl.queueCommand(testCommand, rm); + } + }; + fExecutor.execute(sendCommandQuery); + PDACommandResult result = sendCommandQuery.get(); + Assert.assertEquals(1, listener.fQueuedCommands.size()); + Assert.assertEquals(testCommand, listener.fQueuedCommands.get(0).fCommand); + Assert.assertEquals(0, listener.fRemovedCommands.size()); + Assert.assertEquals(1, listener.fSentCommands.size()); + Assert.assertEquals(testCommand, listener.fSentCommands.get(0).fCommand); + Assert.assertEquals(1, listener.fDoneCommands.size()); + Assert.assertEquals(testCommand, listener.fDoneCommands.get(0).fCommand); + Assert.assertEquals(result, listener.fDoneCommands.get(0).fResult); + + // Test queuing then removing command + listener.reset(); + Query<Object> queueRemoveCommandQuery = new Query<Object>() { + @Override + protected void execute(DataRequestMonitor<Object> rm) { + ICommandToken token = fCommandControl.queueCommand( + testCommand, + new DataRequestMonitor<PDACommandResult>(fExecutor, null) { + @Override + protected void handleCompleted() { + Assert.fail("This command should never have been executed."); + } + }); + fCommandControl.removeCommand(token); + + rm.setData(new Object()); + rm.done(); + } + }; + fExecutor.execute(queueRemoveCommandQuery); + queueRemoveCommandQuery.get(); + Assert.assertEquals(1, listener.fQueuedCommands.size()); + Assert.assertEquals(testCommand, listener.fQueuedCommands.get(0).fCommand); + Assert.assertEquals(1, listener.fRemovedCommands.size()); + Assert.assertEquals(testCommand, listener.fRemovedCommands.get(0).fCommand); + Assert.assertEquals(0, listener.fSentCommands.size()); + Assert.assertEquals(0, listener.fDoneCommands.size()); + + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/service/command/CommandControlTestsBase.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/service/command/CommandControlTestsBase.java new file mode 100644 index 00000000000..c4d2e7b4690 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/service/command/CommandControlTestsBase.java @@ -0,0 +1,165 @@ +/******************************************************************************* + * 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.tests.dsf.pda.service.command; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.LinkedBlockingQueue; + +import junit.framework.Assert; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DefaultDsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.Query; +import org.eclipse.cdt.dsf.debug.service.command.IEventListener; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.examples.dsf.pda.service.PDABackend; +import org.eclipse.cdt.examples.dsf.pda.service.PDACommandControl; +import org.eclipse.cdt.examples.dsf.pda.service.commands.PDACommandResult; +import org.eclipse.cdt.tests.dsf.pda.util.Launching; +import org.eclipse.core.runtime.CoreException; +import org.junit.After; +import org.junit.Before; + +/** + * + */ +public class CommandControlTestsBase { + + protected static String fProgram; + + protected DsfExecutor fExecutor; + protected DsfSession fSession; + protected PDABackend fPDABackend; + protected PDACommandControl fCommandControl; + private BlockingQueue<Object> fEventsQueue = new LinkedBlockingQueue<Object>(); + + private BufferedReader fOutputReader; + + @Before + public void startup() throws CoreException, InterruptedException, ExecutionException, IOException { + + class InitializeCommandServiceQuery extends Query<Object> { + @Override + protected void execute(DataRequestMonitor<Object> rm) { + fCommandControl.initialize(rm); + } + }; + + fExecutor = new DefaultDsfExecutor(); + fSession = DsfSession.startSession(fExecutor, "PDA Test"); + + Process proc = Launching.launchPDA(fSession, null, fProgram); + Assert.assertNotNull(proc); + + // Remember the backend service of this session. + // Note this must be called after the above LaunchPDA(). + fPDABackend = Launching.getBackendService(); + + fOutputReader = new BufferedReader(new InputStreamReader(proc.getInputStream())); + Assert.assertTrue(fOutputReader.readLine().contains("-debug")); + + fCommandControl = new PDACommandControl(fSession); + + fCommandControl.addEventListener(new IEventListener() { + public void eventReceived(Object output) { + fEventsQueue.add(output); + } + }); + + InitializeCommandServiceQuery initQuery = new InitializeCommandServiceQuery(); + fExecutor.execute(initQuery); + initQuery.get(); + Assert.assertEquals("debug connection accepted", fOutputReader.readLine()); + } + + @After + public void shutdown() throws CoreException, InterruptedException, ExecutionException, IOException { + if (fOutputReader != null) { + fOutputReader.close(); + } + + class ShutdownCommandServiceQuery extends Query<Object> { + @Override + protected void execute(DataRequestMonitor<Object> rm) { + fCommandControl.shutdown(rm); + } + }; + + if (fExecutor != null) { + ShutdownCommandServiceQuery shutdownQuery = new ShutdownCommandServiceQuery(); + fExecutor.execute(shutdownQuery); + shutdownQuery.get(); + } + + class ShutdownBackendServiceQuery extends Query<Object> { + @Override + protected void execute(DataRequestMonitor<Object> rm) { + fPDABackend.shutdown(rm); + } + }; + + if (fExecutor != null) { + ShutdownBackendServiceQuery shutdownQuery = new ShutdownBackendServiceQuery(); + fExecutor.execute(shutdownQuery); + shutdownQuery.get(); + } + } + + protected void sendCommand(String command) throws Throwable { + sendCommand(command, "ok"); + } + + protected void sendCommand(String command, String expectedResult) throws Throwable { + + final PDATestCommand testCommand = new PDATestCommand(fCommandControl.getContext(), command); + + // Test sending the command and checking all listeners were called. + Query<PDACommandResult> sendCommandQuery = new Query<PDACommandResult>() { + @Override + protected void execute(DataRequestMonitor<PDACommandResult> rm) { + fCommandControl.queueCommand(testCommand, rm); + } + }; + + String responseText = null; + fExecutor.execute(sendCommandQuery); + try { + PDACommandResult result = sendCommandQuery.get(); + responseText = result.fResponseText; + } catch (ExecutionException e) { + if (e.getCause() instanceof CoreException) { + responseText = ((CoreException)e.getCause()).getStatus().getMessage(); + } else { + throw e.getCause(); + } + } + Assert.assertEquals("Command returned an unexpected result", expectedResult, responseText); + + } + + protected void clearEvents() { + fEventsQueue.clear(); + } + + protected void expectEvent(String expectedEvent) throws InterruptedException { + Assert.assertEquals("Unexpected event received", expectedEvent, fEventsQueue.take()); + } + + protected void expectOutput(String expectedOutput) throws IOException { + Assert.assertEquals("Unexpected output received", expectedOutput, fOutputReader.readLine()); + } + +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/service/command/PDATestCommand.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/service/command/PDATestCommand.java new file mode 100644 index 00000000000..236457a4858 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/service/command/PDATestCommand.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.tests.dsf.pda.service.command; + +import org.eclipse.cdt.examples.dsf.pda.service.PDAVirtualMachineDMContext; +import org.eclipse.cdt.examples.dsf.pda.service.commands.AbstractPDACommand; +import org.eclipse.cdt.examples.dsf.pda.service.commands.PDACommandResult; + +/** + * + */ +class PDATestCommand extends AbstractPDACommand<PDACommandResult> { + PDATestCommand(PDAVirtualMachineDMContext context, String command) { + super(context, command); + } + + @Override + public PDACommandResult createResult(String resultText) { + return new PDACommandResult(resultText); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/service/command/PDATestEvent.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/service/command/PDATestEvent.java new file mode 100644 index 00000000000..325972537d8 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/service/command/PDATestEvent.java @@ -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 + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.pda.service.command; + +/** + * + */ +public class PDATestEvent { + final public String fEventText; + PDATestEvent(String event) { + fEventText = event; + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/service/command/Test1.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/service/command/Test1.java new file mode 100644 index 00000000000..c82179c8d80 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/service/command/Test1.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * 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.tests.dsf.pda.service.command; + +import java.io.File; + +import org.eclipse.cdt.examples.dsf.pda.PDAPlugin; +import org.eclipse.core.runtime.Path; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * + */ +public class Test1 extends CommandControlTestsBase { + + @BeforeClass + public static void setProgram() { + File programFile = PDAPlugin.getFileInPlugin(new Path("samples/example.pda")); + fProgram = programFile.getPath(); + } + + @Test + public void testRun() throws Throwable { + sendCommand("vmresume"); + expectOutput("\"hello\""); + expectOutput("\"barfoo\""); + expectOutput("\"first\""); + expectOutput("\"second\""); + expectOutput("12"); + expectOutput("11"); + expectOutput("10"); + expectOutput("\"barfoo\""); + expectOutput("\"first\""); + expectOutput("\"second\""); + expectOutput("\"end\""); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/service/command/Test10.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/service/command/Test10.java new file mode 100644 index 00000000000..bce14a6b9a7 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/service/command/Test10.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * 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.tests.dsf.pda.service.command; + +import java.io.File; + +import org.eclipse.cdt.examples.dsf.pda.PDAPlugin; +import org.eclipse.core.runtime.Path; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * + */ +public class Test10 extends CommandControlTestsBase { + + @BeforeClass + public static void setProgram() { + File programFile = PDAPlugin.getFileInPlugin(new Path("pdavm/tests/vmtest10.pda")); + + fProgram = programFile.getPath(); + } + + @Test + public void testRegisters() throws Throwable { + expectEvent("started 1"); + // run to the end of register definitions + sendCommand("set 10 1"); + sendCommand("vmresume"); + expectEvent("vmresumed client"); + expectEvent("registers"); + expectEvent("registers"); + expectEvent("registers"); + expectEvent("registers"); + expectEvent("registers"); + expectEvent("registers"); + expectEvent("registers"); + expectEvent("registers"); + expectEvent("registers"); + expectEvent("vmsuspended 1 breakpoint 10"); + + // Test the definitions commands + sendCommand("groups", "group1|group2|"); + sendCommand("registers group1", "reg1 true|field1 0 2 |field2 2 2 zero 0 one 1 two 2 three 3 #reg2 false#"); + sendCommand("registers group2", "reg3 true#"); + + // Run to the end of the program + sendCommand("set 37 1"); + sendCommand("vmresume"); + expectEvent("vmresumed client"); + expectOutput("1"); + expectOutput("2"); + expectOutput("0"); + expectOutput("4"); + expectOutput("0"); + expectOutput("0"); + expectOutput("2"); + expectOutput("8"); + expectEvent("vmsuspended 1 breakpoint 37"); + + // Test var get/set commands + sendCommand("var 1 1 $reg1", "8"); + sendCommand("var 1 1 $reg1.field1", "0"); + sendCommand("var 1 1 $reg1.field2", "2"); + sendCommand("setvar 1 1 $reg1.field2 3"); + sendCommand("var 1 1 $reg1.field2", "3"); + sendCommand("setvar 1 1 $reg1 1"); + sendCommand("var 1 1 $reg1", "1"); + + // exit + sendCommand("exit"); + expectEvent("terminated"); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/service/command/Test2.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/service/command/Test2.java new file mode 100644 index 00000000000..e145ef30128 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/service/command/Test2.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.tests.dsf.pda.service.command; + +import java.io.File; + +import org.eclipse.cdt.examples.dsf.pda.PDAPlugin; +import org.eclipse.core.runtime.Path; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * + */ +public class Test2 extends CommandControlTestsBase { + + @BeforeClass + public static void setProgram() { + File programFile = PDAPlugin.getFileInPlugin(new Path("pdavm/tests/vmtest2.pda")); + + fProgram = programFile.getPath(); + } + + @Test + public void testCommonDebugCommands() throws Throwable { + expectEvent("started 1"); + // test step + sendCommand("step 1"); + expectEvent("vmresumed step"); + expectEvent("vmsuspended 1 step"); + // test breakpoint + sendCommand("set 4 1"); + sendCommand("data 1", "6|"); + sendCommand("vmresume"); + expectEvent("vmresumed client"); + expectEvent("vmsuspended 1 breakpoint 4"); + // test data stack + sendCommand("data 1", "6|7|8|9|"); + sendCommand("popdata 1"); + sendCommand("data 1", "6|7|8|"); + sendCommand("pushdata 1 11"); + sendCommand("data 1", "6|7|8|11|"); + sendCommand("setdata 1 1 2"); + sendCommand("data 1", "6|2|8|11|"); + // test call stack + sendCommand("set 12 1"); + sendCommand("set 19 1"); + sendCommand("stepreturn 1"); + expectEvent("vmresumed step"); + expectEvent("vmsuspended 1 breakpoint 12"); + sendCommand("clear 19"); + sendCommand("stack 1", fProgram + "|6|main#" + fProgram + "|18|sub1|m|n#" + fProgram + "|12|sub2" ); + sendCommand("stackdepth 1", "3"); + sendCommand("frame 1 0", fProgram + "|6|main"); + sendCommand("frame 1 1", fProgram + "|18|sub1|m|n"); + sendCommand("frame 1 2", fProgram + "|12|sub2" ); + sendCommand("stepreturn 1"); + expectEvent("vmresumed step"); + expectEvent("vmsuspended 1 step"); + sendCommand("stack 1", fProgram + "|6|main#" + fProgram + "|18|sub1|m|n#" + fProgram + "|13|sub2" ); + sendCommand("stepreturn 1"); + expectEvent("vmresumed step"); + expectEvent("vmsuspended 1 step"); + sendCommand("stack 1", fProgram + "|6|main#" + fProgram + "|22|sub1|m|n" ); + sendCommand("set 6 1"); + sendCommand("stepreturn 1"); + expectEvent("vmresumed step"); + expectEvent("vmsuspended 1 breakpoint 6"); + // test set and clear + sendCommand("set 27 1"); + sendCommand("set 29 1"); + sendCommand("set 33 1"); + sendCommand("vmresume"); + expectEvent("vmresumed client"); + expectEvent("vmsuspended 1 breakpoint 33"); + sendCommand("vmresume"); + expectEvent("vmresumed client"); + expectEvent("vmsuspended 1 breakpoint 27"); + sendCommand("clear 33"); + sendCommand("vmresume"); + expectEvent("vmresumed client"); + expectEvent("vmsuspended 1 breakpoint 29"); + // test var and setvar + sendCommand("set 47 1"); + sendCommand("vmresume"); + expectEvent("vmresumed client"); + expectEvent("vmsuspended 1 breakpoint 47"); + sendCommand("var 1 1 b", "4"); + sendCommand("var 1 2 b", "2"); + sendCommand("var 1 1 a", "0"); + sendCommand("setvar 1 1 a 99"); + sendCommand("data 1", "6|2|8|11|27|1|4|"); + sendCommand("step 1"); + expectEvent("vmresumed step"); + expectEvent("vmsuspended 1 step"); + sendCommand("var 1 1 a", "99"); + sendCommand("step 1"); + expectEvent("vmresumed step"); + expectEvent("vmsuspended 1 step"); + sendCommand("data 1", "6|2|8|11|27|1|4|99|"); + sendCommand("var 1 1 x", "error: variable undefined"); + sendCommand("setvar 1 1 x 100"); + sendCommand("var 1 1 x", "100"); + // test exit + sendCommand("exit"); + expectEvent("terminated"); + } + + @Test + public void testCommonDebugCommandsWithThreadRC() throws Throwable { + expectEvent("started 1"); + // test breakpoint + sendCommand("set 3 0"); + sendCommand("vmresume"); + expectEvent("vmresumed client"); + expectEvent("suspended 1 breakpoint 3"); + sendCommand("data 1", "6|7|8|"); + // test step + sendCommand("step 1"); + expectEvent("resumed 1 step"); + expectEvent("suspended 1 step"); + // test data stack + sendCommand("data 1", "6|7|8|9|"); + sendCommand("popdata 1"); + sendCommand("data 1", "6|7|8|"); + sendCommand("pushdata 1 11"); + sendCommand("data 1", "6|7|8|11|"); + sendCommand("setdata 1 1 2"); + sendCommand("data 1", "6|2|8|11|"); + // test call stack + sendCommand("set 12 0"); + sendCommand("set 19 0"); + sendCommand("stepreturn 1"); + expectEvent("resumed 1 step"); + expectEvent("suspended 1 breakpoint 12"); + sendCommand("clear 19"); + sendCommand("stack 1", fProgram + "|6|main#" + fProgram + "|18|sub1|m|n#" + fProgram + "|12|sub2" ); + sendCommand("stackdepth 1", "3"); + sendCommand("frame 1 0", fProgram + "|6|main"); + sendCommand("frame 1 1", fProgram + "|18|sub1|m|n"); + sendCommand("frame 1 2", fProgram + "|12|sub2" ); + sendCommand("stepreturn 1"); + expectEvent("resumed 1 step"); + expectEvent("suspended 1 step"); + sendCommand("stack 1", fProgram + "|6|main#" + fProgram + "|18|sub1|m|n#" + fProgram + "|13|sub2" ); + sendCommand("stepreturn 1"); + expectEvent("resumed 1 step"); + expectEvent("suspended 1 step"); + sendCommand("stack 1", fProgram + "|6|main#" + fProgram + "|22|sub1|m|n" ); + sendCommand("set 6 0"); + sendCommand("stepreturn 1"); + expectEvent("resumed 1 step"); + expectEvent("suspended 1 breakpoint 6"); + // test set and clear + sendCommand("set 27 0"); + sendCommand("set 29 0"); + sendCommand("set 33 0"); + sendCommand("resume 1"); + expectEvent("resumed 1 client"); + expectEvent("suspended 1 breakpoint 33"); + sendCommand("resume 1"); + expectEvent("resumed 1 client"); + expectEvent("suspended 1 breakpoint 27"); + sendCommand("clear 33"); + sendCommand("resume 1"); + expectEvent("resumed 1 client"); + expectEvent("suspended 1 breakpoint 29"); + // test var and setvar + sendCommand("set 47 0"); + sendCommand("resume 1"); + expectEvent("resumed 1 client"); + expectEvent("suspended 1 breakpoint 47"); + sendCommand("var 1 1 b", "4"); + sendCommand("var 1 2 b", "2"); + sendCommand("var 1 1 a", "0"); + sendCommand("setvar 1 1 a 99"); + sendCommand("data 1", "6|2|8|11|27|1|4|"); + sendCommand("step 1"); + expectEvent("resumed 1 step"); + expectEvent("suspended 1 step"); + sendCommand("var 1 1 a", "99"); + sendCommand("step 1"); + expectEvent("resumed 1 step"); + expectEvent("suspended 1 step"); + sendCommand("data 1", "6|2|8|11|27|1|4|99|"); + // test exit + sendCommand("exit"); + expectEvent("terminated"); + } + +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/service/command/Test3.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/service/command/Test3.java new file mode 100644 index 00000000000..0569b7b26a3 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/service/command/Test3.java @@ -0,0 +1,90 @@ +/******************************************************************************* + * 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.tests.dsf.pda.service.command; + +import java.io.File; + +import org.eclipse.cdt.examples.dsf.pda.PDAPlugin; +import org.eclipse.core.runtime.Path; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * + */ +public class Test3 extends CommandControlTestsBase { + + @BeforeClass + public static void setProgram() { + File programFile = PDAPlugin.getFileInPlugin(new Path("pdavm/tests/vmtest3.pda")); + + fProgram = programFile.getPath(); + } + + @Test + public void testUncaughtEvents() throws Throwable { + expectEvent("started 1"); + sendCommand("vmresume"); + expectEvent("vmresumed client"); + expectEvent("unimplemented instruction foobar"); + expectEvent("no such label zippy"); + expectEvent("no such label swishy"); + expectEvent("exited 1"); + expectEvent("terminated"); + } + + @Test + public void testCaughtUnimpinstrEvents() throws Throwable { + expectEvent("started 1"); + sendCommand("eventstop unimpinstr 1"); + sendCommand("vmresume"); + expectEvent("vmresumed client"); + expectEvent("unimplemented instruction foobar"); + expectEvent("vmsuspended 1 event unimpinstr"); + sendCommand("eventstop unimpinstr 0"); + sendCommand("vmresume"); + expectEvent("vmresumed client"); + expectEvent("unimplemented instruction foobar"); + expectEvent("no such label zippy"); + expectEvent("no such label swishy"); + expectEvent("exited 1"); + expectEvent("terminated"); + } + + @Test + public void testCaughtNosuchlabelEvents() throws Throwable { + expectEvent("started 1"); + sendCommand("eventstop nosuchlabel 1"); + sendCommand("vmresume"); + expectEvent("vmresumed client"); + expectEvent("unimplemented instruction foobar"); + expectEvent("no such label zippy"); + expectEvent("vmsuspended 1 event nosuchlabel"); + sendCommand("eventstop nosuchlabel 0"); + sendCommand("set 11 1"); + sendCommand("vmresume"); + expectEvent("vmresumed client"); + expectEvent("no such label zippy"); + expectEvent("vmsuspended 1 breakpoint 11"); + sendCommand("eventstop nosuchlabel 1"); + sendCommand("vmresume"); + expectEvent("vmresumed client"); + expectEvent("no such label swishy"); + expectEvent("vmsuspended 1 event nosuchlabel"); + sendCommand("eventstop nosuchlabel 0"); + sendCommand("vmresume"); + expectEvent("vmresumed client"); + expectEvent("no such label swishy"); + expectEvent("exited 1"); + expectEvent("terminated"); + } + +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/service/command/Test6.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/service/command/Test6.java new file mode 100644 index 00000000000..d1241a98a1f --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/service/command/Test6.java @@ -0,0 +1,84 @@ +/******************************************************************************* + * 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.tests.dsf.pda.service.command; + +import java.io.File; + +import org.eclipse.cdt.examples.dsf.pda.PDAPlugin; +import org.eclipse.core.runtime.Path; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * + */ +public class Test6 extends CommandControlTestsBase { + + @BeforeClass + public static void setProgram() { + File programFile = PDAPlugin.getFileInPlugin(new Path("pdavm/tests/vmtest6.pda")); + + fProgram = programFile.getPath(); + } + + @Test + public void testWatchPoints() throws Throwable { + expectEvent("started 1"); + sendCommand("watch inner::a 1"); + sendCommand("watch main::a 2"); + sendCommand("vmresume"); + expectEvent("vmresumed client"); + expectEvent("vmsuspended 1 watch write main::a"); + sendCommand("stack 1", fProgram + "|4|main|a|b"); + sendCommand("vmresume"); + expectEvent("vmresumed client"); + expectEvent("vmsuspended 1 watch read inner::a"); + sendCommand("stack 1", fProgram + "|10|main|a|b#" + fProgram + "|25|inner|a|c"); + sendCommand("watch inner::a 0"); + sendCommand("vmresume"); + expectEvent("vmresumed client"); + expectEvent("exited 1"); + expectEvent("terminated"); + } + + @Test + public void testEval() throws Throwable { + expectEvent("started 1"); + + sendCommand("eval 1 test_error", "error: cannot evaluate while vm is suspended"); + + sendCommand("set 25 0"); + sendCommand("vmresume"); + expectEvent("vmresumed client"); + expectEvent("suspended 1 breakpoint 25"); + + sendCommand("eval 1 push%204|push%205|add"); + expectEvent("resumed 1 eval"); + expectEvent("evalresult 9"); + expectEvent("suspended 1 eval"); + + sendCommand("step 1"); + expectEvent("resumed 1 step"); + expectEvent("suspended 1 step"); + sendCommand("stack 1", fProgram + "|10|main|a|b#" + fProgram + "|26|inner|a|c"); + sendCommand("data 1", "4|4|"); + sendCommand("eval 1 call%20other"); + expectEvent("resumed 1 eval"); + expectEvent("evalresult 15"); + expectEvent("suspended 1 eval"); + sendCommand("stack 1", fProgram + "|10|main|a|b#" + fProgram + "|26|inner|a|c"); + sendCommand("data 1", "4|4|"); + sendCommand("resume 1"); + expectEvent("resumed 1 client"); + expectEvent("exited 1"); + expectEvent("terminated"); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/service/command/Test8.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/service/command/Test8.java new file mode 100644 index 00000000000..d09e8bf80ac --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/service/command/Test8.java @@ -0,0 +1,94 @@ +/******************************************************************************* + * 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.tests.dsf.pda.service.command; + +import java.io.File; + +import org.eclipse.cdt.examples.dsf.pda.PDAPlugin; +import org.eclipse.core.runtime.Path; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * + */ +public class Test8 extends CommandControlTestsBase { + + @BeforeClass + public static void setProgram() { + File programFile = PDAPlugin.getFileInPlugin(new Path("pdavm/tests/vmtest8.pda")); + + fProgram = programFile.getPath(); + } + + @Test + public void testDropFrame() throws Throwable { + expectEvent("started 1"); + sendCommand("step 1"); + expectEvent("vmresumed step"); + expectEvent("vmsuspended 1 step"); + sendCommand("step 1"); + expectEvent("vmresumed step"); + expectEvent("vmsuspended 1 step"); + sendCommand("step 1"); + expectEvent("vmresumed step"); + expectEvent("vmsuspended 1 step"); + sendCommand("step 1"); + expectEvent("vmresumed step"); + expectEvent("vmsuspended 1 step"); + sendCommand("step 1"); + expectEvent("vmresumed step"); + expectEvent("vmsuspended 1 step"); + sendCommand("step 1"); + expectEvent("vmresumed step"); + expectEvent("vmsuspended 1 step"); + sendCommand("step 1"); + expectEvent("vmresumed step"); + expectEvent("vmsuspended 1 step"); + sendCommand("stack 1", fProgram + "|2|main|a#" + fProgram + "|8|inner|b#" + fProgram + "|12|inner2|c"); + sendCommand("drop 1"); + expectEvent("vmresumed drop"); + expectEvent("vmsuspended 1 drop"); + sendCommand("stack 1", fProgram + "|2|main|a#" + fProgram + "|7|inner|b"); + sendCommand("step 1"); + expectEvent("vmresumed step"); + expectEvent("vmsuspended 1 step"); + sendCommand("stack 1", fProgram + "|2|main|a#" + fProgram + "|8|inner|b#" + fProgram + "|10|inner2"); + sendCommand("vmresume"); + expectEvent("vmresumed client"); + expectEvent("exited 1"); + expectEvent("terminated"); + } + + @Test + public void testDropFrameWithThreadRC() throws Throwable { + expectEvent("started 1"); + sendCommand("set 12 0"); + sendCommand("vmresume"); + expectEvent("vmresumed client"); + expectEvent("suspended 1 breakpoint 12"); + sendCommand("stack 1", fProgram + "|2|main|a#" + fProgram + "|8|inner|b#" + fProgram + "|12|inner2|c"); + sendCommand("drop 1"); + expectEvent("resumed 1 drop"); + expectEvent("suspended 1 drop"); + sendCommand("stack 1", fProgram + "|2|main|a#" + fProgram + "|7|inner|b"); + sendCommand("step 1"); + expectEvent("resumed 1 step"); + expectEvent("suspended 1 step"); + sendCommand("stack 1", fProgram + "|2|main|a#" + fProgram + "|8|inner|b#" + fProgram + "|10|inner2"); + sendCommand("clear 12"); + sendCommand("resume 1"); + expectEvent("resumed 1 client"); + expectEvent("exited 1"); + expectEvent("terminated"); + } + +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/service/command/Test9.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/service/command/Test9.java new file mode 100644 index 00000000000..b9b92631d38 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/service/command/Test9.java @@ -0,0 +1,192 @@ +/******************************************************************************* + * 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.tests.dsf.pda.service.command; + +import java.io.File; + +import org.eclipse.cdt.examples.dsf.pda.PDAPlugin; +import org.eclipse.core.runtime.Path; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * + */ +public class Test9 extends CommandControlTestsBase { + + @BeforeClass + public static void setProgram() { + File programFile = PDAPlugin.getFileInPlugin(new Path("pdavm/tests/vmtest9.pda")); + + fProgram = programFile.getPath(); + } + + @Test + public void testThreadsWithVMRC() throws Throwable { + expectEvent("started 1"); + sendCommand("state", "client"); + sendCommand("state 1", "vm"); + + // Check error responses + sendCommand("vmsuspend", "error: vm already suspended"); + sendCommand("resume 1", "error: cannot resume thread when vm is suspended"); + + // Run to thread create routine + sendCommand("threads", "1"); + sendCommand("set 2 1"); + sendCommand("vmresume"); + expectEvent("vmresumed client"); + expectEvent("vmsuspended 1 breakpoint 2"); + sendCommand("state", "1 breakpoint 2"); + sendCommand("state 1", "vm"); + + // Step over first thread create + sendCommand("step 1"); + expectEvent("vmresumed step"); + expectEvent("started 2"); + expectEvent("vmsuspended 1 step"); + sendCommand("state", "1 step"); + sendCommand("state 1", "vm"); + sendCommand("threads", "1 2"); + sendCommand("stack 1", fProgram + "|3|main"); + sendCommand("stack 2", fProgram + "|9|foo"); + sendCommand("step 1"); + expectEvent("vmresumed step"); + expectEvent("vmsuspended 1 step"); + sendCommand("stack 1", fProgram + "|4|main"); + sendCommand("stack 2", fProgram + "|10|foo"); + sendCommand("vmresume"); + expectEvent("vmresumed client"); + expectOutput("thread_created"); + expectEvent("vmsuspended 1 breakpoint 2"); + + // Step over second thread create + sendCommand("step 2"); + expectEvent("vmresumed step"); + expectEvent("started 3"); + expectEvent("vmsuspended 2 step"); + sendCommand("threads", "1 2 3"); + sendCommand("stack 1", fProgram + "|3|main"); + sendCommand("stack 2", fProgram + "|13|foo#" + fProgram + "|15|inner"); + sendCommand("stack 3", fProgram + "|9|foo"); + sendCommand("step 3"); + expectEvent("vmresumed step"); + expectEvent("vmsuspended 3 step"); + sendCommand("stack 1", fProgram + "|4|main"); + sendCommand("stack 2", fProgram + "|13|foo#" + fProgram + "|16|inner|b"); + sendCommand("stack 3", fProgram + "|10|foo"); + + // Run to the end and watch threads starting/exiting. + sendCommand("clear 2"); + sendCommand("vmresume"); + expectOutput("thread_created"); + expectEvent("vmresumed client"); + expectEvent("started 4"); + expectEvent("exited 2"); + expectEvent("started 5"); + expectEvent("exited 3"); + expectEvent("started 6"); + expectEvent("exited 4"); + expectEvent("exited 1"); + expectEvent("exited 5"); + expectEvent("exited 6"); + expectEvent("terminated"); + } + + @Test + public void testThreadsWithThreadRC() throws Throwable { + expectEvent("started 1"); + + // Check error responses for thread run control + sendCommand("set 1 0"); + sendCommand("vmresume"); + expectEvent("vmresumed client"); + expectEvent("suspended 1 breakpoint 1"); + sendCommand("state", "running"); + sendCommand("state 1", "breakpoint 1"); + + sendCommand("resume", "error: invalid thread"); + sendCommand("vmresume", "error: vm already running"); + sendCommand("clear 1"); + sendCommand("suspend 1", "error: thread already suspended"); + sendCommand("vmsuspend"); + expectEvent("vmsuspended client"); + sendCommand("state", "client"); + sendCommand("state 1", "vm"); + sendCommand("suspend 1", "error: vm already suspended"); + sendCommand("resume 1", "error: cannot resume thread when vm is suspended"); + + // Create breakpoints at thread create and thread entry point. + sendCommand("set 2 0"); + sendCommand("set 10 0"); + sendCommand("vmresume"); + expectEvent("vmresumed client"); + expectEvent("suspended 1 breakpoint 2"); + + // Create first thread, and run it to completion + sendCommand("resume 1"); + expectEvent("resumed 1 client"); + expectEvent("started 2"); + expectEvent("suspended 2 breakpoint 10"); + expectEvent("suspended 1 breakpoint 2"); + sendCommand("state 1", "breakpoint 2"); + sendCommand("state 2", "breakpoint 10"); + sendCommand("threads", "1 2"); + sendCommand("resume 2"); + expectEvent("resumed 2 client"); + expectEvent("exited 2"); + sendCommand("threads", "1"); + + // Create second thread, step it + sendCommand("resume 1"); + expectEvent("resumed 1 client"); + expectEvent("started 3"); + expectEvent("suspended 3 breakpoint 10"); + expectEvent("suspended 1 breakpoint 2"); + sendCommand("threads", "1 3"); + sendCommand("stack 1", fProgram + "|2|main"); + sendCommand("stack 3", fProgram + "|10|foo"); + sendCommand("step 3"); + expectEvent("resumed 3 step"); + expectEvent("suspended 3 step"); + sendCommand("state 1", "breakpoint 2"); + sendCommand("state 3", "step"); + sendCommand("stack 1", fProgram + "|2|main"); + sendCommand("stack 3", fProgram + "|11|foo"); + + // Create the rest of threads + sendCommand("resume 1"); + expectEvent("resumed 1 client"); + expectEvent("started 4"); + expectEvent("suspended 4 breakpoint 10"); + expectEvent("suspended 1 breakpoint 2"); + sendCommand("threads", "1 3 4"); + sendCommand("resume 1"); + expectEvent("resumed 1 client"); + expectEvent("started 5"); + expectEvent("suspended 5 breakpoint 10"); + expectEvent("suspended 1 breakpoint 2"); + sendCommand("threads", "1 3 4 5"); + sendCommand("resume 1"); + expectEvent("resumed 1 client"); + + // Main thread exits + expectEvent("started 6"); + expectEvent("suspended 6 breakpoint 10"); + expectEvent("exited 1"); + sendCommand("threads", "3 4 5 6"); + + // Exit + sendCommand("exit"); + expectEvent("terminated"); + } + +} diff --git a/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/util/Launching.java b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/util/Launching.java new file mode 100644 index 00000000000..9917854a05b --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf.pda/src/org/eclipse/cdt/tests/dsf/pda/util/Launching.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * 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 + * Nokia - create and use backend service. + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.pda.util; + +import java.util.concurrent.ExecutionException; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Query; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.examples.dsf.pda.service.PDABackend; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.Launch; + +/** + * + */ +public class Launching { + + private static PDABackend fBackendService; + + public static Process launchPDA(DsfSession session, Launch launch, String pdaProgram) throws CoreException { + + class InitializeBackendServiceQuery extends Query<Object> { + @Override + protected void execute(DataRequestMonitor<Object> rm) { + fBackendService.initialize(rm); + } + }; + + fBackendService = new PDABackend(session, launch, pdaProgram); + InitializeBackendServiceQuery initQuery = new InitializeBackendServiceQuery(); + session.getExecutor().execute(initQuery); + try { + initQuery.get(); + } catch (InterruptedException e) { + e.printStackTrace(); + return null; + } catch (ExecutionException e) { + e.printStackTrace(); + return null; + } + + return fBackendService.getProcess(); + } + + public static PDABackend getBackendService() { + return fBackendService; + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/.classpath b/dsf/org.eclipse.cdt.examples.dsf/.classpath new file mode 100644 index 00000000000..5f6c0064a2e --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/.classpath @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/> + <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> + <classpathentry kind="src" path="src_ant"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/dsf/org.eclipse.cdt.examples.dsf/.externalToolBuilders/PreProcessor.launch b/dsf/org.eclipse.cdt.examples.dsf/.externalToolBuilders/PreProcessor.launch new file mode 100644 index 00000000000..dbd1fe8affe --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/.externalToolBuilders/PreProcessor.launch @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<launchConfiguration type="org.eclipse.ant.AntBuilderLaunchConfigurationType"> +<booleanAttribute key="org.eclipse.ant.ui.ATTR_TARGETS_UPDATED" value="true"/> +<booleanAttribute key="org.eclipse.ant.ui.DEFAULT_VM_INSTALL" value="false"/> +<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS"/> +<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES"/> +<booleanAttribute key="org.eclipse.debug.ui.ATTR_LAUNCH_IN_BACKGROUND" value="false"/> +<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="org.eclipse.ant.ui.AntClasspathProvider"/> +<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="true"/> +<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value=""/> +<stringAttribute key="org.eclipse.ui.externaltools.ATTR_BUILD_SCOPE" value="${working_set:<?xml version="1.0" encoding="UTF-8"?> <launchConfigurationWorkingSet editPageId="org.eclipse.ui.resourceWorkingSetPage" factoryID="org.eclipse.ui.internal.WorkingSetFactory" label="workingSet" name="workingSet"> <item factoryID="org.eclipse.ui.internal.model.ResourceFactory" path="/org.eclipse.dd.examples.dsf/src_preprocess" type="2"/> </launchConfigurationWorkingSet>}"/> +<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/org.eclipse.cdt.examples.dsf/build_preprocess.xml}"/> +<stringAttribute key="org.eclipse.ui.externaltools.ATTR_RUN_BUILD_KINDS" value="full,incremental,"/> +<booleanAttribute key="org.eclipse.ui.externaltools.ATTR_TRIGGERS_CONFIGURED" value="true"/> +</launchConfiguration> diff --git a/dsf/org.eclipse.cdt.examples.dsf/.project b/dsf/org.eclipse.cdt.examples.dsf/.project new file mode 100644 index 00000000000..c3e61028def --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/.project @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>org.eclipse.cdt.examples.dsf</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.ManifestBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.SchemaBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.ui.externaltools.ExternalToolBuilder</name> + <triggers>full,incremental,</triggers> + <arguments> + <dictionary> + <key>LaunchConfigHandle</key> + <value><project>/.externalToolBuilders/PreProcessor.launch</value> + </dictionary> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.pde.PluginNature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/dsf/org.eclipse.cdt.examples.dsf/.settings/org.eclipse.jdt.core.prefs b/dsf/org.eclipse.cdt.examples.dsf/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..2c5e26be9e4 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,71 @@ +#Tue Jun 24 11:03:17 PDT 2008 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.5 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.autoboxing=ignore +org.eclipse.jdt.core.compiler.problem.deprecation=warning +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning +org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=warning +org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore +org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore +org.eclipse.jdt.core.compiler.problem.nullReference=error +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning +org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeUncheckedExceptions=disabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled +org.eclipse.jdt.core.compiler.problem.unusedImport=error +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=warning +org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.source=1.5 diff --git a/dsf/org.eclipse.cdt.examples.dsf/META-INF/MANIFEST.MF b/dsf/org.eclipse.cdt.examples.dsf/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..94ceea3145d --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/META-INF/MANIFEST.MF @@ -0,0 +1,18 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %pluginName +Bundle-Vendor: %providerName +Bundle-SymbolicName: org.eclipse.cdt.examples.dsf;singleton:=true +Bundle-Version: 2.0.0.qualifier +Bundle-Activator: org.eclipse.cdt.examples.dsf.DsfExamplesPlugin +Bundle-Localization: plugin +Require-Bundle: org.eclipse.ui, + org.eclipse.core.runtime, + org.eclipse.debug.core, + org.eclipse.debug.ui, + org.eclipse.cdt.dsf, + org.eclipse.ui, + org.eclipse.cdt.dsf.ui, + org.apache.ant;bundle-version="1.7.0";resolution:=optional +Eclipse-LazyStart: true +Bundle-RequiredExecutionEnvironment: J2SE-1.5 diff --git a/dsf/org.eclipse.cdt.examples.dsf/about.html b/dsf/org.eclipse.cdt.examples.dsf/about.html new file mode 100644 index 00000000000..cb740ae8bc8 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/about.html @@ -0,0 +1,24 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"><head> + + +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>About</title></head><body lang="EN-US"> +<h2>About This Content</h2> + +<p>June 5, 2007</p> +<h3>License</h3> + +<p>The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>. +For purposes of the EPL, "Program" will mean the Content.</p> + +<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p> + +</body></html>
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.examples.dsf/build.properties b/dsf/org.eclipse.cdt.examples.dsf/build.properties new file mode 100644 index 00000000000..e061b4e53d0 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/build.properties @@ -0,0 +1,17 @@ +############################################################################### +# 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 +############################################################################### +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + plugin.xml,\ + plugin.properties,\ + about.html diff --git a/dsf/org.eclipse.cdt.examples.dsf/build_preprocess.xml b/dsf/org.eclipse.cdt.examples.dsf/build_preprocess.xml new file mode 100644 index 00000000000..6183be5cf6c --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/build_preprocess.xml @@ -0,0 +1,54 @@ +<?xml version="1.0"?> +<!-- ====================================================================== + Copyright (c) 2005, 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 DSF + + EclipseCon + DSF Tutorial Exercises + ====================================================================== --> +<project name="EclipseCon" default="generateAll"> + <description> + DSF Tutorial Exercises + </description> + + <taskdef name="preprocess" classname="org.eclipse.cdt.examples.ant.tasks.PreProcessor" classpath="bin" /> + <property name="$workspace" location="c:\eclipse-dev\dev-3.4" /> + <property name="srcBase" location="${workspace}/org.eclipse.cdt.examples.dsf/src_preprocess" /> + <property name="destBase" location="${workspace}/org.eclipse.cdt.examples.dsf/src" /> + + <!-- ================================= + target: generateAll + ================================= --> + <target name="generateAll" description="--> DSF Tutorial Exercises"> + + <!-- = = = = = = = = = = = = = = = = = + macrodef: process + = = = = = = = = = = = = = = = = = --> + <macrodef name="process"> + <attribute name="packagedir"/> + <sequential> + <delete dir="src/@{packagedir}"/> + <mkdir dir="src/@{packagedir}"/> + <mkdir dir="src/@{packagedir}/answers"/> + <preprocess destdir="src/@{packagedir}" symbols="exercises"> + <fileset dir="src_preprocess/@{packagedir}"/> + </preprocess> + <preprocess destdir="src/@{packagedir}/answers" symbols="answers"> + <fileset dir="src_preprocess/@{packagedir}"/> + </preprocess> + </sequential> + </macrodef> + + <process packagedir="org/eclipse/cdt/examples/dsf/requestmonitor"/> + <process packagedir="org/eclipse/cdt/examples/dsf/dataviewer"/> + </target> + +</project> + diff --git a/dsf/org.eclipse.cdt.examples.dsf/icons/alarm.gif b/dsf/org.eclipse.cdt.examples.dsf/icons/alarm.gif Binary files differnew file mode 100644 index 00000000000..33cc76e9dc5 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/icons/alarm.gif diff --git a/dsf/org.eclipse.cdt.examples.dsf/icons/alarm_triggered.gif b/dsf/org.eclipse.cdt.examples.dsf/icons/alarm_triggered.gif Binary files differnew file mode 100644 index 00000000000..609dbb7269c --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/icons/alarm_triggered.gif diff --git a/dsf/org.eclipse.cdt.examples.dsf/icons/layout.gif b/dsf/org.eclipse.cdt.examples.dsf/icons/layout.gif Binary files differnew file mode 100644 index 00000000000..4a07fffc16a --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/icons/layout.gif diff --git a/dsf/org.eclipse.cdt.examples.dsf/icons/remove.gif b/dsf/org.eclipse.cdt.examples.dsf/icons/remove.gif Binary files differnew file mode 100644 index 00000000000..2cd9c544436 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/icons/remove.gif diff --git a/dsf/org.eclipse.cdt.examples.dsf/icons/sample.gif b/dsf/org.eclipse.cdt.examples.dsf/icons/sample.gif Binary files differnew file mode 100644 index 00000000000..34fb3c9d8cb --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/icons/sample.gif diff --git a/dsf/org.eclipse.cdt.examples.dsf/icons/timer.gif b/dsf/org.eclipse.cdt.examples.dsf/icons/timer.gif Binary files differnew file mode 100644 index 00000000000..6089d528ce0 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/icons/timer.gif diff --git a/dsf/org.eclipse.cdt.examples.dsf/plugin.properties b/dsf/org.eclipse.cdt.examples.dsf/plugin.properties new file mode 100644 index 00000000000..79a07a28bc3 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/plugin.properties @@ -0,0 +1,13 @@ +############################################################################### +# 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 +############################################################################### +pluginName=Debugger Services Framework Examples +providerName=Eclipse.org + diff --git a/dsf/org.eclipse.cdt.examples.dsf/plugin.xml b/dsf/org.eclipse.cdt.examples.dsf/plugin.xml new file mode 100644 index 00000000000..afe5a8574a2 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/plugin.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<?eclipse version="3.2"?> +<plugin> + + <extension + point="org.eclipse.ui.views"> + <category + name="DSF Examples" + id="org.eclipse.cdt.examples.dsf"> + </category> + <view + name="Timers View" + icon="icons/timer.gif" + category="org.eclipse.cdt.examples.dsf" + class="org.eclipse.cdt.examples.dsf.timers.TimersView" + id="org.eclipse.cdt.examples.dsf.TimersView"> + </view> + </extension> + <extension + point="org.eclipse.ui.actionSets"> + <actionSet + id="org.eclipse.cdt.dsf.test.actionSet" + label="DSF Examples"> + <menu + id="org.eclipse.cdt.examples.dsf" + label="DSF Examples" + path="additions"> + <groupMarker name="concurrent"/> + </menu> + <action + class="org.eclipse.cdt.examples.dsf.filebrowser.FileBrowserAction" + id="org.eclipse.cdt.dsf.test.fileBrowser" + label="Open File Browser Dialog" + menubarPath="org.eclipse.cdt.examples.dsf/concurrent" + style="push"/> + </actionSet> + </extension> + +</plugin> diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/DsfExamplesPlugin.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/DsfExamplesPlugin.java new file mode 100644 index 00000000000..8799f748c88 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/DsfExamplesPlugin.java @@ -0,0 +1,92 @@ +/******************************************************************************* + * 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.examples.dsf; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +/** + * The activator class controls the plug-in life cycle + */ +public class DsfExamplesPlugin extends AbstractUIPlugin { + + // The plug-in ID + public static final String PLUGIN_ID = "org.eclipse.cdt.examples.dsf"; //$NON-NLS-1$ + + public static final String IMG_LAYOUT_TOGGLE = "icons/layout.gif"; //$NON-NLS-1$ + public static final String IMG_ALARM = "icons/alarm.gif"; //$NON-NLS-1$ + public static final String IMG_ALARM_TRIGGERED = "icons/alarm_triggered.gif"; //$NON-NLS-1$ + public static final String IMG_TIMER = "icons/timer.gif"; //$NON-NLS-1$ + public static final String IMG_REMOVE = "icons/remove.gif"; //$NON-NLS-1$ + + // The shared instance + private static DsfExamplesPlugin fgPlugin; + + private static BundleContext fgBundleContext; + + /** + * The constructor + */ + public DsfExamplesPlugin() { + fgPlugin = this; + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) + */ + @Override + public void start(BundleContext context) throws Exception { + fgBundleContext = context; + super.start(context); + getImageRegistry().put(IMG_ALARM, imageDescriptorFromPlugin(PLUGIN_ID, IMG_ALARM)); + getImageRegistry().put(IMG_ALARM_TRIGGERED, imageDescriptorFromPlugin(PLUGIN_ID, IMG_ALARM_TRIGGERED)); + getImageRegistry().put(IMG_TIMER, imageDescriptorFromPlugin(PLUGIN_ID, IMG_TIMER)); + getImageRegistry().put(IMG_REMOVE, imageDescriptorFromPlugin(PLUGIN_ID, IMG_REMOVE)); + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) + */ + @Override + public void stop(BundleContext context) throws Exception { + super.stop(context); + fgPlugin = null; + fgBundleContext = null; + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static DsfExamplesPlugin getDefault() { + return fgPlugin; + } + + public static BundleContext getBundleContext() { + return fgBundleContext; + } + + /** + * Returns an image descriptor for the image file at the given + * plug-in relative path + * + * @param path the path + * @return the image descriptor + */ + public static ImageDescriptor getImageDescriptor(String path) { + return imageDescriptorFromPlugin(PLUGIN_ID, path); + } + +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileBrowserAction.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileBrowserAction.java new file mode 100644 index 00000000000..2e686f5c8d1 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileBrowserAction.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * 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.examples.dsf.filebrowser; + +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.IWorkbenchWindowActionDelegate; +import org.eclipse.ui.actions.ActionDelegate; + +/** + * Action that opens the File Browser example dialog. + */ +public class FileBrowserAction extends ActionDelegate + implements IWorkbenchWindowActionDelegate +{ + private IWorkbenchWindow fWindow; + + @Override + public void run(IAction action) { + if (fWindow != null) { + // Create the dialog and open it. + Dialog dialog = new FileBrowserDialog(fWindow.getShell()); + dialog.open(); + } + } + + public void init(IWorkbenchWindow window) { + fWindow = window; + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileBrowserDialog.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileBrowserDialog.java new file mode 100644 index 00000000000..807913c0216 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileBrowserDialog.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.examples.dsf.filebrowser; + +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer; +import org.eclipse.jface.dialogs.Dialog; +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.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + +/** + * File Browser example dialog. It hold a tree viewer that displays + * file system contents and a text box for entering a file path to be + * shown in the tree. + */ +@SuppressWarnings("restriction") +public class FileBrowserDialog extends Dialog { + + /** + * Tree viewer for showing the filesystem contents. + */ + private TreeModelViewer fViewer; + + /** + * The model adapter for the tree viewer. + */ + private FileBrowserModelAdapter fModelAdapter; + + /** + * Flag used to disable text-box changed events, when the text + * box is updated due to selection change in tree. + */ + private boolean fDisableTextChangeNotifications = false; + + public FileBrowserDialog(Shell parent) { + super(parent); + setShellStyle(getShellStyle() | SWT.RESIZE); + } + + @Override + protected Control createDialogArea(Composite parent) { + Composite area = (Composite) super.createDialogArea(parent); + IPresentationContext presentationContext = new PresentationContext("org.eclipse.cdt.examples.dsf.filebrowser"); //$NON-NLS-1$ + + fViewer = new TreeModelViewer(area, SWT.VIRTUAL, presentationContext); + fViewer.getControl().setLayoutData(new GridData(GridData.FILL_BOTH)); + + fModelAdapter = new FileBrowserModelAdapter(presentationContext); + fViewer.setInput(fModelAdapter.getVMProvider().getViewerInputObject()); + + final Text text = new Text(area, SWT.SINGLE | SWT.BORDER); + text.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + + fViewer.addSelectionChangedListener(new ISelectionChangedListener() { + public void selectionChanged(SelectionChangedEvent event) { + /* + * Update the file name in the text control, to match the + * selection in the tree. Do this only if the user is not + * actively typing in the text field (test if text has focus). + */ + if (!text.isFocusControl() && + event.getSelection() instanceof IStructuredSelection && + ((IStructuredSelection)event.getSelection()).getFirstElement() instanceof FileVMContext) + { + FileVMContext fileVmc = (FileVMContext)((IStructuredSelection)event.getSelection()).getFirstElement(); + + fDisableTextChangeNotifications = true; + text.setText(fileVmc.getFile().getAbsolutePath()); + fDisableTextChangeNotifications = false; + } + } + }); + + text.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + if (!fDisableTextChangeNotifications) { + fModelAdapter.getVMProvider().selectionTextChanged(text.getText()); + } + } + }); + + return area; + } + + @Override + public boolean close() { + if (super.close()) { + fModelAdapter.dispose(); + fModelAdapter = null; + return true; + } + return false; + } + +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileBrowserModelAdapter.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileBrowserModelAdapter.java new file mode 100644 index 00000000000..497b31f45c1 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileBrowserModelAdapter.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * 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.examples.dsf.filebrowser; + +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMAdapter; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; + +/** + * This is the adapter that implements the flexible hierarchy viewer interfaces + * for providing content, labels, and event proxy-ing for the viewer. This + * adapter is registered with the DSF Session object, and is returned by the + * IDMContext.getAdapter() and IVMContext.getAdapter() methods, + * which both call {@link DsfSession#getModelAdapter(Class)}. + * <p> + * The adapter implementation for this exercise is hard-coded to provide + * contents for only one view. In turn the view contents are determined using + * the configurable ViewModelProvider. For demonstration purposes, this model + * adapter has two different layout configurations that can be used. These + * layout configurations can be set by calling the {@link #setViewLayout} method. + * <p> + * This class is primarily accessed by the flexible hierarchy viewer from a + * non-executor thread. So the class is thread-safe, except for a view methods + * which must be called on the executor thread. + * + * @see AbstractDMVMProvider + */ +@SuppressWarnings("restriction") +@ThreadSafe +public class FileBrowserModelAdapter extends AbstractVMAdapter +{ + FileBrowserVMProvider fViewModelProvider; + + @Override + protected IVMProvider createViewModelProvider(IPresentationContext context) { + /* + * In this example there is only one viewer, so there is only one + * VMProvider. + */ + return fViewModelProvider; + } + + public FileBrowserModelAdapter(IPresentationContext presentationContext) { + super(); + fViewModelProvider = new FileBrowserVMProvider(this, presentationContext); + } + + FileBrowserVMProvider getVMProvider() { + return fViewModelProvider; + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileBrowserVMProvider.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileBrowserVMProvider.java new file mode 100644 index 00000000000..ef3d098bb9c --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileBrowserVMProvider.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * 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.examples.dsf.filebrowser; + +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMAdapter; +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.IRootVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.RootVMNode; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; + +/** + * + */ +@SuppressWarnings("restriction") +public class FileBrowserVMProvider extends AbstractVMProvider +{ + /** + * The object to be set to the viewer that shows contents supplied by this provider. + * @see org.eclipse.jface.viewers.TreeViewer#setInput(Object) + */ + private final IAdaptable fViewerInputObject = + new IAdaptable() { + /** + * The input object provides the viewer access to the viewer model adapter. + */ + @SuppressWarnings("unchecked") + public Object getAdapter(Class adapter) { + if ( adapter.isInstance(getVMAdapter()) ) { + return getVMAdapter(); + } + return null; + } + + @Override + public String toString() { + return "File Browser Viewer Input"; //$NON-NLS-1$ + } + }; + + /** + * Constructor creates and configures the layout nodes to display file + * system contents. + * @param adapter The viewer model adapter that this provider is registered with. + * @param presentationContext The presentation context that this provider is + * generating contents for. + */ + public FileBrowserVMProvider(AbstractVMAdapter adapter, IPresentationContext presentationContext) { + super(adapter, presentationContext); + + IRootVMNode root = new RootVMNode(this); + IVMNode fileSystemRoots = new FilesystemRootsVMNode(this); + addChildNodes(root, new IVMNode[] { fileSystemRoots }); + IVMNode files = new FileVMNode(this); + addChildNodes(fileSystemRoots, new IVMNode[] { files }); + addChildNodes(files, new IVMNode[] { files }); + setRootNode(root); + } + + /** + * Returns the input object to be set to the viewer that shows contents + * supplied by this provider. + */ + public Object getViewerInputObject() { + return fViewerInputObject; + } + + /** + * Event handler for file selection text changes in the dialog. + * @param text New text entered in file selection text box. + */ + void selectionTextChanged(final String text) { + if (isDisposed()) return; + + // We're in the UI 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; + handleEvent(text); + }}); + } catch (RejectedExecutionException e) { + // Ignore. This exception could be thrown if the provider is being + // shut down. + } + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileVMContext.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileVMContext.java new file mode 100644 index 00000000000..93b8da64e9e --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileVMContext.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.examples.dsf.filebrowser; + +import java.io.File; + +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMContext; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMNode; + +class FileVMContext extends AbstractVMContext { + private File fFile; + FileVMContext(IVMNode layoutNode, File file) { + super(layoutNode); + fFile = file; + } + + File getFile() { return fFile; } + + @Override + public boolean equals(Object obj) { + return obj instanceof FileVMContext && ((FileVMContext)obj).getFile().equals(fFile); + } + + @Override + public int hashCode() { + return fFile.hashCode(); + } + + @Override + public String toString() { + return fFile.toString(); + } +}
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileVMNode.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileVMNode.java new file mode 100644 index 00000000000..e137dad245f --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileVMNode.java @@ -0,0 +1,314 @@ +/******************************************************************************* + * 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.examples.dsf.filebrowser; + +import java.io.File; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMContext; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.VMDelta; +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.internal.ui.viewers.model.provisional.IChildrenCountUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; + + +/** + * File view model node which returns file elements that are found in the directory + * specified by the parent element. The child nodes of this node are fixed to + * reference this element, and therefore this node will recursively populate + * the contents of the tree reflecting the underlying filesystem directories. + * <br> + * Note: this node does NOT sub-class the {@link org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMNode} + */ +@SuppressWarnings("restriction") +class FileVMNode + implements IElementLabelProvider, IVMNode +{ + /** + * Reference to the viewer model provider. It's mainly used to access the + * viewer model adapter and its executor. + */ + private final FileBrowserVMProvider fProvider; + + public FileVMNode(FileBrowserVMProvider provider) { + fProvider = provider; + } + + @Override + public String toString() { + return "FileVMNode"; + } + + + public void dispose() { + // All resources garbage collected. + } + + public void setChildNodes(IVMNode[] childNodes) { + throw new UnsupportedOperationException("This node does not support children."); //$NON-NLS-1$ + } + + /** + * List of child nodes containing only a reference to this. + */ + private final IVMNode[] fChildNodes = { this }; + + public IVMNode[] getChildNodes() { + return fChildNodes; + } + + public void update(final IHasChildrenUpdate[] updates) { + new Job("") { //$NON-NLS-1$ + { + setSystem(true); + setPriority(INTERACTIVE); + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + for (IHasChildrenUpdate update : updates) { + /* + * Do not retrieve directory contents just to mark the plus + * sign in the tree. If it's a directory, just assume that + * it has children. + */ + FileVMContext vmc = (FileVMContext)update.getElement(); + update.setHasChilren(vmc.getFile().isDirectory()); + update.done(); + } + + return Status.OK_STATUS; + } + }.schedule(); + } + + public void update(final IChildrenCountUpdate[] updates) { + new Job("") { //$NON-NLS-1$ + { + setSystem(true); + setPriority(INTERACTIVE); + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + for (IChildrenCountUpdate update : updates) { + update.setChildCount(getFiles(update).length); + update.done(); + } + return Status.OK_STATUS; + } + }.schedule(); + } + + public void update(final IChildrenUpdate[] updates) { + new Job("") { //$NON-NLS-1$ + { + setSystem(true); + setPriority(INTERACTIVE); + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + for (IChildrenUpdate update : updates) { + File[] files = getFiles(update); + int offset = update.getOffset() != -1 ? update.getOffset() : 0; + int length = update.getLength() != -1 ? update.getLength() : files.length; + for (int i = offset; (i < files.length) && (i < (offset + length)); i++) { + update.setChild(new FileVMContext(FileVMNode.this, files[i]), i); + } + update.done(); + } + return Status.OK_STATUS; + } + }.schedule(); + } + + public void update(final ILabelUpdate[] updates) { + new Job("") { //$NON-NLS-1$ + { + setSystem(true); + setPriority(INTERACTIVE); + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + for (ILabelUpdate update : updates) { + update.setLabel(getLabel((FileVMContext)update.getElement()), 0); + update.done(); + } + + return Status.OK_STATUS; + } + }.schedule(); + } + + private static final File[] EMPTY_FILE_LIST = new File[0]; + + /** + * Retrieves the list of files for this node. The list of files is based + * on the parent element in the tree, which must be of type FileVMC. + * + * @param update Update object containing the path (and the parent element) + * in the tree viewer. + * @return List of files contained in the directory specified in the + * update object. An empty list if the parent element is not a directory. + * @throws ClassCastException If the parent element contained in the update + * is NOT of type FileVMC. + */ + private File[] getFiles(IViewerUpdate update) { + FileVMContext vmc = (FileVMContext)update.getElement(); + File[] files = vmc.getFile().listFiles(); + return files != null ? files : EMPTY_FILE_LIST; + } + + /** + * Returs the text label to show in the tree for given element. + */ + private String getLabel(FileVMContext vmc) { + return vmc.getFile().getName(); + } + + public void getContextsForEvent(VMDelta parentDelta, Object event, DataRequestMonitor<IVMContext[]> rm) { + rm.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED, "", null)); //$NON-NLS-1$ + rm.done(); + } + + public int getDeltaFlags(Object e) { + /* + * @see buildDelta() + */ + int retVal = IModelDelta.NO_CHANGE; + if (e instanceof String) { + retVal |= IModelDelta.SELECT | IModelDelta.EXPAND; + } + + return retVal; + } + + public void buildDelta(final Object event, final VMDelta parentDelta, final int nodeOffset, final RequestMonitor requestMonitor) { + /* + * The FileLayoutNode is recursive, with itself as the only child. In this + * method the delta is calculated for a full path VMContext elements, and the + * implementation of this method is not recursive. + */ + if (event instanceof String) { + new Job("") { //$NON-NLS-1$ + { + setSystem(true); + setPriority(INTERACTIVE); + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + /* + * Requirements for a selection event to be issued is that the file exist, and + * that the parentDelta contain a FileVMC of a parent directory as its element. + * + * The test for first the former requirement could be performed inside getDeltaFlags() + * but getDeltaFlags() is synchronous, so it is better to perform this test here using + * a background thread (job). + * + * The latter is requirement is needed because this node does not have the algorithm + * calculate the complete list of root nodes. That algorithm is implemented inside the + * {@link FileSystemRootsLayoutNode#updateElements} method. + */ + + final File eventFile = new File((String)event); + File parentFile = null; + if (parentDelta.getElement() instanceof FileVMContext) { + parentFile = ((FileVMContext)parentDelta.getElement()).getFile(); + } + + // The file has to exist in order for us to be able to select + // it in the tree. + if (eventFile.exists() && parentFile != null) { + // Create a list containing all files in path + List<File> filePath = new LinkedList<File>(); + for (File file = eventFile; file != null && !file.equals(parentFile); file = file.getParentFile()) { + filePath.add(0, file); + } + + if (filePath.size() != 0) { + // Build the delta for all files in path. + ModelDelta delta = parentDelta; + File[] allFilesInDirectory = parentFile.listFiles(); + for (File pathSegment : filePath) { + // All files in path should be directories, and should therefore + // have a valid list of elements. + assert allFilesInDirectory != null; + + File[] pathSegmentDirectoryFiles = pathSegment.listFiles(); + delta = delta.addNode( + new FileVMContext(FileVMNode.this, pathSegment), + nodeOffset + Arrays.asList(allFilesInDirectory).indexOf(pathSegment), + IModelDelta.NO_CHANGE, + pathSegmentDirectoryFiles != null ? pathSegmentDirectoryFiles.length : 0); + allFilesInDirectory = pathSegmentDirectoryFiles; + } + + // The last file in path gets the EXPAND | SELECT flags. + delta.setFlags(delta.getFlags() | IModelDelta.SELECT | IModelDelta.EXPAND); + } + } + + // Invoke the request monitor. + + requestMonitor.done(); + + return Status.OK_STATUS; + } + }.schedule(); + } else { + requestMonitor.done(); + } + } + + /** + * Override the behavior which checks for delta flags of all the child nodes, + * because we would get stuck in a recursive loop. Instead call only the child + * nodes which are not us. + */ + protected Map<IVMNode, Integer> getChildNodesWithDeltas(Object e) { + Map<IVMNode, Integer> nodes = new HashMap<IVMNode, Integer>(); + for (final IVMNode childNode : getChildNodes()) { + int delta = childNode.getDeltaFlags(e); + if (delta != IModelDelta.NO_CHANGE) { + nodes.put(childNode, delta); + } + } + return nodes; + } + + public IVMProvider getVMProvider() { + return fProvider; + } + + +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FilesystemRootsVMNode.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FilesystemRootsVMNode.java new file mode 100644 index 00000000000..a2eb938da95 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FilesystemRootsVMNode.java @@ -0,0 +1,197 @@ +/******************************************************************************* + * 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.examples.dsf.filebrowser; + +import java.io.File; +import java.util.LinkedList; +import java.util.List; + +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.VMDelta; +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.internal.ui.viewers.model.provisional.IChildrenCountUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; + + +/** + * Viewer model node that populates the filesystem root elements. + */ +@SuppressWarnings("restriction") +class FilesystemRootsVMNode extends AbstractVMNode + implements IElementLabelProvider +{ + public FilesystemRootsVMNode(AbstractVMProvider provider) { + super(provider); + } + + @Override + public String toString() { + return "FilesystemRootsVMNode"; + } + + public void update(final IChildrenUpdate[] updates) { + new Job("") { //$NON-NLS-1$ + { + setSystem(true); + setPriority(INTERACTIVE); + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + File[] files = File.listRoots(); + for (IChildrenUpdate update : updates) { + int offset = update.getOffset() != -1 ? update.getOffset() : 0; + int length = update.getLength() != -1 ? update.getLength() : files.length; + for (int i = offset; (i < files.length) && (i < (offset + length)); i++) { + update.setChild(new FileVMContext(FilesystemRootsVMNode.this, files[i]), i); + } + update.done(); + } + return Status.OK_STATUS; + } + }.schedule(); + } + + public void update(final IHasChildrenUpdate[] updates) { + for (IHasChildrenUpdate update : updates) { + /* + * Assume that all filesystem roots have children. If user attempts + * to expand an empty directory, the plus sign will be removed + * from the element. + */ + update.setHasChilren(true); + update.done(); + } + } + + public void update(final IChildrenCountUpdate[] updates) { + new Job("") { //$NON-NLS-1$ + { + setSystem(true); + setPriority(INTERACTIVE); + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + for (IChildrenCountUpdate update : updates) { + if (!checkUpdate(update)) continue; + update.setChildCount(File.listRoots().length); + update.done(); + } + return Status.OK_STATUS; + } + }.schedule(); + } + + public void update(final ILabelUpdate[] updates) { + new Job("") { //$NON-NLS-1$ + { + setSystem(true); + setPriority(INTERACTIVE); + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + for (ILabelUpdate update : updates) { + update.setLabel(getLabel((FileVMContext)update.getElement()), 0); + update.done(); + } + + return Status.OK_STATUS; + } + }.schedule(); + } + + + /** + * Returs the text label to show in the tree for given element. Filesystem + * roots return an empty string for call to File.getName(), use the abolute path + * string instead. + */ + private String getLabel(FileVMContext vmc) { + return vmc.getFile().getAbsolutePath(); + } + + public int getDeltaFlags(Object e) { + /* + * @see buildDelta() + */ + int retVal = IModelDelta.NO_CHANGE; + if (e instanceof String) { + retVal |= IModelDelta.SELECT | IModelDelta.EXPAND; + } + + return retVal; + } + + public void buildDelta(final Object event, final VMDelta parentDelta, final int nodeOffset, final RequestMonitor requestMonitor) { + if (event instanceof String) { + new Job("") { //$NON-NLS-1$ + { + setSystem(true); + setPriority(INTERACTIVE); + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + final File eventFile = new File((String)event); + + if (eventFile.exists()) { + // Create a list containing all files in path of the file from the event + List<File> filePath = new LinkedList<File>(); + for (File file = eventFile; file != null; file = file.getParentFile()) { + filePath.add(0, file); + } + File eventRoot = filePath.get(0); + + // Get the index of the file in list of filesystem roots. + File[] roots = File.listRoots(); + + int index = 0; + for (; index < roots.length; index++) { + if (eventRoot.equals(roots[index])) break; + } + + // Check if the specified file is not one of the roots. + if (index < roots.length) { + ModelDelta delta = parentDelta.addNode( + new FileVMContext(FilesystemRootsVMNode.this, eventRoot), + index, IModelDelta.NO_CHANGE); + + if (eventFile.equals(eventRoot)) { + // The event is for the root node. Select it and extend parent node. + delta.setFlags(delta.getFlags() | IModelDelta.SELECT | IModelDelta.EXPAND); + } + } + } + + // Invoke the request monitor. + requestMonitor.done(); + + return Status.OK_STATUS; + } + }.schedule(); + } else { + requestMonitor.done(); + } + } + +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/package.html b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/package.html new file mode 100644 index 00000000000..865be837a58 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/package.html @@ -0,0 +1,127 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html> +<head> + <meta content="text/html; charset=ISO-8859-1" + http-equiv="content-type"> + <title>DSF Filesystem Browser Example</title> +</head> +<body> +<h2>DSF Filesystem Browser Example</h2> +<h3>Goals</h3> +This example demonstrates an implementation of a viewer model with a +layout node that has itself as a child. Such layout nodes are +needed to represents elements which themselves have a natural tree +structures. This example uses filesystem folders as the +tree-structured data, which is retrieved directly from the java.io.File +class. This example also demonstrates a viewer model +implementation which does not retrieve data using DSF services and +associated data model interfaces. <br> +<h3><span style="font-weight: bold;">Design</span></h3> +<span style="text-decoration: underline;">Model Adapter Hookup</span><br> +A flexible-hierarchy tree viewer {@link +org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer} +is created within a model dialog. Corresponding {@link +FileBrowserModelAdapter} and {@link FileBrowserVMProvider} classes are +instanciated, and the root element object created by +FileBrowserVMProvider is set as input to the tree viewer. From +there FileBrowserModelAdapter is returned as the {@link +IElementContentProvier} and {@link IModelProxyFactory} for all elements +in the tree.<br> +<br> +<p><span style="text-decoration: underline;">Layout Nodes</span><br> +There are three layout nodes:<br> +</p> +<ul> + <li>{@link FileBrowserVMProvider.VMRootLayoutNode} is just a root +node, which generates the input element for the viewer.</li> + <li>{@link FilesystemRootsLayoutNode} retrieves the roots of the +filesystem hierarchy ("C:\", "D:\", etc on Windows). <br> + </li> + <li>{@link FileLayoutNode} is a child of <span + style="font-family: monospace;">FilesystemRootsLayoutNode</span> and +it recursively retrieves all folders and files under the given parent +file element. This layout node does not allow any children nodes +to be added to it, and it returns only itself as a child node (through +a call to <span style="font-family: monospace;">IVMLayoutNode.getChildLayoutNodes</span>).<br> + </li> +</ul> +Both <span style="font-family: monospace;">FilesystemRootsLayoutNode</span> +and <span style="font-family: monospace;">FileLayoutNode</span> create +elements of the same type: {@link FileVMContext}. Additionally, +when populating elements in the tree, the <span + style="font-family: monospace;">FileLayoutNode</span> requires that a <span + style="font-family: monospace;">FileVMContext</span> element be the +parent element in order to be able to retrieve its children. <br> +<span style="font-family: monospace;"></span> +<p><span style="font-family: monospace;"></span></p> +<span style="text-decoration: underline;">Event Handling/Generating +Model Deltas</span><br> +The view model responds to events generated by a text box in the +dialog, where the user can type in a filesystem path. If the +entered path resolves to a file on the filesystem, the view model +generates a delta to select and reveal the given file in the +tree. The two file layout nodes handle generating the delta in +different ways:<br> +<ul> + <li><span style="font-family: monospace;">FilesystemRootsLayoutNode</span> +is a standard layout node. <br> + </li> + <ol> + <li>In the event handler implementation {@link +org.eclipse.cdt.dsf.ui.viewermodel.IVMLayoutNode#buildDelta}, the user +specified file-path is compared to the list of file-system roots. + <br> + </li> + <li>If the user file-path contains one of the filesystem roots, a +new delta node is added for that root and the child layout node is +called to continue the delta processing. <br> + </li> + <li>If the user file-path points to one of the filesystem roots, +the <span style="font-family: monospace;">IModelDelta.SELECT</span> +and <span style="font-family: monospace;">IModelDelta.EXPAND</span> +flags are also added to the delta so that the root will be selected in +the viewer.<br> + </li> + </ol> + <li><span style="font-family: monospace;">FileLayoutNode</span> is +the special case, because it is a recusrive node. This node does +not call any child nodes to process the delta, instead it calculates +the delta for all file elements in user file-path, starting at the +parent element. <br> + </li> + <ol> + <li>First the parent <span style="font-family: monospace;">FileVMContext</span> +element is retrieved from the delta. <br> + </li> + <li>Then the user file-path is broken down into {@link +java.io.File} objects representing each segment in the path, starting +at the parent file element retrieved in step 1.</li> + <li>Then a delta node is added for each segment of the calculated +path. <br> + </li> + <li><span style="font-family: monospace;">IModelDelta.SELECT</span> +and <span style="font-family: monospace;">IModelDelta.EXPAND</span> +flags are added to the last delta.<br> + </li> + </ol> +</ul> +<h3>How to use</h3> +<ol> + <li>Make sure that the DSF examples menu is visible in the perspective</li> + <ul> + <li>Go to Windows -> Customize Perspective...</li> + <li>Select Commands tab</li> + <li>Check the "DSF Examples" in the "Available command groups" +table.</li> + </ul> + <li>Open the dialog by selecting DSF Examples->Open File Browser +Dialog menu item.</li> + <li>Expand the items in the tree to see filesystem contents.</li> + <li>Select elements in the tree, to fill in text box with selected +file's path.</li> + <li>Type in a file path in text box and have the tree expand to the +specified element.<br> + </li> +</ol> +</body> +</html> diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/AlarmService.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/AlarmService.java new file mode 100644 index 00000000000..3823930faf7 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/AlarmService.java @@ -0,0 +1,252 @@ +/******************************************************************************* + * 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.examples.dsf.timers; + +import java.util.Hashtable; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.AbstractDMContext; +import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.examples.dsf.DsfExamplesPlugin; +import org.eclipse.cdt.examples.dsf.timers.TimerService.TimerDMContext; +import org.eclipse.cdt.examples.dsf.timers.TimerService.TimerTickDMEvent; +import org.osgi.framework.BundleContext; + +/** + * The alarm service tracks triggers and alarms. Triggers have a specified + * value and can be created and removed independently. Alarms are created + * for a specific timer and a trigger, and can indicate whether an alarm is + * triggered. + * <p> + * This service depends on the TimerService, so the TimerService has to be + * initialized before this service is initialized. + * </p> + */ +public class AlarmService extends AbstractDsfService +{ + /** Event indicating that the list of triggers is changed. */ + @Immutable + public static class TriggersChangedEvent {} + + /** Context representing an alarm tracked by this service. */ + @Immutable + public static class TriggerDMContext extends AbstractDMContext { + /** Alarm number, also index into alarm map */ + final int fNumber; + + private TriggerDMContext(String sessionId, int number) { + super(sessionId, new IDMContext[0]); + fNumber = number; + } + + @Override + public boolean equals(Object other) { + return baseEquals(other) && + ((TriggerDMContext)other).fNumber == fNumber; + } + + public int getTriggerNumber() { + return fNumber; + } + + @Override + public int hashCode() { + return baseHashCode() + fNumber; + } + + @Override + public String toString() { + return baseToString() + ".trigger[" + fNumber + "]"; + } + } + + /** + * Context representing the "triggered" status of an alarm with respect to + * a specific timer. + */ + @Immutable + public static class AlarmDMContext extends AbstractDMContext { + // An alarm requires both a timer and alarm context, both of which + // become parents of the alarm context. + // Note: beyond the parent contexts this context does not contain + // any other data, because no other data is needed. + private AlarmDMContext(String sessionId, + TimerDMContext timerCtx, TriggerDMContext alarmCtx) + { + super(sessionId, new IDMContext[] { timerCtx, alarmCtx }); + } + + @Override + public boolean equals(Object other) { return baseEquals(other); } + + @Override + public int hashCode() { return baseHashCode(); } + + @Override + public String toString() { + return baseToString() + ":alarm"; //$NON-NLS-1$ + } + } + + /** + * Event indicating that an alarm has been triggered by a timer. + */ + public class AlarmTriggeredDMEvent extends AbstractDMEvent<AlarmDMContext> { + public AlarmTriggeredDMEvent(AlarmDMContext context) { + super(context); + } + } + + private int fTriggerNumberCounter = 1; + private Map<TriggerDMContext, Integer> fTriggers = + new LinkedHashMap<TriggerDMContext, Integer>(); + + AlarmService(DsfSession session) { + super(session); + } + + @Override + protected BundleContext getBundleContext() { + return DsfExamplesPlugin.getDefault().getBundle().getBundleContext(); + } + + @Override + public void initialize(final RequestMonitor requestMonitor) { + super.initialize( + new RequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleSuccess() { + // After super-class is finished initializing + // perform TimerService initialization. + doInitialize(requestMonitor); + }}); + } + + private void doInitialize(RequestMonitor requestMonitor) { + // Add this class as a listener for service events, in order to receive + // TimerTickEvent events. + getSession().addServiceEventListener(this, null); + + // Register service + register(new String[]{AlarmService.class.getName()}, new Hashtable<String,String>()); + + requestMonitor.done(); + } + + @Override + public void shutdown(RequestMonitor requestMonitor) { + getSession().removeServiceEventListener(this); + unregister(); + super.shutdown(requestMonitor); + } + + public boolean isValid() { return true; } + + @DsfServiceEventHandler + public void eventDispatched(TimerTickDMEvent event) { + final TimerDMContext timerContext = event.getDMContext(); + + int timerValue = getServicesTracker().getService(TimerService.class). + getTimerValue(event.getDMContext()); + + // If a timer triggers an alarm, this service needs to issue an alarm + // triggered event. + checkAlarmsForTimer(timerContext, timerValue); + } + + private void checkAlarmsForTimer(TimerDMContext timerContext, int timerValue) { + // Check the existing alarms for whether they are triggered by given + // timer. + for (Map.Entry<TriggerDMContext, Integer> entry : fTriggers.entrySet()) { + if (timerValue == entry.getValue()) { + // Generate the AlarmTriggeredEvent + AlarmDMContext alarmCtx = new AlarmDMContext( + getSession().getId(), timerContext, entry.getKey()); + getSession().dispatchEvent( + new AlarmTriggeredDMEvent(alarmCtx), getProperties()); + } + } + } + + + /** Returns the list of triggers. */ + public TriggerDMContext[] getTriggers() { + return fTriggers.keySet().toArray(new TriggerDMContext[fTriggers.size()]); + } + + /** Returns the trigger value. */ + public int getTriggerValue(TriggerDMContext alarmCtx) { + Integer value = fTriggers.get(alarmCtx); + if (value != null) { + return value; + } else { + return -1; + } + } + + /** Returns the alarm context for given timer and trigger contexts. */ + public AlarmDMContext getAlarm(TriggerDMContext alarmCtx, TimerDMContext timerCtx) { + return new AlarmDMContext(getSession().getId(), timerCtx, alarmCtx); + } + + /** Returns true if the given alarm is triggered */ + public boolean isAlarmTriggered(AlarmDMContext alarmCtx) { + // Extract the timer and trigger contexts. They should always be part + // of the alarm. + TimerService.TimerDMContext timerCtx = DMContexts.getAncestorOfType( + alarmCtx, TimerService.TimerDMContext.class); + TriggerDMContext triggerCtx = DMContexts.getAncestorOfType( + alarmCtx, TriggerDMContext.class); + + assert triggerCtx != null && timerCtx != null; + + // Find the trigger and check whether the timers value has surpassed it. + if (fTriggers.containsKey(triggerCtx)) { + int timerValue = getServicesTracker().getService(TimerService.class). + getTimerValue(timerCtx); + + return timerValue >= fTriggers.get(triggerCtx); + } + + return false; + } + + /** Creates a new alarm object with given value. */ + public TriggerDMContext createTrigger(int value) { + TriggerDMContext triggerCtx = + new TriggerDMContext(getSession().getId(), fTriggerNumberCounter++); + fTriggers.put(triggerCtx, value); + getSession().dispatchEvent(new TriggersChangedEvent(), getProperties()); + return triggerCtx; + } + + /** Removes given alarm from service. */ + public void deleteTrigger(TriggerDMContext alarmCtx) { + fTriggers.remove(alarmCtx); + getSession().dispatchEvent(new TriggersChangedEvent(), getProperties()); + } + + /** Changes the value of the given trigger. */ + public void setTriggerValue(TriggerDMContext ctx, int newValue) { + if (fTriggers.containsKey(ctx)) { + fTriggers.put(ctx, newValue); + } + getSession().dispatchEvent(new TriggersChangedEvent(), getProperties()); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/AlarmsVMNode.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/AlarmsVMNode.java new file mode 100644 index 00000000000..be7b22f3b07 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/AlarmsVMNode.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * 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.examples.dsf.timers; + +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.service.DsfSession; +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.examples.dsf.DsfExamplesPlugin; +import org.eclipse.cdt.examples.dsf.timers.AlarmService.AlarmDMContext; +import org.eclipse.cdt.examples.dsf.timers.AlarmService.TriggerDMContext; +import org.eclipse.cdt.examples.dsf.timers.TimerService.TimerDMContext; +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; + +/** + * View model node that determines whether an "alarm triggered" indicator is + * shown in the tree. This indicator is only shown if a given alarm is + * triggered for a given timer. + * + * @see AlarmDMContext + */ +@SuppressWarnings("restriction") +class AlarmsVMNode extends AbstractDMVMNode + implements IElementLabelProvider +{ + public AlarmsVMNode(AbstractDMVMProvider provider, DsfSession session) { + super(provider, session, AlarmDMContext.class); + } + + @Override + public String toString() { + return "AlarmsVMNode(" + getSession().getId() + ")"; + } + + + @Override + protected void updateElementsInSessionThread(final IChildrenUpdate update) { + // Check that the service is available and find the trigger and timer contexts. + // If not found, fail. + AlarmService alarmService = getServicesTracker().getService(AlarmService.class, null); + TriggerDMContext alarmDmc = findDmcInPath( + update.getViewerInput(), update.getElementPath(), TriggerDMContext.class); + TimerDMContext timerDmc = findDmcInPath( + update.getViewerInput(), update.getElementPath(), TimerDMContext.class); + if (alarmService == null || alarmDmc == null || timerDmc == null) { + update.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Required elements not found in path")); + update.done(); + return; + } + + // Get the alarm context then check the triggered value. + final AlarmDMContext alarmStatusDmc = alarmService.getAlarm(alarmDmc, timerDmc); + boolean triggered = alarmService.isAlarmTriggered(alarmStatusDmc); + + // Only return the alarm in list of elements if it is triggered. + if (triggered) { + update.setChild(createVMContext(alarmStatusDmc), 0); + } + update.done(); + } + + public void update(ILabelUpdate[] updates) { + for (ILabelUpdate update : updates) { + update.setLabel("ALARM TRIGGERED", 0); + update.setImageDescriptor( + DsfExamplesPlugin.getDefault().getImageRegistry().getDescriptor( + DsfExamplesPlugin.IMG_ALARM_TRIGGERED), + 0); + update.done(); + } + } + + + public int getDeltaFlags(Object e) { + if (e instanceof AlarmService.AlarmTriggeredDMEvent) { + return IModelDelta.ADDED | IModelDelta.SELECT | IModelDelta.EXPAND; + } + return IModelDelta.NO_CHANGE; + } + + public void buildDelta(Object e, VMDelta parentDelta, int nodeOffset, RequestMonitor requestMonitor) { + // The alarm element is added when and selected upon a triggered event. + // Parent element is also expanded allow the alarm to be selected. + if (e instanceof AlarmService.AlarmTriggeredDMEvent) { + parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.EXPAND); + parentDelta.addNode( + createVMContext( ((AlarmService.AlarmTriggeredDMEvent)e).getDMContext() ), + 0, + IModelDelta.ADDED | IModelDelta.SELECT); + } + requestMonitor.done(); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/ServicesShutdownSequence.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/ServicesShutdownSequence.java new file mode 100644 index 00000000000..cb8276c9a13 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/ServicesShutdownSequence.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * 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.examples.dsf.timers; + +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Sequence; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.service.IDsfService; +import org.eclipse.cdt.examples.dsf.DsfExamplesPlugin; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +/** + * Sequence that stops the services in the timers session. + */ +public class ServicesShutdownSequence extends Sequence { + + // Session to that the services are running in. + final private DsfSession fSession; + + // DSF Services is created as the first step of the sequence. It + // cannot be created by the constructor because it can only be called + // in the session thread. + DsfServicesTracker fTracker; + + public ServicesShutdownSequence(DsfSession session) { + super(session.getExecutor()); + fSession = session; + } + + Step[] fSteps = new Step[] { + new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + fTracker = new DsfServicesTracker(DsfExamplesPlugin.getBundleContext(), fSession.getId()); + requestMonitor.done(); + } + + @Override + public void rollBack(RequestMonitor requestMonitor) { + // Dispose the tracker in case shutdown sequence is aborted + // and is rolled back. + fTracker.dispose(); + fTracker = null; + requestMonitor.done(); + } + }, + new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + shutdownService(AlarmService.class, requestMonitor); + } + }, + new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + shutdownService(TimerService.class, requestMonitor); + } + }, + new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + // Dispose the tracker after the services are shut down. + fTracker.dispose(); + fTracker = null; + requestMonitor.done(); + } + } + }; + + @Override + public Step[] getSteps() { return fSteps; } + + // A convenience method that shuts down given service. Only service class + // is used to identify the service. + private <V extends IDsfService> void shutdownService(Class<V> clazz, RequestMonitor requestMonitor) { + IDsfService service = fTracker.getService(clazz); + if (service != null) { + service.shutdown(requestMonitor); + } + else { + requestMonitor.setStatus(new Status( + IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, + IDsfStatusConstants.INTERNAL_ERROR, + "Service '" + clazz.getName() + "' not found.", null)); + requestMonitor.done(); + } + } + +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/ServicesStartupSequence.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/ServicesStartupSequence.java new file mode 100644 index 00000000000..c137bc36369 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/ServicesStartupSequence.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * 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.examples.dsf.timers; + +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Sequence; +import org.eclipse.cdt.dsf.service.DsfSession; + +/** + * Startup sequence for the timers session. With only two services, this is + * a very simple sequence. + */ +public class ServicesStartupSequence extends Sequence { + + final private DsfSession fSession; + + // The reference to the services are saved to use in the last step. + private TimerService fTimerService = null; + private AlarmService fAlarmService = null; + + + public ServicesStartupSequence(DsfSession session) { + super(session.getExecutor()); + fSession = session; + } + + Step[] fSteps = new Step[] { + new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + fTimerService = new TimerService(fSession); + fTimerService.initialize(requestMonitor); + }}, + new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + fAlarmService = new AlarmService(fSession); + fAlarmService.initialize(requestMonitor); + }}, + new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + // Create the first timer and trigger. + fTimerService.startTimer(); + fAlarmService.createTrigger(5); + requestMonitor.done(); + }} + }; + + @Override + public Step[] getSteps() { return fSteps; } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimerService.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimerService.java new file mode 100644 index 00000000000..6b05f0a1c5e --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimerService.java @@ -0,0 +1,184 @@ +/******************************************************************************* + * 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.examples.dsf.timers; + +import java.util.HashMap; +import java.util.Hashtable; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.AbstractDMContext; +import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.examples.dsf.DsfExamplesPlugin; +import org.osgi.framework.BundleContext; + +/** + * Timer service tracks a set of timers, which are created per user request. + * The timers are represented using a Data Model context object, which + * implements {@link IDMContext}. Each timers value, which can be retrieved + * by calling {@link #getTimerValue(TimerDMContext)}, is incremented every + * second. When a timer value is incremented the TimerService issues a + * {@link TimerTickDMEvent}. + */ +public class TimerService extends AbstractDsfService +{ + /** Event indicating that the list of timers is changed. */ + @Immutable + public static class TimersChangedEvent {} + + /** Data Model context representing a timer. */ + @Immutable + public static class TimerDMContext extends AbstractDMContext { + final int fNumber; + + public TimerDMContext(String sessionId, int timer) { + super(sessionId, new IDMContext[0]); + fNumber = timer; + } + + /** Returns the sequential creation number of this timer. */ + public int getTimerNumber() { + return fNumber; + } + + // Timer context objects are created as needed and not cached, so the + // equals method implementation is critical. + @Override + public boolean equals(Object other) { + return baseEquals(other) && + ((TimerDMContext)other).fNumber == fNumber; + } + + @Override + public int hashCode() { return baseHashCode() + fNumber; } + + @Override + public String toString() { + return baseToString() + ".timer[" + fNumber + "]"; + } + } + + /** + * Event indicating that a timer's value has incremented. The context in + * the event points to the timer that has changed. + */ + public class TimerTickDMEvent extends AbstractDMEvent<TimerDMContext> { + public TimerTickDMEvent(TimerDMContext context) { + super(context); + } + } + + /** Counter for generating timer numbers */ + private int fTimerNumberCounter = 1; + + // Use a linked hash in order to be able to return an ordered list of timers. + private Map<TimerDMContext, Integer> fTimers = + new LinkedHashMap<TimerDMContext, Integer>(); + + private Map<TimerDMContext, Future<?>> fTimerFutures = + new HashMap<TimerDMContext, Future<?>>(); + + + TimerService(DsfSession session) { + super(session); + } + + @Override + protected BundleContext getBundleContext() { + return DsfExamplesPlugin.getDefault().getBundle().getBundleContext(); + } + + @Override + public void initialize(final RequestMonitor requestMonitor) { + super.initialize( + new RequestMonitor(getExecutor(), requestMonitor) { + @Override + public void handleSuccess() { + // After super-class is finished initializing + // perform TimerService initialization. + doInitialize(requestMonitor); + }}); + } + + private void doInitialize(RequestMonitor requestMonitor) { + // Register service + register( new String[]{ TimerService.class.getName() }, + new Hashtable<String,String>() ); + requestMonitor.done(); + } + + @Override + public void shutdown(RequestMonitor requestMonitor) { + // Cancel timer futures to avoid firing more events. + for (Future<?> future : fTimerFutures.values()) { + future.cancel(false); + } + unregister(); + super.shutdown(requestMonitor); + } + + /** Retrieves the list of timer contexts. */ + public TimerDMContext[] getTimers() { + return fTimers.keySet().toArray(new TimerDMContext[fTimers.size()]); + } + + /** Retrieves the timer value for the given context. */ + public int getTimerValue(TimerDMContext context) { + Integer value = fTimers.get(context); + if (value != null) { + return value; + } + return -1; + } + + /** Creates a new timer and returns its context. */ + public TimerDMContext startTimer() { + // Create a new timer context and add it to the internal list. + final TimerDMContext newTimer = + new TimerDMContext(getSession().getId(), fTimerNumberCounter++); + fTimers.put(newTimer, 0); + + // Create a new runnable that will execute every second and increment + // the timer value. The returned future is the handle that allows + // for canceling the scheduling of the runnable. + Future<?> timerFuture = getExecutor().scheduleAtFixedRate( + new Runnable() { + public void run() { + fTimers.put(newTimer, fTimers.get(newTimer) + 1); + getSession().dispatchEvent(new TimerTickDMEvent(newTimer), getProperties()); + } + @Override + public String toString() { return "Scheduled timer runnable for timer " + newTimer; } //$NON-NLS-1$ + }, + 1, 1, TimeUnit.SECONDS); + fTimerFutures.put(newTimer, timerFuture); + + // Issue an event to allow clients to update the list of timers. + getSession().dispatchEvent(new TimersChangedEvent(), getProperties()); + return newTimer; + } + + /** Removes given timer from list of timers. */ + public void killTimer(TimerDMContext timerContext) { + if (fTimers.containsKey(timerContext)) { + fTimers.remove(timerContext); + fTimerFutures.remove(timerContext).cancel(false); + } + getSession().dispatchEvent(new TimersChangedEvent(), getProperties()); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersRootVMNode.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersRootVMNode.java new file mode 100644 index 00000000000..c804f6d2476 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersRootVMNode.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * 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.examples.dsf.timers; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.VMDelta; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.RootDMVMNode; +import org.eclipse.cdt.examples.dsf.timers.TimersVMProvider.TimersViewLayoutChanged; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; + +/** + * + */ +@SuppressWarnings("restriction") +public class TimersRootVMNode extends RootDMVMNode { + + public TimersRootVMNode(AbstractVMProvider provider) { + super(provider); + } + + @Override + public boolean isDeltaEvent(Object rootObject, Object e) { + if (e instanceof TimersViewLayoutChanged) { + return true; + } + return super.isDeltaEvent(rootObject, e); + } + + @Override + public int getDeltaFlags(Object e) { + if (e instanceof TimersViewLayoutChanged) { + return IModelDelta.CONTENT; + } + + return IModelDelta.NO_CHANGE; + } + + @Override + public void createRootDelta(Object rootObject, Object event, final DataRequestMonitor<VMDelta> rm) { + rm.setData(new VMDelta(rootObject, 0, IModelDelta.NO_CHANGE)); + int flags = IModelDelta.NO_CHANGE; + if (event instanceof TimersViewLayoutChanged) { + flags |= IModelDelta.CONTENT; + } + rm.setData( new VMDelta(rootObject, 0, flags) ); + rm.done(); + } + +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersVMAdapter.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersVMAdapter.java new file mode 100644 index 00000000000..5fae6c32c75 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersVMAdapter.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.examples.dsf.timers; + +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +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; + +/** + * This is the adapter that implements the flexible hierarchy viewer interfaces + * for providing content, labels, and event processing for the viewer. This + * adapter is registered with the DSF Session object, and is returned by the + * IDMContext.getAdapter() and IVMContext.getAdapter() methods, + * which both call {@link DsfSession#getModelAdapter(Class)}. + */ +@SuppressWarnings("restriction") +@ThreadSafe +public class TimersVMAdapter extends AbstractDMVMAdapter +{ + @Override + protected IVMProvider createViewModelProvider(IPresentationContext context) { + if ( TimersView.ID_VIEW_TIMERS.equals(context.getId()) ) { + return new TimersVMProvider(this, context, getSession()); + } + return null; + } + + public TimersVMAdapter(DsfSession session, IPresentationContext presentationContext) { + super(session); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersVMNode.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersVMNode.java new file mode 100644 index 00000000000..bba6d1f1ed3 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersVMNode.java @@ -0,0 +1,169 @@ +/******************************************************************************* + * 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.examples.dsf.timers; + +import java.text.MessageFormat; +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.RequestMonitor; +import org.eclipse.cdt.dsf.service.DsfSession; +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.properties.IElementPropertiesProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.IPropertiesUpdate; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelAttribute; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelColumnInfo; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelImage; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelText; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.PropertyBasedLabelProvider; +import org.eclipse.cdt.examples.dsf.DsfExamplesPlugin; +import org.eclipse.cdt.examples.dsf.timers.TimerService.TimerDMContext; +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; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; + + +/** + * View model node that defines how timer DMContexts are displayed in the view. Timers + * change with every tick of the timer, so the label has to be repained + * upon timer tick events. + * @see TimerDMContext + */ +@SuppressWarnings("restriction") +class TimersVMNode extends AbstractDMVMNode + implements IElementLabelProvider, IElementPropertiesProvider +{ + private static final String PROP_TIMER_NUMBER = "alarmNumber"; + private static final String PROP_TIMER_VALUE = "alarmTriggerValue"; + + // Create and configure the label provider. + private static final PropertyBasedLabelProvider fgLabelProvider; + static { + fgLabelProvider = new PropertyBasedLabelProvider(); + + LabelColumnInfo idCol = new LabelColumnInfo( + new LabelAttribute[] { + new LabelText(new MessageFormat("Timer #{0}"), + new String[] { PROP_TIMER_NUMBER }), + new LabelImage(DsfExamplesPlugin.getDefault().getImageRegistry(). + getDescriptor(DsfExamplesPlugin.IMG_ALARM)) + }); + fgLabelProvider.setColumnInfo(TimersViewColumnPresentation.COL_ID, idCol); + + LabelColumnInfo valueCol = new LabelColumnInfo( + new LabelAttribute[] { + new LabelText(new MessageFormat("{0}"), + new String[] { PROP_TIMER_VALUE }) + }); + fgLabelProvider.setColumnInfo(TimersViewColumnPresentation.COL_VALUE, + valueCol); + + } + + + public TimersVMNode(AbstractDMVMProvider provider, DsfSession session) { + super(provider, session, TimerDMContext.class); + } + + @Override + public String toString() { + return "TimersVMNode(" + getSession().getId() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + } + + public void update(ILabelUpdate[] updates) { + fgLabelProvider.update(updates); + } + + @Override + protected void updateElementsInSessionThread(final IChildrenUpdate update) { + TimerService timerService = getServicesTracker().getService(TimerService.class, null); + if ( timerService == null ) { + handleFailedUpdate(update); + return; + } + + // Retrieve the timer DMContexts, create the corresponding VMCs array, and + // set them as result. + TimerDMContext[] timers = timerService.getTimers(); + fillUpdateWithVMCs(update, timers); + update.done(); + } + + + public void update(final IPropertiesUpdate[] updates) { + // Switch to the session thread before processing the updates. + try { + getSession().getExecutor().execute(new DsfRunnable() { + public void run() { + for (IPropertiesUpdate update : updates) { + updatePropertiesInSessionThread(update); + } + }}); + } catch (RejectedExecutionException e) { + for (IViewerUpdate update : updates) { + handleFailedUpdate(update); + } + } + } + + @ConfinedToDsfExecutor("getSession#getExecutor") + private void updatePropertiesInSessionThread(final IPropertiesUpdate update) { + // Find the timer context in the element being updated + TimerDMContext dmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), TimerDMContext.class); + TimerService timerService = getServicesTracker().getService(TimerService.class, null); + + // If either update or service are not valid, fail the update and exit. + if ( dmc == null || timerService == null) { + handleFailedUpdate(update); + return; + } + + int value = timerService.getTimerValue(dmc); + + if (value == -1) { + handleFailedUpdate(update); + return; + } + + update.setProperty(PROP_TIMER_NUMBER, dmc.getTimerNumber()); + update.setProperty(PROP_TIMER_VALUE, value); + update.done(); + } + + public int getDeltaFlags(Object e) { + // This node generates delta if the timers have changed, or if the + // label has changed. + if (e instanceof TimerService.TimerTickDMEvent) { + return IModelDelta.STATE; + } else if (e instanceof TimerService.TimersChangedEvent) { + return IModelDelta.CONTENT; + } + return IModelDelta.NO_CHANGE; + } + + public void buildDelta(Object e, VMDelta parentDelta, int nodeOffset, RequestMonitor requestMonitor) { + if (e instanceof TimerService.TimerTickDMEvent) { + // Add delta indicating that the given timer has changed. + parentDelta.addNode( createVMContext(((TimerService.TimerTickDMEvent)e).getDMContext()), IModelDelta.STATE ); + } else if (e instanceof TimerService.TimersChangedEvent) { + // The list of timers has changed, which means that the parent + // node needs to refresh its contents, which in turn will re-fetch the + // elements from this node. + parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); + } + requestMonitor.done(); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersVMProvider.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersVMProvider.java new file mode 100644 index 00000000000..e44c122d7db --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersVMProvider.java @@ -0,0 +1,121 @@ +/******************************************************************************* + * 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.examples.dsf.timers; + +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +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.examples.dsf.timers.AlarmService.TriggersChangedEvent; +import org.eclipse.cdt.examples.dsf.timers.TimerService.TimersChangedEvent; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; + +/** + * The View Model provider for the Timers view. This provider allows for + * switching between two different view layouts: + * <ol> + * <li>Timers -> Triggers -> Alarms</li> + * <li>Triggers -> Timers -> Alarms</li> + * </ol> + * A special event is sent when the layout is changed in order to generate + * a proper delta to refresh the view. + */ +@SuppressWarnings("restriction") +public class TimersVMProvider extends AbstractDMVMProvider { + + /** Event indicating that the timers view layout has changed */ + public static class TimersViewLayoutChanged {} + + /** Enumeration of possible layouts for the timers view model */ + public enum ViewLayout { TRIGGERS_AT_TOP, TIMERS_AT_TOP } + + public TimersVMProvider(AbstractVMAdapter adapter, IPresentationContext presentationContext, DsfSession session) { + super(adapter, presentationContext, session); + // Set the initial view layout. + setViewLayout(ViewLayout.TIMERS_AT_TOP); + } + + /** + * Configures a new layout for the timers view model. + * @param layout New layout to use. + */ + public void setViewLayout(ViewLayout layout) { + clearNodes(); + if (layout == ViewLayout.TRIGGERS_AT_TOP) { + IRootVMNode root = new TimersRootVMNode(this); + IVMNode triggersNode = new TriggersVMNode(this, getSession()); + addChildNodes(root, new IVMNode[] { triggersNode }); + IVMNode timersNode = new TimersVMNode(this, getSession()); + addChildNodes(triggersNode, new IVMNode[] { timersNode }); + IVMNode alarmNode = new AlarmsVMNode(this, getSession()); + addChildNodes(timersNode, new IVMNode[] { alarmNode }); + setRootNode(root); + } else if (layout == ViewLayout.TIMERS_AT_TOP) { + IRootVMNode root = new TimersRootVMNode(this); + IVMNode timersNode = new TimersVMNode(this, getSession()); + addChildNodes(root, new IVMNode[] { timersNode }); + IVMNode triggersNode = new TriggersVMNode(this, getSession()); + addChildNodes(timersNode, new IVMNode[] { triggersNode }); + IVMNode alarmNode = new AlarmsVMNode(this, getSession()); + addChildNodes(triggersNode, new IVMNode[] { alarmNode }); + setRootNode(root); + } + + handleEvent(new TimersViewLayoutChanged()); + } + + @Override + public IColumnPresentation createColumnPresentation(IPresentationContext context, Object element) { + return new TimersViewColumnPresentation(); + } + + @Override + public String getColumnPresentationId(IPresentationContext context, Object element) { + return TimersViewColumnPresentation.ID; + } + + // Add a handler for the triggers and timers changed events. The + // AbstractDMVMProvider superclass automatically registers this provider + // for all IDMEvent events, however these two events do not implement + // IDMEvent + @DsfServiceEventHandler + public void eventDispatched(final TriggersChangedEvent event) { + if (isDisposed()) return; + + try { + getExecutor().execute(new Runnable() { + public void run() { + if (isDisposed()) return; + handleEvent(event); + } + }); + } catch (RejectedExecutionException e) {} + } + + @DsfServiceEventHandler + public void eventDispatched(final TimersChangedEvent event) { + if (isDisposed()) return; + + try { + getExecutor().execute(new Runnable() { + public void run() { + if (isDisposed()) return; + handleEvent(event); + } + }); + } catch (RejectedExecutionException e) {} + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersView.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersView.java new file mode 100644 index 00000000000..d9c4a59dac8 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersView.java @@ -0,0 +1,325 @@ +/******************************************************************************* + * 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.examples.dsf.timers; + +import java.util.concurrent.ExecutionException; + +import org.eclipse.cdt.dsf.concurrent.DefaultDsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; +import org.eclipse.cdt.examples.dsf.DsfExamplesPlugin; +import org.eclipse.cdt.examples.dsf.timers.TimerService.TimerDMContext; +import org.eclipse.cdt.examples.dsf.timers.TimersVMProvider.ViewLayout; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentationFactory; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxyFactory; +import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.action.IToolBarManager; +import org.eclipse.jface.action.Separator; +import org.eclipse.jface.dialogs.IInputValidator; +import org.eclipse.jface.dialogs.InputDialog; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.window.Window; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.ui.IActionBars; +import org.eclipse.ui.part.ViewPart; + + +/** + * Example view which displays data from timers and alarms services. It starts + * a new DSF session and configures the services for it. Then it configures + * a data model provider to process the service data and display it in a + * flexible-hierarchy asynchronous viewer. + */ +@SuppressWarnings("restriction") +public class TimersView extends ViewPart { + + /** Timers view ID */ + public static final String ID_VIEW_TIMERS = "org.eclipse.cdt.examples.dsf.TimersView"; + + /** Asynchronous tree viewer from the platform debug.ui plugin. */ + private TreeModelViewer fViewer; + + /** Presentation context of the timers viewer */ + private PresentationContext fPresentationContext; + + /** DSF executor to use for a new session with timers and alarms services */ + private DsfExecutor fExecutor; + + /** DSF session */ + private DsfSession fSession; + + /** DSF services tracker used by actions in the viewer. */ + private DsfServicesTracker fServices; + + /** Adapter used to provide view model for flexible-hierarchy viewer */ + private TimersVMAdapter fTimersVMAdapter; + + /** Action which toggles the layout in the viewer */ + private Action fToggleLayoutAction; + + /** Action that adds a new timer */ + private Action fAddTimerAction; + + /** Action that adds a new trigger */ + private Action fAddTriggerAction; + + /** Action that removes the selected trigger or timer */ + private Action fRemoveAction; + + public TimersView() {} + + /** + * This is a call-back that will allow us to create the viewer and + * initialize it. For this view, it creates the DSF session, along + * with its services. Then it creates the viewer model adapter and + * registers it with the session. + */ + @Override + public void createPartControl(Composite parent) { + // Create the Flexible Hierarchy viewer. Also create a presentation + // context which will be given to the content/label provider adapters + // to distinguish this view from other flexible-hierarchy views. + fPresentationContext = new PresentationContext(ID_VIEW_TIMERS); + fViewer = new TreeModelViewer( + parent, SWT.VIRTUAL | SWT.FULL_SELECTION, fPresentationContext); + + // Create the executor, which will be used exclusively with this view, + // as well as a session and a services tracker for managing references + // to services. + fExecutor = new DefaultDsfExecutor(); + fSession = DsfSession.startSession(fExecutor, "Timers(DSF Example)"); + fServices = new DsfServicesTracker( + DsfExamplesPlugin.getBundleContext(), fSession.getId()); + + // Start the services using a sequence. The sequence runs in the + // session executor thread, therefore the thread calling this method + // has to block using Future.get() until the sequence it completes. + // The Future.get() will throw an exception if the sequence fails. + ServicesStartupSequence startupSeq = new ServicesStartupSequence(fSession); + fSession.getExecutor().execute(startupSeq); + try { + startupSeq.get(); + } catch (InterruptedException e) { assert false; + } catch (ExecutionException e) { assert false; + } + + // Create the flexible hierarchy content/label adapter. Then register + // it with the session. + fTimersVMAdapter = new TimersVMAdapter(fSession, fPresentationContext); + fSession.registerModelAdapter(IElementContentProvider.class, fTimersVMAdapter); + fSession.registerModelAdapter(IModelProxyFactory.class, fTimersVMAdapter); + fSession.registerModelAdapter(IColumnPresentationFactory.class, fTimersVMAdapter); + + // Create the input object for the view. This object needs to return + // the VM adapter through the IAdaptable interface when queried for the + // flexible hierarchy adapters. + final IAdaptable viewerInputObject = + new IAdaptable() { + /** + * The input object provides the viewer access to the viewer model adapter. + */ + @SuppressWarnings("unchecked") + public Object getAdapter(Class adapter) { + if ( adapter.isInstance(fTimersVMAdapter) ) { + return fTimersVMAdapter; + } + return null; + } + + @Override + public String toString() { + return "Timers View Root"; //$NON-NLS-1$ + } + }; + fViewer.setInput(viewerInputObject); + + makeActions(); + contributeToActionBars(); + } + + @Override + public void dispose() { + try { + // First dispose the view model, which is the client of services. + // This operation needs to be performed in the session executor + // thread. Block using Future.get() until this call completes. + fSession.getExecutor().submit(new Runnable() { + public void run() { + fSession.unregisterModelAdapter(IElementContentProvider.class); + fSession.unregisterModelAdapter(IModelProxyFactory.class); + fSession.unregisterModelAdapter(IColumnPresentationFactory.class); + }}).get(); + + // Dispose the VM adapter. + fTimersVMAdapter.dispose(); + fTimersVMAdapter = null; + + // Next invoke the shutdown sequence for the services. Sequence + // class also implements Future.get()... + ServicesShutdownSequence shutdownSeq = + new ServicesShutdownSequence(fSession); + fSession.getExecutor().execute(shutdownSeq); + try { + shutdownSeq.get(); + } catch (InterruptedException e) { assert false; + } catch (ExecutionException e) { assert false; + } + + // Finally end the session and the executor. + fSession.getExecutor().submit(new Runnable() { + public void run() { + DsfSession.endSession(fSession); + fSession = null; + fExecutor.shutdown(); + fExecutor = null; + }}).get(); + } catch (InterruptedException e) { + } catch (ExecutionException e) { + } + super.dispose(); + } + + private void contributeToActionBars() { + IActionBars bars = getViewSite().getActionBars(); + fillLocalToolBar(bars.getToolBarManager()); + } + + private void fillLocalToolBar(IToolBarManager manager) { + manager.add(fToggleLayoutAction); + manager.add(fAddTimerAction); + manager.add(fAddTriggerAction); + manager.add(fRemoveAction); + manager.add(new Separator()); + } + + private void makeActions() { + fToggleLayoutAction = new Action("Toggle Layout", IAction.AS_CHECK_BOX) { //$NON-NLS-1$ + @Override + public void run() { + // Get the toggle state of the action while on UI thread. + final ViewLayout layout = isChecked() ? ViewLayout.TRIGGERS_AT_TOP : ViewLayout.TIMERS_AT_TOP; + + IVMProvider provider = fTimersVMAdapter.getVMProvider(fPresentationContext); + ((TimersVMProvider)provider).setViewLayout(layout); + } + }; + fToggleLayoutAction.setToolTipText("Toggle Layout"); //$NON-NLS-1$ + fToggleLayoutAction.setImageDescriptor(DsfExamplesPlugin.getDefault().getImageRegistry().getDescriptor( + DsfExamplesPlugin.IMG_LAYOUT_TOGGLE)); + + fAddTimerAction = new Action("Add New Timer") { + @Override + public void run() { + fExecutor.execute(new Runnable() { + public void run() { + // Only need to create the new timer, the events will + // cause the view to refresh. + fServices.getService(TimerService.class).startTimer(); + } + }); + } + }; + fAddTimerAction.setToolTipText("Add a new timer"); + fAddTimerAction.setImageDescriptor( + getImage(DsfExamplesPlugin.IMG_TIMER)); + + fAddTriggerAction = new Action("Add New Trigger") { + @Override + public void run() { + // Ask user for the new trigger value. + InputDialog inputDialog = new InputDialog( + getSite().getShell(), + "New Trigger", + "Please enter trigger value", + "", + new IInputValidator() { + public String isValid(String input) { + try { + int i= Integer.parseInt(input); + if (i <= 0) + return "Please enter a positive integer"; + + } catch (NumberFormatException x) { + return "Please enter a positive integer"; + } + return null; + } + } + ); + if (inputDialog.open() != Window.OK) return; + int tmpTriggerValue = -1; + try { + tmpTriggerValue = Integer.parseInt(inputDialog.getValue()); + } catch (NumberFormatException x) { assert false; } + final int triggerValue = tmpTriggerValue; + fExecutor.execute(new Runnable() { + public void run() { + // Create the new trigger + fServices.getService(AlarmService.class). + createTrigger(triggerValue); + } + }); + } + }; + fAddTriggerAction.setToolTipText("Add a new trigger"); + fAddTriggerAction.setImageDescriptor( + getImage(DsfExamplesPlugin.IMG_ALARM)); + + fRemoveAction = new Action("Remove") { + @Override + public void run() { + final Object selectedElement = + ((IStructuredSelection)fViewer.getSelection()).getFirstElement(); + if (!(selectedElement instanceof IDMVMContext)) return; + final IDMContext selectedCtx = + ((IDMVMContext)selectedElement).getDMContext(); + // Based on the context from the selection, call the + // appropriate service to remove the item. + if (selectedCtx instanceof TimerDMContext) { + fExecutor.execute(new Runnable() { public void run() { + fServices.getService(TimerService.class).killTimer( + ((TimerDMContext)selectedCtx)); + }}); + } else if (selectedCtx instanceof AlarmService.TriggerDMContext) { + fExecutor.execute(new Runnable() { public void run() { + fServices.getService(AlarmService.class).deleteTrigger( + (AlarmService.TriggerDMContext)selectedCtx); + }}); + } + } + }; + fRemoveAction.setToolTipText("Remove selected item"); + fRemoveAction.setImageDescriptor( getImage(DsfExamplesPlugin.IMG_REMOVE) ); + } + + private ImageDescriptor getImage(String key) { + return DsfExamplesPlugin.getDefault().getImageRegistry().getDescriptor(key); + } + + /** + * Passing the focus request to the viewer's control. + */ + @Override + public void setFocus() { + fViewer.getControl().setFocus(); + } +}
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersViewColumnPresentation.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersViewColumnPresentation.java new file mode 100644 index 00000000000..a43778a525a --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersViewColumnPresentation.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * 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.examples.dsf.timers; + +import org.eclipse.cdt.examples.dsf.DsfExamplesPlugin; +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 TimersViewColumnPresentation implements IColumnPresentation { + + public static final String ID = DsfExamplesPlugin.PLUGIN_ID + ".TIMER_COLUMN_PRESENTATION_ID"; //$NON-NLS-1$ + public static final String COL_ID = ID + ".COL_ID"; //$NON-NLS-1$ + public static final String COL_VALUE = ID + ".COL_VALUE"; //$NON-NLS-1$ + + public void init(IPresentationContext context) {} + + public void dispose() {} + + public String[] getAvailableColumns() { + return new String[] { COL_ID, COL_VALUE }; + } + + public String getHeader(String id) { + if (COL_ID.equals(id)) { + return "ID"; //$NON-NLS-1$ + } else if (COL_VALUE.equals(id)) { + return "Value"; //$NON-NLS-1$ + } + return null; + } + + public String getId() { + return ID; + } + + public ImageDescriptor getImageDescriptor(String id) { + return null; + } + + public String[] getInitialColumns() { + return getAvailableColumns(); + } + + public boolean isOptional() { + return true; + } + +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TriggerCellModifier.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TriggerCellModifier.java new file mode 100644 index 00000000000..b7de00a0ca4 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TriggerCellModifier.java @@ -0,0 +1,258 @@ +/******************************************************************************* + * 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.examples.dsf.timers; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.RejectedExecutionException; + +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.service.DsfServices; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.examples.dsf.DsfExamplesPlugin; +import org.eclipse.cdt.examples.dsf.timers.AlarmService.TriggerDMContext; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.viewers.ICellModifier; +import org.eclipse.swt.widgets.Shell; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.util.tracker.ServiceTracker; + +/** + * Cell modifier used to edit the trigger value. + */ +@ThreadSafeAndProhibitedFromDsfExecutor("fSession.getExecutor()") +public class TriggerCellModifier implements ICellModifier { + + private final DsfSession fSession; + + // Need to use the OSGi service tracker (instead of DsfServiceTracker), + // because it's being accessed on multiple threads. + @ThreadSafe + private ServiceTracker fServiceTracker; + + /** + * Constructor for the modifier requires a valid session in order to + * initialize the service tracker. + * @param session DSF session this modifier will use. + */ + public TriggerCellModifier(DsfSession session) { + fSession = session; + } + + public boolean canModify(Object element, String property) { + return TimersViewColumnPresentation.COL_VALUE.equals(property) && + getAlarmDMC(element) != null; + } + + public Object getValue(Object element, String property) { + if (!TimersViewColumnPresentation.COL_VALUE.equals(property)) return ""; + + // Get the context and the session. If element is not an trigger + // context or if the session is stale then bail out. + TriggerDMContext triggerCtx = getAlarmDMC(element); + if (triggerCtx == null) return ""; + DsfSession session = DsfSession.getSession(triggerCtx.getSessionId()); + if (session == null) return ""; + + // Create the query to request the value from service. + GetValueQuery query = new GetValueQuery(triggerCtx); + try { + session.getExecutor().execute(query); + } catch (RejectedExecutionException e) { + return ""; + } + try { + return query.get().toString(); + } catch (InterruptedException e) { + assert false; + return ""; + } catch (ExecutionException e) { + return ""; + } + } + + + public void modify(Object element, String property, Object value) { + if (!TimersViewColumnPresentation.COL_VALUE.equals(property)) return; + + TriggerDMContext dmc = getAlarmDMC(element); + if (dmc == null) return; + DsfSession session = DsfSession.getSession(dmc.getSessionId()); + if (session == null) return; + + // Shell is used in displaying error dialogs. + Shell shell = getShell(); + if (shell == null) return; + + Integer intValue = null; + if (value instanceof String) { + try { + intValue = new Integer(((String)value).trim()); + } catch (NumberFormatException e) { + MessageDialog.openError(shell, "Invalid Value", + "Please enter a positive integer"); + return; + } + if (intValue.intValue() <= 0) { + MessageDialog.openError(shell, "Invalid Value", + "Please enter a positive integer"); + return; + } + } + + // Create the query to write the value to the service. + SetValueQuery query = new SetValueQuery(dmc, intValue); + + try { + session.getExecutor().execute(query); + } catch (RejectedExecutionException e) { + // View must be shutting down, no need to show error dialog. + } + try { + // Return value is irrelevant, any error would come through with an exception. + query.get().toString(); + } catch (InterruptedException e) { + assert false; + } catch (ExecutionException e) { + // View must be shutting down, no need to show error dialog. + } + } + + /** + * Need to dispose the cell modifier property because of the service + * tracker. + */ + @ThreadSafe + public synchronized void dispose() { + if (fServiceTracker != null) { + fServiceTracker.close(); + } + } + + private Shell getShell() { + if (DsfExamplesPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow() != null) { + return DsfExamplesPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getShell(); + } + return null; + } + + private TriggerDMContext getAlarmDMC(Object element) { + if (element instanceof IAdaptable) { + return (TriggerDMContext)((IAdaptable)element).getAdapter(TriggerDMContext.class); + } + return null; + } + + @ThreadSafe + private synchronized AlarmService getService(TriggerDMContext dmc) { + // Create and initialize the service tracker if needed. + String serviceId = DsfServices.createServiceFilter( AlarmService.class, fSession.getId() ); + if (fServiceTracker == null) { + try { + fServiceTracker = new ServiceTracker( + DsfExamplesPlugin.getBundleContext(), + DsfExamplesPlugin.getBundleContext().createFilter(serviceId), + null); + fServiceTracker.open(); + } catch (InvalidSyntaxException e) { + return null; + } + } + // Get the service. + return (AlarmService)fServiceTracker.getService(); + } + + + private class GetValueQuery extends Query<Integer> { + final TriggerDMContext fDmc; + + private GetValueQuery(TriggerDMContext dmc) { + super(); + fDmc = dmc; + } + + @Override + protected void execute(final DataRequestMonitor<Integer> 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 execute the "done" argument. + // In that case, cancel to notify waiting thread. + final DsfSession session = DsfSession.getSession(fDmc.getSessionId()); + if (session == null) { + cancel(false); + return; + } + + AlarmService service = getService(fDmc); + if (service == null) { + rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, + "Service not available", null)); + rm.done(); + return; + } + + int value = service.getTriggerValue(fDmc); + if (value == -1) { + rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_HANDLE, + "Invalid context", null)); + rm.done(); + return; + } + + rm.setData(value); + rm.done(); + } + } + + private class SetValueQuery extends Query<Object> { + + TriggerDMContext fDmc; + int fValue; + + SetValueQuery(TriggerDMContext dmc, int value) { + super(); + fDmc = dmc; + fValue = value; + } + + @Override + protected void execute(final DataRequestMonitor<Object> rm) { + // Guard against terminated session + final DsfSession session = DsfSession.getSession(fDmc.getSessionId()); + if (session == null) { + cancel(false); + return; + } + + // Guard against a disposed service + AlarmService service = getService(fDmc); + if (service == null) { + rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, + "Service not available", null)); + rm.done(); + return; + } + + // Finally set the value and return. + service.setTriggerValue(fDmc, fValue); + + // Return value is irrelevant. + rm.setData(new Object()); + rm.done(); + } + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TriggersVMNode.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TriggersVMNode.java new file mode 100644 index 00000000000..4b6cef7d88a --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TriggersVMNode.java @@ -0,0 +1,201 @@ +/******************************************************************************* + * 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.examples.dsf.timers; + +import java.text.MessageFormat; +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.RequestMonitor; +import org.eclipse.cdt.dsf.service.DsfSession; +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.properties.IElementPropertiesProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.IPropertiesUpdate; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelAttribute; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelColumnInfo; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelImage; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelText; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.PropertyBasedLabelProvider; +import org.eclipse.cdt.examples.dsf.DsfExamplesPlugin; +import org.eclipse.cdt.examples.dsf.timers.AlarmService.TriggerDMContext; +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.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.jface.viewers.CellEditor; +import org.eclipse.jface.viewers.ICellModifier; +import org.eclipse.jface.viewers.TextCellEditor; +import org.eclipse.swt.widgets.Composite; + + +/** + * View model node that defines how alarm DMContexts are displayed in the view. Alarm + * nodes are fairly static, once they are created their label doesn't change. + * @see TriggerDMContext + */ +@SuppressWarnings("restriction") +class TriggersVMNode extends AbstractDMVMNode + implements IElementEditor, IElementPropertiesProvider, IElementLabelProvider +{ + private static final String PROP_TRIGGER_NUMBER = "alarmNumber"; + private static final String PROP_TRIGGER_VALUE = "alarmTriggerValue"; + + // Create and configure the label provider. + private static final PropertyBasedLabelProvider fgLabelProvider; + static { + fgLabelProvider = new PropertyBasedLabelProvider(); + + LabelColumnInfo idCol = new LabelColumnInfo( + new LabelAttribute[] { + new LabelText(new MessageFormat("Trigger #{0}"), + new String[] { PROP_TRIGGER_NUMBER }), + new LabelImage(DsfExamplesPlugin.getDefault().getImageRegistry(). + getDescriptor(DsfExamplesPlugin.IMG_ALARM)) + }); + fgLabelProvider.setColumnInfo(TimersViewColumnPresentation.COL_ID, idCol); + + LabelColumnInfo valueCol = new LabelColumnInfo( + new LabelAttribute[] { + new LabelText(new MessageFormat("{0}"), + new String[] { PROP_TRIGGER_VALUE }) + }); + fgLabelProvider.setColumnInfo(TimersViewColumnPresentation.COL_VALUE, + valueCol); + } + + private TriggerCellModifier fAlarmCellModifier; + + public TriggersVMNode(AbstractDMVMProvider provider, DsfSession session) { + super(provider, session, TriggerDMContext.class); + } + + @Override + public String toString() { + return "TriggersVMNode(" + getSession().getId() + ")"; + } + + @Override + protected void updateElementsInSessionThread(final IChildrenUpdate update) { + AlarmService alarmService = getServicesTracker().getService(AlarmService.class, null); + if ( alarmService == null ) { + handleFailedUpdate(update); + return; + } + + TriggerDMContext[] triggers = alarmService.getTriggers(); + fillUpdateWithVMCs(update, triggers); + update.done(); + } + + public void update(ILabelUpdate[] updates) { + fgLabelProvider.update(updates); + } + + public void update(final IPropertiesUpdate[] updates) { + // Switch to the session thread before processing the updates. + try { + getSession().getExecutor().execute(new DsfRunnable() { + public void run() { + for (IPropertiesUpdate update : updates) { + updatePropertiesInSessionThread(update); + } + }}); + } catch (RejectedExecutionException e) { + for (IViewerUpdate update : updates) { + handleFailedUpdate(update); + } + } + } + + @ConfinedToDsfExecutor("getSession#getExecutor") + private void updatePropertiesInSessionThread(final IPropertiesUpdate update) { + // Find the trigger context in the element being updated + TriggerDMContext triggerCtx = findDmcInPath( + update.getViewerInput(), update.getElementPath(), TriggerDMContext.class); + AlarmService alarmService = getServicesTracker().getService(AlarmService.class, null); + + // If either update or service are not valid, fail the update and return. + if ( triggerCtx == null || alarmService == null) { + handleFailedUpdate(update); + return; + } + + // Calculate and set the update properties. + int value = alarmService.getTriggerValue(triggerCtx); + + if (value == -1) { + handleFailedUpdate(update); + return; + } + + update.setProperty(PROP_TRIGGER_NUMBER, triggerCtx.getTriggerNumber()); + update.setProperty(PROP_TRIGGER_VALUE, value); + update.done(); + } + + public CellEditor getCellEditor(IPresentationContext context, String columnId, + Object element, Composite parent) + { + // Create a cell editor to modify the trigger value. + if (TimersViewColumnPresentation.COL_VALUE.equals(columnId)) { + return new TextCellEditor(parent); + } + return null; + } + + // Note: this method is synchronized because IElementEditor.getCellModifier can be called + // on any thread, even though in practice it should be only called on the UI thread. + public ICellModifier getCellModifier(IPresentationContext context, + Object element) + { + // Create the cell modifier if needed. + if (fAlarmCellModifier == null) { + fAlarmCellModifier = new TriggerCellModifier(getSession()); + } + return fAlarmCellModifier; + } + + public int getDeltaFlags(Object e) { + // Since the label for triggers doesn't change, this node will generate + // delta info only if the list of alarms is changed. + if (e instanceof AlarmService.TriggersChangedEvent) { + return IModelDelta.CONTENT; + } + return IModelDelta.NO_CHANGE; + } + + + public void buildDelta(Object event, VMDelta parentDelta, int nodeOffset, + RequestMonitor requestMonitor) + { + if (event instanceof AlarmService.TriggersChangedEvent) { + // The list of alarms has changed, which means that the parent + // node needs to refresh its contents, which in turn will re-fetch the + // elements from this node. + parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); + } + requestMonitor.done(); + } + + @Override + public void dispose() { + if (fAlarmCellModifier != null) { + fAlarmCellModifier.dispose(); + } + super.dispose(); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/doc-files/package-1.png b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/doc-files/package-1.png Binary files differnew file mode 100644 index 00000000000..0e7c8683d61 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/doc-files/package-1.png diff --git a/dsf/org.eclipse.cdt.examples.dsf/src_ant/org/eclipse/cdt/examples/ant/tasks/PreProcessor.java b/dsf/org.eclipse.cdt.examples.dsf/src_ant/org/eclipse/cdt/examples/ant/tasks/PreProcessor.java new file mode 100644 index 00000000000..b90cfcefc12 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src_ant/org/eclipse/cdt/examples/ant/tasks/PreProcessor.java @@ -0,0 +1,292 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 + * Bjorn Freeman-Benson - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.examples.ant.tasks; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.Vector; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.types.FileSet; +import org.apache.tools.ant.util.FileUtils; + +/** + * Java preprocessor for code examples. Used to export source code for + * example plug-ins with parts of code missing/inserted etc., for + * various exercises. + * <p> + * The preprocessor looks for #ifdef statements in java comments, and is + * run with a set of symbols. For example: + * <pre> + * //#ifdef ex1 + * ... code to insert when 'ex1' symbol is on + * //#else + * ... code to insert when not 'ex1' + * //#endif + * </pre> + * </p> + */ +public class PreProcessor extends Task { + + private Vector fFileSets = new Vector(); + private File fDestDir = null; + private Set fSymbols = new HashSet(); + private FileUtils fUtils = FileUtils.getFileUtils(); + + // possible states + private static final int STATE_OUTSIDE_CONDITION = 0; + private static final int STATE_TRUE_CONDITION = 1; + private static final int STATE_FALSE_CONDITION = 2; + private static final int STATE_POST_TRUE_CONDITION = 3; + + // matchers + private Matcher IF_DEF_MATCHER = Pattern.compile("#ifdef\\s+\\w+").matcher(""); + private Matcher ELSE_IF_MATCHER = Pattern.compile("#elseif\\s+\\w+").matcher(""); + private Matcher ELSE_MATCHER = Pattern.compile("#else$|#else\\W+").matcher(""); + private Matcher END_MATCHER = Pattern.compile("#endif").matcher(""); + + + /** + * Constructs a new preprocessor task + */ + public PreProcessor() { + } + + /** + * Adds a set of files to process. + * + * @param set a set of files to process + */ + public void addFileset(FileSet set) { + fFileSets.addElement(set); + } + + /** + * Sets the destination directory for processed files. + * + * @param destDir destination directory for processed files + */ + public void setDestdir(File destDir) { + fDestDir = destDir; + } + + /** + * Sets the symbols that are "on" for the preprocessing. + * + * @param symbols symbols that are "on" for the preprocessing + */ + public void setSymbols(String symbols) { + String[] strings = symbols.split(","); + for (int i = 0; i < strings.length; i++) { + String string = strings[i].trim(); + if (string.length() > 0) { + fSymbols.add(string); + } + } + } + + public void execute() throws BuildException { + if (fSymbols.size() == 0) { + throw new BuildException("No symbols specified for preprocessor"); + } + if (fFileSets.isEmpty()) { + throw new BuildException("No filesets specified for processing"); + } + if (!fDestDir.exists()) { + throw new BuildException("destdir does not exist: " + fDestDir.getAbsolutePath()); + } + StringBuffer buf = new StringBuffer("Symbols: "); + String[] symbols = (String[]) fSymbols.toArray(new String[fSymbols.size()]); + for (int i = 0; i < symbols.length; i++) { + String symbol = symbols[i]; + buf.append(symbol); + if(i < (symbols.length -1)) { + buf.append(", "); + } + } + log(buf.toString()); + + Iterator fileSets = fFileSets.iterator(); + while (fileSets.hasNext()) { + FileSet fileSet = (FileSet) fileSets.next(); + DirectoryScanner scanner = fileSet.getDirectoryScanner(getProject()); + String[] includedFiles = scanner.getIncludedFiles(); + File baseDir = fileSet.getDir(getProject()); + for (int i = 0; i < includedFiles.length; i++) { + String fileName = includedFiles[i]; + processFile(baseDir, fileName, fDestDir); + } + } + + } + + /** + * Process the file + * @param baseDir base directory source file is relative to + * @param fileName source file name + * @param destDir root destination directory + */ + private void processFile(File baseDir, String fileName, File destDir) throws BuildException { + File destFile = new File(destDir, fileName); + File srcFile = new File(baseDir, fileName); + File dir = destFile.getParentFile(); + if (!dir.exists()) { + dir.mkdirs(); + } + String contents = null; + if (fileName.endsWith(".java")) { + contents = preProcessFile(srcFile, "//#"); + } else if (fileName.equals("plugin.xml")) { + contents = preProcessFile(srcFile, null); + } + if (contents == null) { + // no change, just copy file + try { + fUtils.copyFile(srcFile, destFile); + } catch (IOException e) { + throw new BuildException(e); + } + } else { + // write new file + FileWriter writer; + try { + writer = new FileWriter(destFile); + writer.write(contents); + writer.close(); + } catch (IOException e) { + throw new BuildException(e); + } + + } + } + + /** + * Preprocesses a file + * + * @param srcFile the file to process + * @param strip chars to stip off lines in a true condition, or <code>null</code> + * @return + */ + public String preProcessFile(File srcFile, String strip) { + try { + FileReader fileReader = new FileReader(srcFile); + BufferedReader reader = new BufferedReader(fileReader); + StringBuffer buffer = new StringBuffer(); + String line = reader.readLine(); + String activeSymbol = null; + int state = STATE_OUTSIDE_CONDITION; + boolean changed = false; + while (line != null) { + boolean ifdef = IF_DEF_MATCHER.reset(line).find(); + boolean elseif = ELSE_IF_MATCHER.reset(line).find(); + boolean elze = ELSE_MATCHER.reset(line).find(); + boolean endif = END_MATCHER.reset(line).find(); + boolean commandLine = ifdef || elseif || elze || endif; + boolean written = false; + switch (state) { + case STATE_OUTSIDE_CONDITION: + if (ifdef) { + String condition = line.substring(IF_DEF_MATCHER.start(), IF_DEF_MATCHER.end()); + String[] strings = condition.split("\\s+"); + activeSymbol = strings[1].trim(); + if (fSymbols.contains(activeSymbol)) { + state = STATE_TRUE_CONDITION; + } else { + state = STATE_FALSE_CONDITION; + } + } else if (elseif) { + throw new BuildException("#elseif encountered without corresponding #ifdef"); + } else if (elze) { + throw new BuildException("#else encountered without corresponding #ifdef (" + srcFile.getPath() + ")"); + } else if (endif) { + throw new BuildException("#endif encountered without corresponding #ifdef"); + } + break; + case STATE_TRUE_CONDITION: + if (elze || elseif) { + state = STATE_POST_TRUE_CONDITION; + break; + } else if (endif) { + state = STATE_OUTSIDE_CONDITION; + break; + } else if (ifdef) { + throw new BuildException("illegal nested #ifdef"); + } + case STATE_FALSE_CONDITION: + if (elseif) { + String condition = line.substring(ELSE_IF_MATCHER.start(), ELSE_IF_MATCHER.end()); + String[] strings = condition.split("\\s+"); + activeSymbol = strings[1].trim(); + if (fSymbols.contains(activeSymbol)) { + state = STATE_TRUE_CONDITION; + } else { + state = STATE_FALSE_CONDITION; + } + } else if (elze) { + state = STATE_TRUE_CONDITION; + break; + } else if (endif) { + state = STATE_OUTSIDE_CONDITION; + break; + } else if (ifdef) { + throw new BuildException("illegal nested #ifdef"); + } + case STATE_POST_TRUE_CONDITION: + if (endif) { + state = STATE_OUTSIDE_CONDITION; + break; + } else if (ifdef) { + throw new BuildException("illegal nested #ifdef"); + } + } + if (!commandLine) { + if (state == STATE_OUTSIDE_CONDITION || state == STATE_TRUE_CONDITION) { + if (state == STATE_TRUE_CONDITION && strip != null) { + if (line.startsWith(strip)) { + line = line.substring(strip.length()); + } + } + buffer.append(line); + buffer.append("\n"); + written = true; + } + } + changed = changed || !written; + line = reader.readLine(); + } + if (!changed) { + return null; + } + return buffer.toString(); + } catch (IOException e) { + throw new BuildException(e); + } + } + + public static void main(String[] args) { + PreProcessor processor = new PreProcessor(); + processor.setSymbols("ex2"); + String string = processor.preProcessFile(new File("c:\\eclipse3.1\\dev\\example.debug.core\\src\\example\\debug\\core\\launcher\\PDALaunchDelegate.java"), "//#"); + //String string = processor.preProcessFile(new File("c:\\eclipse3.1\\dev\\example.debug.core\\plugin.xml"), null); + System.out.println(string); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src_preprocess/org/eclipse/cdt/examples/dsf/dataviewer/AsyncDataViewer.java b/dsf/org.eclipse.cdt.examples.dsf/src_preprocess/org/eclipse/cdt/examples/dsf/dataviewer/AsyncDataViewer.java new file mode 100644 index 00000000000..bbf9bb8b921 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src_preprocess/org/eclipse/cdt/examples/dsf/dataviewer/AsyncDataViewer.java @@ -0,0 +1,276 @@ +/******************************************************************************* + * 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 + *******************************************************************************/ +//#ifdef exercises +package org.eclipse.cdt.examples.dsf.dataviewer; +//#else +//#package org.eclipse.cdt.examples.dsf.dataviewer.answers; +//#endif + +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +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.Query; +import org.eclipse.cdt.dsf.ui.concurrent.DisplayDsfExecutor; +import org.eclipse.jface.viewers.ILazyContentProvider; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Table; + +/** + * Data viewer based on a table, which reads data using asynchronous methods. + * <p> + * This viewer implements the {@link ILazyContentProvider} interface + * which is used by the JFace TableViewer class to populate a Table. This + * interface contains separate asynchronous methods for requesting the count + * and values for individual indexes, which neatly correspond to the methods + * in {@link IDataGenerator}. As an added optimization, this viewer + * implementation checks for the range of visible items in the view upon each + * request, and it cancels old requests which scroll out of view but have not + * been completed yet. However, it is up to the data generator implementation + * to check the canceled state of the requests and ignore them. + * </p> + */ +@ConfinedToDsfExecutor("fDisplayExecutor") +public class AsyncDataViewer + implements ILazyContentProvider, IDataGenerator.Listener +{ + // Executor to use instead of Display.asyncExec(). + @ThreadSafe + final private DsfExecutor fDisplayExecutor; + + // The viewer and generator that this content provider using. + final private TableViewer fViewer; + final private IDataGenerator fDataGenerator; + + // Fields used in request cancellation logic. + private List<ValueDataRequestMonitor> fItemDataRequestMonitors = new LinkedList<ValueDataRequestMonitor>(); + private Set<Integer> fIndexesToCancel = new HashSet<Integer>(); + private int fCancelCallsPending = 0; + + public AsyncDataViewer(TableViewer viewer, IDataGenerator generator) { + fViewer = viewer; + fDisplayExecutor = DisplayDsfExecutor.getDisplayDsfExecutor(fViewer.getTable().getDisplay()); + fDataGenerator = generator; + fDataGenerator.addListener(this); + } + + public void dispose() { + fDataGenerator.removeListener(this); + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + // Set the initial count to the viewer after the input is set. + queryItemCount(); + } + + public void updateElement(final int index) { + // Calculate the visible index range. + final int topIdx = fViewer.getTable().getTopIndex(); + final int botIdx = topIdx + getVisibleItemCount(topIdx); + + // Request the item for the given index. + queryValue(index); + + // Invoke a cancel task with a delay. The delay allows multiple cancel + // calls to be combined together improving performance of the viewer. + fCancelCallsPending++; + fDisplayExecutor.schedule( + new Runnable() { public void run() { + cancelStaleRequests(topIdx, botIdx); + }}, + 1, TimeUnit.MILLISECONDS); + } + + private int getVisibleItemCount(int top) { + Table table = fViewer.getTable(); + int itemCount = table.getItemCount(); + return Math.min((table.getBounds().height / table.getItemHeight()) + 2, itemCount - top); + } + + @ThreadSafe + public void countChanged() { + queryItemCount(); + } + + @ThreadSafe + public void valuesChanged(final Set<Integer> indexes) { + // Mark the changed items in table viewer as dirty, this will + // trigger update requests for these indexes if they are + // visible in the viewer. + final TableViewer tableViewer = fViewer; + fDisplayExecutor.execute( new Runnable() { + public void run() { + if (!fViewer.getTable().isDisposed()) { + for (Integer index : indexes) { + tableViewer.clear(index); + } + } + }}); + } + + + private void queryItemCount() { + // Request count from data provider. When the count is returned, we + // have to re-dispatch into the display thread to avoid calling + // the table widget on the DSF dispatch thread. + fIndexesToCancel.clear(); + fDataGenerator.getCount( + // Use the display executor to construct the request monitor, this + // will cause the handleCompleted() method to be automatically + // called on the display thread. + new DataRequestMonitor<Integer>(fDisplayExecutor, null) { + @Override + protected void handleCompleted() { + if (!fViewer.getTable().isDisposed()) { + fViewer.setItemCount(getData()); + fViewer.getTable().clearAll(); + } + } + }); + + } + + + // Dedicated class for data item requests. This class holds the index + // argument so it can be examined when canceling stale requests. + private class ValueDataRequestMonitor extends DataRequestMonitor<String> { + + /** Index is used when canceling stale requests. */ + int fIndex; + + ValueDataRequestMonitor(int index) { + super(fDisplayExecutor, null); + fIndex = index; + } + + @Override + protected void handleCompleted() { + fItemDataRequestMonitors.remove(this); + + // Check if the request completed successfully, otherwise ignore it. + if (isSuccess()) { + if (!fViewer.getTable().isDisposed()) { + fViewer.replace(getData(), fIndex); + } + } + } + } + + private void queryValue(final int index) { + ValueDataRequestMonitor rm = new ValueDataRequestMonitor(index); + fItemDataRequestMonitors.add(rm); + fDataGenerator.getValue(index, rm); + } + + private void cancelStaleRequests(int topIdx, int botIdx) { + // Decrement the count of outstanding cancel calls. + fCancelCallsPending--; + + // Must check again, in case disposed while re-dispatching. + if (fDataGenerator == null || fViewer.getTable().isDisposed()) return; + + // Go through the outstanding requests and cancel any that + // are not visible anymore. + for (Iterator<ValueDataRequestMonitor> itr = fItemDataRequestMonitors.iterator(); itr.hasNext();) { + ValueDataRequestMonitor item = itr.next(); + if (item.fIndex < topIdx || item.fIndex > botIdx) { + // Set the item to canceled status, so that the data provider + // will ignore it. + item.cancel(); + + // Add the item index to list of indexes that were canceled, + // which will be sent to the table widget. + fIndexesToCancel.add(item.fIndex); + + // Remove the item from the outstanding cancel requests. + itr.remove(); + } + } + if (!fIndexesToCancel.isEmpty() && fCancelCallsPending == 0) { + Set<Integer> canceledIdxs = fIndexesToCancel; + fIndexesToCancel = new HashSet<Integer>(); + + // Clear the indexes of the canceled request, so that the + // viewer knows to request them again when needed. + // Note: clearing using TableViewer.clear(int) seems very + // inefficient, it's better to use Table.clear(int[]). + int[] canceledIdxsArray = new int[canceledIdxs.size()]; + int i = 0; + for (Integer index : canceledIdxs) { + canceledIdxsArray[i++] = index; + } + fViewer.getTable().clear(canceledIdxsArray); + } + } + + + public static void main(String[] args) { + // Create the shell to hold the viewer. + Display display = new Display(); + Shell shell = new Shell(display, SWT.SHELL_TRIM); + shell.setLayout(new GridLayout()); + GridData data = new GridData(GridData.FILL_BOTH); + shell.setLayoutData(data); + Font font = new Font(display, "Courier", 10, SWT.NORMAL); + + // Create the table viewer. + TableViewer tableViewer = new TableViewer(shell, SWT.BORDER | SWT.VIRTUAL); + tableViewer.getControl().setLayoutData(data); + + // Create the data generator. + final IDataGenerator generator = new DataGeneratorWithExecutor(); + + // Create the content provider which will populate the viewer. + AsyncDataViewer contentProvider = new AsyncDataViewer(tableViewer, generator); + tableViewer.setContentProvider(contentProvider); + tableViewer.setInput(new Object()); + + // Open the shell and service the display dispatch loop until user + // closes the shell. + shell.open(); + while (!shell.isDisposed()) { + if (!display.readAndDispatch()) + display.sleep(); + } + + // The IDataGenerator.shutdown() method is asynchronous, this requires + // using a query again in order to wait for its completion. + Query<Object> shutdownQuery = new Query<Object>() { + @Override + protected void execute(DataRequestMonitor<Object> rm) { + generator.shutdown(rm); + } + }; + ImmediateExecutor.getInstance().execute(shutdownQuery); + try { + shutdownQuery.get(); + } catch (Exception e) {} + + // Shut down the display. + font.dispose(); + display.dispose(); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src_preprocess/org/eclipse/cdt/examples/dsf/dataviewer/DataGeneratorWithExecutor.java b/dsf/org.eclipse.cdt.examples.dsf/src_preprocess/org/eclipse/cdt/examples/dsf/dataviewer/DataGeneratorWithExecutor.java new file mode 100644 index 00000000000..1643017df75 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src_preprocess/org/eclipse/cdt/examples/dsf/dataviewer/DataGeneratorWithExecutor.java @@ -0,0 +1,438 @@ +/******************************************************************************* + * 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 + *******************************************************************************/ +//#ifdef exercises +package org.eclipse.cdt.examples.dsf.dataviewer; +//#else +//#package org.eclipse.cdt.examples.dsf.dataviewer.answers; +//#endif + +import java.util.HashSet; +//#ifdef answers +//#import java.util.Iterator; +//#endif +import java.util.LinkedList; +import java.util.List; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.TimeUnit; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +//#ifdef answers +//#import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; +//#import org.eclipse.cdt.dsf.concurrent.Immutable; +//#import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +//#endif +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DefaultDsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.examples.dsf.DsfExamplesPlugin; + +/** + * DSF Executor-based implementation of the data generator. + * <p> + * This generator uses a queue of client requests and processes these + * requests periodically using a DSF executor. The main feature of this + * generator is that it uses the executor as its only synchronization object. + * This means that all the fields with the exception of the executor can only + * be accessed while running in the executor thread. + * </p> + */ +//#ifdef exercises +//TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) +//indicating allowed thread access to this class/method/member +//#else +//#@ThreadSafe +//#endif +public class DataGeneratorWithExecutor implements IDataGenerator { + + // Request objects are used to serialize the interface calls into objects + // which can then be pushed into a queue. + //#ifdef exercises + // TODO Ecercise 4 - Add an annotationindicating allowed concurrency access + // Hint: Request and its subclasses have all their fields declared as final. + //#else +//# @Immutable + //#endif + abstract class Request { + final RequestMonitor fRequestMonitor; + + Request(RequestMonitor rm) { + fRequestMonitor = rm; + } + } + + //#ifdef exercises + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + //#else +//# @Immutable + //#endif + class CountRequest extends Request { + CountRequest(DataRequestMonitor<Integer> rm) { + super(rm); + } + } + + //#ifdef exercises + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + //#else +//# @Immutable + //#endif + class ItemRequest extends Request { + final int fIndex; + ItemRequest(int index, DataRequestMonitor<String> rm) { + super(rm); + fIndex = index; + } + } + + // The executor used to access all internal data of the generator. + //#ifdef exercises + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + // Hint: If a member does not have an annotation, the programmer can assume + // that the concurrency rule that applies to the class also applies to this + // member. + //#endif + private DsfExecutor fExecutor; + + // Main request queue of the data generator. The getValue(), getCount(), + // and shutdown() methods write into the queue, while the serviceQueue() + // method reads from it. + // The executor used to access all internal data of the generator. + //#ifdef exercises + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + //#else +//# @ConfinedToDsfExecutor("fExecutor") + //#endif + private List<Request> fQueue = new LinkedList<Request>(); + + // List of listeners is not synchronized, it also has to be accessed + // using the executor. + //#ifdef exercises + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + //#else +//# @ConfinedToDsfExecutor("fExecutor") + //#endif + private List<Listener> fListeners = new LinkedList<Listener>(); + + // Current number of elements in this generator. + //#ifdef exercises + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + //#else +//# @ConfinedToDsfExecutor("fExecutor") + //#endif + private int fCount = MIN_COUNT; + + // Counter used to determine when to reset the element count. + //#ifdef exercises + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + //#else +//# @ConfinedToDsfExecutor("fExecutor") + //#endif + private int fCountResetTrigger = 0; + + // Elements which were modified since the last reset. + //#ifdef exercises + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + //#else +//# @ConfinedToDsfExecutor("fExecutor") + //#endif + private Set<Integer> fChangedIndexes = new HashSet<Integer>(); + + // Flag used to ensure that requests are processed sequentially. + //#ifdef exercises + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + //#else +//# @ConfinedToDsfExecutor("fExecutor") + //#endif + private boolean fServiceQueueInProgress = false; + + //#ifdef exercises + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + //#endif + public DataGeneratorWithExecutor() { + // Create the executor + fExecutor = new DefaultDsfExecutor("Supplier Executor"); + + // Schedule a runnable to make the random changes. + fExecutor.scheduleAtFixedRate( + new DsfRunnable() { + public void run() { + randomChanges(); + } + }, + RANDOM_CHANGE_INTERVAL, + RANDOM_CHANGE_INTERVAL, + TimeUnit.MILLISECONDS); + } + + //#ifdef exercises + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + //#endif + public void shutdown(final RequestMonitor rm) { + try { + fExecutor.execute( new DsfRunnable() { + public void run() { + // Empty the queue of requests and fail them. + for (Request request : fQueue) { + request.fRequestMonitor.setStatus( + new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down")); + request.fRequestMonitor.done(); + } + fQueue.clear(); + + // Kill executor. + fExecutor.shutdown(); + rm.done(); + } + }); + } catch (RejectedExecutionException e) { + rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down")); + rm.done(); + } + } + + //#ifdef exercises + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + //#endif + public void getCount(final DataRequestMonitor<Integer> rm) { + try { + fExecutor.execute( new DsfRunnable() { + public void run() { + fQueue.add(new CountRequest(rm)); + serviceQueue(); + } + }); + } catch (RejectedExecutionException e) { + rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down")); + rm.done(); + } + } + + //#ifdef exercises + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + //#endif + public void getValue(final int index, final DataRequestMonitor<String> rm) { + try { + fExecutor.execute( new DsfRunnable() { + public void run() { + fQueue.add(new ItemRequest(index, rm)); + serviceQueue(); + } + }); + } catch (RejectedExecutionException e) { + rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down")); + rm.done(); + } + } + + //#ifdef exercises + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + //#endif + public void addListener(final Listener listener) { + try { + fExecutor.execute( new DsfRunnable() { + public void run() { + fListeners.add(listener); + } + }); + } catch (RejectedExecutionException e) {} + } + + //#ifdef exercises + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + //#endif + public void removeListener(final Listener listener) { + try { + fExecutor.execute( new DsfRunnable() { + public void run() { + fListeners.remove(listener); + } + }); + } catch (RejectedExecutionException e) {} + } + + // Main processing function of this generator. + //#ifdef exercises + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + //#else +//# @ConfinedToDsfExecutor("fExecutor") + //#endif + private void serviceQueue() { + + //#ifdef exercises + // TODO Exercise 3 - Add logic to discard cancelled requests from queue. + // Hint: Since serviceQueue() is called using the executor, and the + // fQueue list can only be modified when running in the executor + // thread. This method can safely iterate and modify fQueue without + // risk of race conditions or concurrent modification exceptions. + //#else +//# for (Iterator<Request> requestItr = fQueue.iterator(); requestItr.hasNext();) { +//# Request request = requestItr.next(); +//# if (request.fRequestMonitor.isCanceled()) { +//# request.fRequestMonitor.setStatus( +//# new Status(IStatus.CANCEL, DsfExamplesPlugin.PLUGIN_ID, "Request canceled")); +//# request.fRequestMonitor.done(); +//# requestItr.remove(); +//# } +//# } + //#endif + + // If a queue servicing is already scheduled, do nothing. + if (fServiceQueueInProgress) { + return; + } + + if (fQueue.size() != 0) { + // If there are requests to service, remove one from the queue and + // schedule a runnable to process the request after a processing + // delay. + fServiceQueueInProgress = true; + final Request request = fQueue.remove(0); + fExecutor.schedule( + new DsfRunnable() { + public void run() { + if (request instanceof CountRequest) { + processCountRequest((CountRequest)request); + } else if (request instanceof ItemRequest) { + processItemRequest((ItemRequest)request); + } + + // Reset the processing flag and process next + // request. + fServiceQueueInProgress = false; + serviceQueue(); + } + }, + PROCESSING_DELAY, TimeUnit.MILLISECONDS); + } + } + + //#ifdef exercises + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + //#else +//# @ConfinedToDsfExecutor("fExecutor") + //#endif + private void processCountRequest(CountRequest request) { + @SuppressWarnings("unchecked") // Suppress warning about lost type info. + DataRequestMonitor<Integer> rm = (DataRequestMonitor<Integer>)request.fRequestMonitor; + + rm.setData(fCount); + rm.done(); + } + + //#ifdef exercises + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + //#else +//# @ConfinedToDsfExecutor("fExecutor") + //#endif + private void processItemRequest(ItemRequest request) { + @SuppressWarnings("unchecked") // Suppress warning about lost type info. + DataRequestMonitor<String> rm = (DataRequestMonitor<String>)request.fRequestMonitor; + + if (fChangedIndexes.contains(request.fIndex)) { + rm.setData("Changed: " + request.fIndex); + } else { + rm.setData(Integer.toString(request.fIndex)); + } + rm.done(); + } + + /** + * This method simulates changes in the supplier's data set. + */ + //#ifdef exercises + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + //#else +//# @ConfinedToDsfExecutor("fExecutor") + //#endif + private void randomChanges() { + // Once every number of changes, reset the count, the rest of the + // times just change certain values. + if (++fCountResetTrigger % RANDOM_COUNT_CHANGE_INTERVALS == 0){ + randomCountReset(); + } else { + randomDataChange(); + } + } + + /** + * Calculates new size for provider's data set. + */ + //#ifdef exercises + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + //#else +//# @ConfinedToDsfExecutor("fExecutor") + //#endif + private void randomCountReset() { + // Calculate the new count. + Random random = new java.util.Random(); + fCount = MIN_COUNT + Math.abs(random.nextInt()) % (MAX_COUNT - MIN_COUNT); + + // Reset the changed values. + fChangedIndexes.clear(); + + // Notify listeners + for (Listener listener : fListeners) { + listener.countChanged(); + } + } + + /** + * Invalidates a random range of indexes. + */ + //#ifdef exercises + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + //#else +//# @ConfinedToDsfExecutor("fExecutor") + //#endif + private void randomDataChange() { + // Calculate the indexes to change. + Random random = new java.util.Random(); + Set<Integer> set = new HashSet<Integer>(); + for (int i = 0; i < fCount * RANDOM_CHANGE_SET_PERCENTAGE / 100; i++) { + set.add( new Integer(Math.abs(random.nextInt()) % fCount) ); + } + + // Add the indexes to an overall set of changed indexes. + fChangedIndexes.addAll(set); + + // Notify listeners + for (Listener listener : fListeners) { + listener.valuesChanged(set); + } + } +} + diff --git a/dsf/org.eclipse.cdt.examples.dsf/src_preprocess/org/eclipse/cdt/examples/dsf/dataviewer/DataGeneratorWithThread.java b/dsf/org.eclipse.cdt.examples.dsf/src_preprocess/org/eclipse/cdt/examples/dsf/dataviewer/DataGeneratorWithThread.java new file mode 100644 index 00000000000..f3647f59f5a --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src_preprocess/org/eclipse/cdt/examples/dsf/dataviewer/DataGeneratorWithThread.java @@ -0,0 +1,242 @@ +/******************************************************************************* + * 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 + *******************************************************************************/ +//#ifdef exercises +package org.eclipse.cdt.examples.dsf.dataviewer; +//#else +//#package org.eclipse.cdt.examples.dsf.dataviewer.answers; +//#endif + +import java.util.Collections; +import java.util.HashSet; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.ListenerList; +import org.eclipse.core.runtime.Status; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.examples.dsf.DsfExamplesPlugin; + +/** + * Thread-based implementation of the data generator. + * <p> + * This generator is based around a queue of client requests and a thread which + * reads the requests from the queue and processes them. The distinguishing + * feature of this generator is that it uses a a blocking queue as the main + * synchronization object. However, fListeners, fShutdown, and fChangedIndexes + * fields also need to be thread-safe and so they implement their own + * synchronization. + * </p> + */ +public class DataGeneratorWithThread extends Thread implements IDataGenerator { + + // Request objects are used to serialize the interface calls into objects + // which can then be pushed into a queue. + abstract class Request { + final RequestMonitor fRequestMonitor; + + Request(RequestMonitor rm) { + fRequestMonitor = rm; + } + } + + class CountRequest extends Request { + CountRequest(DataRequestMonitor<Integer> rm) { + super(rm); + } + } + + class ItemRequest extends Request { + final int fIndex; + ItemRequest(int index, DataRequestMonitor<String> rm) { + super(rm); + fIndex = index; + } + } + + class ShutdownRequest extends Request { + ShutdownRequest(RequestMonitor rm) { + super(rm); + } + } + + // Main request queue of the data generator. The getValue(), getCount(), + // and shutdown() methods write into the queue, while the run() method + // reads from it. + private final BlockingQueue<Request> fQueue = new LinkedBlockingQueue<Request>(); + + // ListenerList class provides thread safety. + private ListenerList fListeners = new ListenerList(); + + // Current number of elements in this generator. + private int fCount = MIN_COUNT; + + // Counter used to determine when to reset the element count. + private int fCountResetTrigger = 0; + + // Elements which were modified since the last reset. + private Set<Integer> fChangedIndexes = Collections.synchronizedSet(new HashSet<Integer>()); + + // Used to determine when to make changes in data. + private long fLastChangeTime = System.currentTimeMillis(); + + // Flag indicating when the generator has been shut down. + private AtomicBoolean fShutdown = new AtomicBoolean(false); + + public DataGeneratorWithThread() { + // Immediately kick off the request processing thread. + start(); + } + + public void shutdown(RequestMonitor rm) { + // Mark the generator as shut down. After the fShutdown flag is set, + // all new requests should be shut down. + if (!fShutdown.getAndSet(true)) { + fQueue.add(new ShutdownRequest(rm)); + } else { + // + rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down")); + rm.done(); + } + } + + public void getCount(DataRequestMonitor<Integer> rm) { + if (!fShutdown.get()) { + fQueue.add(new CountRequest(rm)); + } else { + rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down")); + rm.done(); + } + } + + public void getValue(int index, DataRequestMonitor<String> rm) { + if (!fShutdown.get()) { + fQueue.add(new ItemRequest(index, rm)); + } else { + rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down")); + rm.done(); + } + } + + public void addListener(Listener listener) { + fListeners.add(listener); + } + + public void removeListener(Listener listener) { + fListeners.remove(listener); + } + + @Override + public void run() { + try { + while(true) { + // Get the next request from the queue. The time-out + // ensures that that the random changes get processed. + final Request request = fQueue.poll(100, TimeUnit.MILLISECONDS); + + // If a request was dequeued, process it. + if (request != null) { + // Simulate a processing delay. + Thread.sleep(PROCESSING_DELAY); + + if (request instanceof CountRequest) { + processCountRequest((CountRequest)request); + } else if (request instanceof ItemRequest) { + processItemRequest((ItemRequest)request); + } else if (request instanceof ShutdownRequest) { + // If shutting down, just break out of the while(true) + // loop and thread will exit. + request.fRequestMonitor.done(); + break; + } + } + + // Simulate data changes. + randomChanges(); + } + } + catch (InterruptedException x) {} + } + + private void processCountRequest(CountRequest request) { + @SuppressWarnings("unchecked") // Suppress warning about lost type info. + DataRequestMonitor<Integer> rm = (DataRequestMonitor<Integer>)request.fRequestMonitor; + + rm.setData(fCount); + rm.done(); + } + + private void processItemRequest(ItemRequest request) { + @SuppressWarnings("unchecked") // Suppress warning about lost type info. + DataRequestMonitor<String> rm = (DataRequestMonitor<String>)request.fRequestMonitor; + + if (fChangedIndexes.contains(request.fIndex)) { + rm.setData("Changed: " + request.fIndex); + } else { + rm.setData(Integer.toString(request.fIndex)); + } + rm.done(); + } + + + private void randomChanges() { + // Check if enough time is elapsed. + if (System.currentTimeMillis() > fLastChangeTime + RANDOM_CHANGE_INTERVAL) { + fLastChangeTime = System.currentTimeMillis(); + + // Once every number of changes, reset the count, the rest of the + // times just change certain values. + if (++fCountResetTrigger % RANDOM_COUNT_CHANGE_INTERVALS == 0){ + randomCountReset(); + } else { + randomDataChange(); + } + } + } + + private void randomCountReset() { + // Calculate the new count. + Random random = new java.util.Random(); + fCount = MIN_COUNT + Math.abs(random.nextInt()) % (MAX_COUNT - MIN_COUNT); + + // Reset the changed values. + fChangedIndexes.clear(); + + // Notify listeners + for (Object listener : fListeners.getListeners()) { + ((Listener)listener).countChanged(); + } + } + + private void randomDataChange() { + // Calculate the indexes to change. + Random random = new java.util.Random(); + Set<Integer> set = new HashSet<Integer>(); + for (int i = 0; i < fCount * RANDOM_CHANGE_SET_PERCENTAGE / 100; i++) { + set.add( new Integer(Math.abs(random.nextInt()) % fCount) ); + } + + // Add the indexes to an overall set of changed indexes. + fChangedIndexes.addAll(set); + + // Notify listeners + for (Object listener : fListeners.getListeners()) { + ((Listener)listener).valuesChanged(set); + } + } +} + + diff --git a/dsf/org.eclipse.cdt.examples.dsf/src_preprocess/org/eclipse/cdt/examples/dsf/dataviewer/IDataGenerator.java b/dsf/org.eclipse.cdt.examples.dsf/src_preprocess/org/eclipse/cdt/examples/dsf/dataviewer/IDataGenerator.java new file mode 100644 index 00000000000..882b0547211 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src_preprocess/org/eclipse/cdt/examples/dsf/dataviewer/IDataGenerator.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * 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 + *******************************************************************************/ +//#ifdef exercises +package org.eclipse.cdt.examples.dsf.dataviewer; +//#else +//#package org.eclipse.cdt.examples.dsf.dataviewer.answers; +//#endif + +import java.util.Set; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +//#ifdef answers +//#import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +//#endif + +/** + * Data generator is simple source of data used to populate the example table + * view. It contains two asynchronous methods for retrieving the data + * parameters: the count and the value for a given index. It also allows the + * view to receive events indicating when the data supplied by the generator + * is changed. + */ +//#ifdef exercises +// TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) +// indicating allowed thread access to this class/method/member +//#else +//#@ThreadSafe +//#endif +public interface IDataGenerator { + + // Constants which control the data generator behavior. + // Changing the count range can stress the scalability of the system, while + // changing of the process delay and random change interval can stress + // its performance. + final static int MIN_COUNT = 100; + final static int MAX_COUNT = 200; + final static int PROCESSING_DELAY = 10; + final static int RANDOM_CHANGE_INTERVAL = 10000; + final static int RANDOM_COUNT_CHANGE_INTERVALS = 3; + final static int RANDOM_CHANGE_SET_PERCENTAGE = 10; + + + // Listener interface that the view needs to implement to react + // to the changes in data. + public interface Listener { + void countChanged(); + void valuesChanged(Set<Integer> indexes); + } + + // Data access methods. + void getCount(DataRequestMonitor<Integer> rm); + void getValue(int index, DataRequestMonitor<String> rm); + + // Method used to shutdown the data generator including any threads that + // it may use. + void shutdown(RequestMonitor rm); + + // Methods for registering change listeners. + void addListener(Listener listener); + void removeListener(Listener listener); +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src_preprocess/org/eclipse/cdt/examples/dsf/dataviewer/SyncDataViewer.java b/dsf/org.eclipse.cdt.examples.dsf/src_preprocess/org/eclipse/cdt/examples/dsf/dataviewer/SyncDataViewer.java new file mode 100644 index 00000000000..6f4dec363e8 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src_preprocess/org/eclipse/cdt/examples/dsf/dataviewer/SyncDataViewer.java @@ -0,0 +1,195 @@ +/******************************************************************************* + * 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 + *******************************************************************************/ +//#ifdef exercises +package org.eclipse.cdt.examples.dsf.dataviewer; +//#else +//#package org.eclipse.cdt.examples.dsf.dataviewer.answers; +//#endif + +import java.util.Set; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; +import org.eclipse.cdt.dsf.concurrent.Query; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +/** + * Data viewer based on a table, which reads data using synchronous methods. + * <p> + * This viewer implements the {@link IStructuredContentProvider} interface + * which is used by the JFace TableViewer class to populate a Table. This + * interface contains one principal methods for reading data {@link #getElements(Object)}, + * which synchronously returns an array of elements. In order to implement this + * method using the asynchronous data generator, this provider uses the + * {@link Query} object. + * </p> + */ +public class SyncDataViewer + implements IStructuredContentProvider, IDataGenerator.Listener +{ + // The viewer and generator that this content provider using. + final private TableViewer fViewer; + final private IDataGenerator fDataGenerator; + + public SyncDataViewer(TableViewer viewer, IDataGenerator generator) { + fViewer = viewer; + fDataGenerator = generator; + fDataGenerator.addListener(this); + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + // Not used + } + + + public Object[] getElements(Object inputElement) { + + // Create the query object for reading data count. + Query<Integer> countQuery = new Query<Integer>() { + @Override + protected void execute(DataRequestMonitor<Integer> rm) { + fDataGenerator.getCount(rm); + } + }; + + // Submit the query to be executed. A query implements a runnable + // interface and it has to be executed in order to do its work. + ImmediateExecutor.getInstance().execute(countQuery); + int count = 0; + + // Block until the query completes, which will happen when the request + // monitor of the execute() method is marked done. + try { + count = countQuery.get(); + } catch (Exception e) { + // InterruptedException and ExecutionException can be thrown here. + // ExecutionException containing a CoreException will be thrown + // if an error status is set to the Query's request monitor. + return new Object[0]; + } + + // Create the array that will be filled with elements. + // For each index in the array execute a query to get the element at + // that index. + final Object[] elements = new Object[count]; + + for (int i = 0; i < count; i++) { + final int index = i; + Query<String> valueQuery = new Query<String>() { + @Override + protected void execute(DataRequestMonitor<String> rm) { + fDataGenerator.getValue(index, rm); + } + }; + ImmediateExecutor.getInstance().execute(valueQuery); + try { + elements[i] = valueQuery.get(); + } catch (Exception e) { + elements[i] = "error"; + } + } + return elements; + } + + public void dispose() { + fDataGenerator.removeListener(this); + } + + public void countChanged() { + // For any event from the generator, refresh the whole viewer. + refreshViewer(); + } + + public void valuesChanged(Set<Integer> indexes) { + // For any event from the generator, refresh the whole viewer. + refreshViewer(); + } + + private void refreshViewer() { + //#ifdef exercises + // TODO Exercise 5 - Add a call to getElements() to force a deadlock. + //#else +//# getElements(null); + //#endif + + // This method may be called on any thread, switch to the display + // thread before calling the viewer. + Display display = fViewer.getControl().getDisplay(); + display.asyncExec( new Runnable() { + public void run() { + if (!fViewer.getControl().isDisposed()) { + fViewer.refresh(); + } + } + }); + } + + public static void main(String[] args) { + // Create the shell to hold the viewer. + Display display = new Display(); + Shell shell = new Shell(display, SWT.SHELL_TRIM); + shell.setLayout(new GridLayout()); + GridData data = new GridData(GridData.FILL_BOTH); + shell.setLayoutData(data); + Font font = new Font(display, "Courier", 10, SWT.NORMAL); + + // Create the table viewer. + TableViewer tableViewer = new TableViewer(shell, SWT.BORDER); + tableViewer.getControl().setLayoutData(data); + + // Create the data generator. + //#ifdef exercises + // TODO Exercise 5 - Use the DataGeneratorWithExecutor() instead. + final IDataGenerator generator = new DataGeneratorWithThread(); + //#else +//# final IDataGenerator generator = new DataGeneratorWithExecutor(); + //#endif + + // Create the content provider which will populate the viewer. + SyncDataViewer contentProvider = new SyncDataViewer(tableViewer, generator); + tableViewer.setContentProvider(contentProvider); + tableViewer.setInput(new Object()); + + // Open the shell and service the display dispatch loop until user + // closes the shell. + shell.open(); + while (!shell.isDisposed()) { + if (!display.readAndDispatch()) + display.sleep(); + } + + // The IDataGenerator.shutdown() method is asynchronous, this requires + // using a query again in order to wait for its completion. + Query<Object> shutdownQuery = new Query<Object>() { + @Override + protected void execute(DataRequestMonitor<Object> rm) { + generator.shutdown(rm); + } + }; + ImmediateExecutor.getInstance().execute(shutdownQuery); + try { + shutdownQuery.get(); + } catch (Exception e) {} + + // Shut down the display. + font.dispose(); + display.dispose(); + } + +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src_preprocess/org/eclipse/cdt/examples/dsf/requestmonitor/Async2Plus2.java b/dsf/org.eclipse.cdt.examples.dsf/src_preprocess/org/eclipse/cdt/examples/dsf/requestmonitor/Async2Plus2.java new file mode 100644 index 00000000000..f2587380c4d --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src_preprocess/org/eclipse/cdt/examples/dsf/requestmonitor/Async2Plus2.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * 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 + *******************************************************************************/ +//#ifdef exercises +package org.eclipse.cdt.examples.dsf.requestmonitor; +//#else +//#package org.eclipse.cdt.examples.dsf.requestmonitor.answers; +//#endif + +import java.util.concurrent.Executor; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; + +/** + * Example of using a DataRequestMonitor to retrieve a result from an + * asynchronous method. + */ +public class Async2Plus2 { + + public static void main(String[] args) { + Executor executor = ImmediateExecutor.getInstance(); + DataRequestMonitor<Integer> rm = + new DataRequestMonitor<Integer>(executor, null) { + @Override + protected void handleCompleted() { + System.out.println("2 + 2 = " + getData()); + } + }; + asyncAdd(2, 2, rm); + } + + static void asyncAdd(int value1, int value2, DataRequestMonitor<Integer> rm) { + rm.setData(value1 + value2); + rm.done(); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src_preprocess/org/eclipse/cdt/examples/dsf/requestmonitor/AsyncHelloWorld.java b/dsf/org.eclipse.cdt.examples.dsf/src_preprocess/org/eclipse/cdt/examples/dsf/requestmonitor/AsyncHelloWorld.java new file mode 100644 index 00000000000..81f23f61a2d --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src_preprocess/org/eclipse/cdt/examples/dsf/requestmonitor/AsyncHelloWorld.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * 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 + *******************************************************************************/ +//#ifdef exercises +package org.eclipse.cdt.examples.dsf.requestmonitor; +//#else +//#package org.eclipse.cdt.examples.dsf.requestmonitor.answers; +//#endif + +import java.util.concurrent.Executor; + +import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; + +/** + * "Hello world" example which uses an asynchronous method to print out + * the result. + * <p> + * The main method uses an immediate executor, which executes runnables + * as soon as they are submitted, in creating its request monitor. + * + */ +public class AsyncHelloWorld { + + public static void main(String[] args) { + Executor executor = ImmediateExecutor.getInstance(); + RequestMonitor rm = new RequestMonitor(executor, null); + asyncHelloWorld(rm); + } + + static void asyncHelloWorld(RequestMonitor rm) { + System.out.println("Hello world"); + //#ifdef exercises + // TODO Exercise 1: - Call the second async. "Hello world 2" method. + // Hint: Calling an asynchronous method requires passing to it a + // request monitor. A new request monitor can be constructed with + // a parent RequestMonitor as an argument argument. The parent gets + // completed automatically when the lower level request monitor is + // completed. + rm.done(); + //#else +//# RequestMonitor rm2 = new RequestMonitor(ImmediateExecutor.getInstance(), rm); +//# asyncHelloWorld2(rm2); + //#endif + } + + //#ifdef exercises + // TODO: Exercise 1 - Add a second async. "Hello world 2" method. + //#else +//# static void asyncHelloWorld2(RequestMonitor rm) { +//# System.out.println("Hello world 2"); +//# rm.done(); +//# } + //#endif +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src_preprocess/org/eclipse/cdt/examples/dsf/requestmonitor/AsyncQuicksort.java b/dsf/org.eclipse.cdt.examples.dsf/src_preprocess/org/eclipse/cdt/examples/dsf/requestmonitor/AsyncQuicksort.java new file mode 100644 index 00000000000..2adb1a02c28 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src_preprocess/org/eclipse/cdt/examples/dsf/requestmonitor/AsyncQuicksort.java @@ -0,0 +1,146 @@ +/******************************************************************************* + * 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 + *******************************************************************************/ +//#ifdef exercises +package org.eclipse.cdt.examples.dsf.requestmonitor; +//#else +//#package org.eclipse.cdt.examples.dsf.requestmonitor.answers; +//#endif + +import java.util.Arrays; +import java.util.concurrent.Executor; + +import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor; +//#ifdef answers +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +//#endif +import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; + +/** + * Example of using a CountingRequestMonitor to wait for multiple + * asynchronous calls to complete. + */ +public class AsyncQuicksort { + + static Executor fgExecutor = ImmediateExecutor.getInstance(); + + public static void main(String[] args) { + final int[] array = {5, 7, 8, 3, 2, 1, 9, 5, 4}; + + System.out.println("To sort: " + Arrays.toString(array)); + asyncQuicksort( + array, 0, array.length - 1, + new RequestMonitor(fgExecutor, null) { + @Override + protected void handleCompleted() { + System.out.println("Sorted: " + Arrays.toString(array)); + } + }); + } + + static void asyncQuicksort(final int[] array, final int left, + final int right, final RequestMonitor rm) + { + if (right > left) { + int pivot = left; + //#ifdef exercises + // TODO: Exercise 2 - Convert the call to partition into an + // asynchronous call to asyncPartition(). + // Hint: The rest of the code below should be executed inside + // the DataRequestMonitor.handleCompleted() overriding method. + int newPivot = partition(array, left, right, pivot); + printArray(array, left, right, newPivot); + + CountingRequestMonitor countingRm = new CountingRequestMonitor(fgExecutor, rm); + asyncQuicksort(array, left, newPivot - 1, countingRm); + asyncQuicksort(array, newPivot + 1, right, countingRm); + countingRm.setDoneCount(2); + //#else +//# asyncPartition( +//# array, left, right, pivot, +//# new DataRequestMonitor<Integer>(fgExecutor, rm) { +//# @Override +//# protected void handleCompleted() { +//# int newPivot = getData(); +//# printArray(array, left, right, newPivot); +//# +//# CountingRequestMonitor countingRm = new CountingRequestMonitor(fgExecutor, rm); +//# asyncQuicksort(array, left, newPivot - 1, countingRm); +//# asyncQuicksort(array, newPivot + 1, right, countingRm); +//# countingRm.setDoneCount(2); +//# } +//# }); + //#endif + } else { + rm.done(); + } + } + + //#ifdef exercises + // TODO Exercise 2 - Convert partition to an asynchronous method. + // Hint: a DataRequestMonitor<Integer> should be used to carry the + // return value to the caller. + static int partition(int[] array, int left, int right, int pivot) + //#else +//# static void asyncPartition(int[] array, int left, int right, int pivot, DataRequestMonitor<Integer> rm) + //#endif + { + int pivotValue = array[pivot]; + array[pivot] = array[right]; + array[right] = pivotValue; + int store = left; + for (int i = left; i < right; i++) { + if (array[i] <= pivotValue) { + int tmp = array[store]; + array[store] = array[i]; + array[i] = tmp; + store++; + } + } + array[right] = array[store]; + array[store] = pivotValue; + + //#ifdef exercises + // TODO: Request Monitors Exercise 2 - Return the data to caller using + // a request monitor. + return store; + //#else +//# // Java 5 automatically converts the int type of the store variable +//# // to an Integer object. +//# rm.setData(store); +//# rm.done(); + //#endif + } + + static void printArray(int[] array, int left, int right, int pivot) { + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < array.length; i++ ) { + if (i == left) { + buffer.append('>'); + } else if (i == pivot) { + buffer.append('-'); + } else { + buffer.append(' '); + } + buffer.append(array[i]); + + if (i == right) { + buffer.append('<'); + } else if (i == pivot) { + buffer.append('-'); + } else { + buffer.append(' '); + } + } + + System.out.println(buffer); + } +} diff --git a/dsf/org.eclipse.cdt.tests.dsf/.classpath b/dsf/org.eclipse.cdt.tests.dsf/.classpath new file mode 100644 index 00000000000..304e86186aa --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/.classpath @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/> + <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/dsf/org.eclipse.cdt.tests.dsf/.cvsignore b/dsf/org.eclipse.cdt.tests.dsf/.cvsignore new file mode 100644 index 00000000000..ba077a4031a --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/.cvsignore @@ -0,0 +1 @@ +bin diff --git a/dsf/org.eclipse.cdt.tests.dsf/.project b/dsf/org.eclipse.cdt.tests.dsf/.project new file mode 100644 index 00000000000..1764ef4ec6f --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/.project @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>org.eclipse.cdt.tests.dsf</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.ManifestBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.SchemaBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.pde.PluginNature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/dsf/org.eclipse.cdt.tests.dsf/.settings/org.eclipse.jdt.core.prefs b/dsf/org.eclipse.cdt.tests.dsf/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..8f1321e3841 --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,70 @@ +#Thu Sep 25 17:12:53 PDT 2008 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.5 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.autoboxing=ignore +org.eclipse.jdt.core.compiler.problem.deprecation=warning +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning +org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=warning +org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore +org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore +org.eclipse.jdt.core.compiler.problem.nullReference=error +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning +org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled +org.eclipse.jdt.core.compiler.problem.unusedImport=error +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=warning +org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.source=1.5 diff --git a/dsf/org.eclipse.cdt.tests.dsf/.settings/org.eclipse.jdt.ui.prefs b/dsf/org.eclipse.cdt.tests.dsf/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 00000000000..28984618f01 --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,3 @@ +#Thu Oct 12 11:27:35 PDT 2006 +eclipse.preferences.version=1 +internal.default.compliance=default diff --git a/dsf/org.eclipse.cdt.tests.dsf/META-INF/MANIFEST.MF b/dsf/org.eclipse.cdt.tests.dsf/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..bbf5a59a186 --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/META-INF/MANIFEST.MF @@ -0,0 +1,17 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %pluginName +Bundle-Vendor: %pluginProvider +Bundle-SymbolicName: org.eclipse.cdt.tests.dsf;singleton:=true +Bundle-Version: 2.0.0.qualifier +Bundle-Activator: org.eclipse.cdt.tests.dsf.DsfTestPlugin +Bundle-Localization: plugin +Bundle-ActivationPolicy: lazy +Require-Bundle: org.eclipse.core.runtime, + org.eclipse.debug.core, + org.eclipse.debug.ui, + org.eclipse.cdt.dsf, + org.junit4, + org.eclipse.ui, + org.eclipse.cdt.dsf.ui +Bundle-RequiredExecutionEnvironment: J2SE-1.5 diff --git a/dsf/org.eclipse.cdt.tests.dsf/about.html b/dsf/org.eclipse.cdt.tests.dsf/about.html new file mode 100644 index 00000000000..04492dd7e1b --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/about.html @@ -0,0 +1,24 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"><head> + + +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>About</title></head><body lang="EN-US"> +<h2>About This Content</h2> + +<p>May 14, 2008</p> +<h3>License</h3> + +<p>The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>. +For purposes of the EPL, "Program" will mean the Content.</p> + +<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p> + +</body></html>
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.tests.dsf/build.properties b/dsf/org.eclipse.cdt.tests.dsf/build.properties new file mode 100644 index 00000000000..e398e4efb7b --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/build.properties @@ -0,0 +1,7 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + plugin.xml,\ + about.html,\ + plugin.properties diff --git a/dsf/org.eclipse.cdt.tests.dsf/plugin.properties b/dsf/org.eclipse.cdt.tests.dsf/plugin.properties new file mode 100644 index 00000000000..309e6e2f7d7 --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/plugin.properties @@ -0,0 +1,13 @@ +############################################################################### +# Copyright (c) 2006 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 +############################################################################### +pluginName=Debugger Services Framework Test Plugin +providerName=Eclipse.org + diff --git a/dsf/org.eclipse.cdt.tests.dsf/plugin.xml b/dsf/org.eclipse.cdt.tests.dsf/plugin.xml new file mode 100644 index 00000000000..ba839803701 --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/plugin.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<?eclipse version="3.2"?> +<plugin> + + <extension + point="org.eclipse.ui.views"> + <category + name="DSF Tests" + id="org.eclipse.cdt.tests.dsf.model"> + </category> + <view + name="Model Test View" + category="org.eclipse.cdt.tests.dsf.model" + class="org.eclipse.cdt.tests.dsf.model.ModelTestsView" + id="org.eclipse.cdt.tests.dsf.model.ModelTestView"> + </view> + </extension> +</plugin> diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/DsfTestPlugin.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/DsfTestPlugin.java new file mode 100644 index 00000000000..742f1dda5cf --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/DsfTestPlugin.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (c) 2006 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.tests.dsf; + +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +/** + * The activator class controls the plug-in life cycle + */ +public class DsfTestPlugin extends AbstractUIPlugin { + + // The plug-in ID + public static final String PLUGIN_ID = "org.eclipse.cdt.tests.dsf"; //$NON-NLS-1$ + + // The shared instance + private static DsfTestPlugin fgPlugin; + private static BundleContext fgBundleContext; + + /** + * The constructor + */ + public DsfTestPlugin() { + fgPlugin = this; + } + + /* + * (non-Javadoc) + * @see org.eclipse.core.runtime.Plugins#start(org.osgi.framework.BundleContext) + */ + @Override + public void start(BundleContext context) throws Exception { + fgBundleContext = context; + super.start(context); + } + + /* + * (non-Javadoc) + * @see org.eclipse.core.runtime.Plugin#stop(org.osgi.framework.BundleContext) + */ + @Override + public void stop(BundleContext context) throws Exception { + super.stop(context); + fgBundleContext = null; + fgPlugin = null; + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static DsfTestPlugin getDefault() { + return fgPlugin; + } + + public static BundleContext getBundleContext() { + return fgBundleContext; + } + +} diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/TestDsfExecutor.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/TestDsfExecutor.java new file mode 100644 index 00000000000..3abea7202ae --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/TestDsfExecutor.java @@ -0,0 +1,50 @@ +package org.eclipse.cdt.tests.dsf; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +import org.eclipse.cdt.dsf.concurrent.DefaultDsfExecutor; + +/** + * DsfExecutor for use with unit tests. It records the exceptions that were + * thrown in the executor thread so that they can be re-thrown by the test. + * + */ +public class TestDsfExecutor extends DefaultDsfExecutor { + private List<Throwable> fExceptions = Collections.synchronizedList(new ArrayList<Throwable>()); + + @Override + protected void afterExecute(Runnable r, Throwable t) { + super.afterExecute(r, t); + if (r instanceof Future<?>) { + Future<?> future = (Future<?>)r; + try { + if (future.isDone()) { + future.get(); + } + future.get(); + } catch (InterruptedException e) { // Ignore + } catch (CancellationException e) { // Ignore also + } catch (ExecutionException e) { + if (e.getCause() != null) { + fExceptions.add(e.getCause()); + } + } + } + } + + public boolean exceptionsCaught() { + return fExceptions.size() != 0; + } + + public Throwable[] getExceptions() { + synchronized (fExceptions) { + return fExceptions.toArray(new Throwable[fExceptions.size()]); + } + } + +} diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/ValueHolder.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/ValueHolder.java new file mode 100644 index 00000000000..fb3945e25fd --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/ValueHolder.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2006 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.tests.dsf; + +/** + * Utility class to hold a value retrieved in a runnable. + * Usage in a test is as follows: + * <pre> + * final ValueHolder<Integer> value = new ValueHolder<Integer>(); + * fExecutor.execute(new Runnable() { + * public void run() { + * value.fValue = 1; + * } + * }); + * Assert.assertTrue(value.fValue == 1); + * </pre> + */ +public class ValueHolder<V> { + public V fValue; +} diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/concurrent/DsfQueryTests.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/concurrent/DsfQueryTests.java new file mode 100644 index 00000000000..6c78e535019 --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/concurrent/DsfQueryTests.java @@ -0,0 +1,216 @@ +/******************************************************************************* + * Copyright (c) 2006 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.tests.dsf.concurrent; + +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import junit.framework.Assert; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.Query; +import org.eclipse.cdt.tests.dsf.DsfTestPlugin; +import org.eclipse.cdt.tests.dsf.TestDsfExecutor; +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.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * Tests that exercise the Query object. + */ +public class DsfQueryTests { + TestDsfExecutor fExecutor; + + @Before + public void startServices() throws ExecutionException, InterruptedException { + fExecutor = new TestDsfExecutor(); + } + + @After + public void shutdownServices() throws ExecutionException, InterruptedException { + fExecutor.submit(new DsfRunnable() { public void run() { + fExecutor.shutdown(); + }}).get(); + if (fExecutor.exceptionsCaught()) { + Throwable[] exceptions = fExecutor.getExceptions(); + throw new ExecutionException(exceptions[0]); + } + fExecutor = null; + } + + @Test + public void simpleGetTest() throws InterruptedException, ExecutionException { + Query<Integer> q = new Query<Integer>() { + @Override + protected void execute(DataRequestMonitor<Integer> rm) { + rm.setData(1); + rm.done(); + } + }; + // Check initial state + Assert.assertTrue(!q.isDone()); + Assert.assertTrue(!q.isCancelled()); + + fExecutor.execute(q); + Assert.assertEquals(1, (int)q.get()); + + // Check final state + Assert.assertTrue(q.isDone()); + Assert.assertTrue(!q.isCancelled()); + + } + + @Test + public void getWithMultipleDispatchesTest() throws InterruptedException, ExecutionException { + Query<Integer> q = new Query<Integer>() { + @Override + protected void execute(final DataRequestMonitor<Integer> rm) { + fExecutor.execute(new DsfRunnable() { + public void run() { + rm.setData(1); + rm.done(); + } + @Override + public String toString() { return super.toString() + "\n getWithMultipleDispatchesTest() second runnable"; } //$NON-NLS-1$ + }); + } + @Override + public String toString() { return super.toString() + "\n getWithMultipleDispatchesTest() first runnable (query)"; } //$NON-NLS-1$ + }; + fExecutor.execute(q); + Assert.assertEquals(1, (int)q.get()); + } + + @Test (expected = ExecutionException.class) + public void exceptionOnGetTest() throws InterruptedException, ExecutionException { + Query<Integer> q = new Query<Integer>() { + @Override + protected void execute(final DataRequestMonitor<Integer> rm) { + rm.setStatus(new Status(IStatus.ERROR, DsfTestPlugin.PLUGIN_ID, -1, "", null)); //$NON-NLS-1$ + rm.done(); + } + }; + + fExecutor.execute(q); + + try { + q.get(); + } finally { + Assert.assertTrue(q.isDone()); + Assert.assertTrue(!q.isCancelled()); + } + } + + @Test + public void cancelWhileWaitingTest() throws InterruptedException, ExecutionException { + final Query<Integer> q = new Query<Integer>() { + @Override + protected void execute(final DataRequestMonitor<Integer> rm) { + // Call done with a delay of 1 second, to avoid stalling the tests. + fExecutor.schedule( + new DsfRunnable() { + public void run() { rm.done(); } + }, + 1, TimeUnit.SECONDS); + } + }; + + fExecutor.execute(q); + + // Note: no point in checking isDone() and isCancelled() here, because + // the value could change on timing. + + // This does not really guarantee that the cancel will be called after + // the call to Fugure.get(), but the 1ms delay in call to schedule should + // help. + new Job("DsfQueryTests cancel job") { @Override public IStatus run(IProgressMonitor monitor) { //$NON-NLS-1$ + q.cancel(false); + return Status.OK_STATUS; + }}.schedule(1); + + try { + q.get(); + } catch (CancellationException e) { + return; // Success + } finally { + Assert.assertTrue(q.isDone()); + Assert.assertTrue(q.isCancelled()); + } + Assert.assertTrue("CancellationException should have been thrown", false); //$NON-NLS-1$ + } + + @Test + public void cancelBeforeWaitingTest() throws InterruptedException, ExecutionException { + final Query<Integer> q = new Query<Integer>() { + @Override protected void execute(final DataRequestMonitor<Integer> rm) { + Assert.fail("Query was cancelled, it should not be called."); //$NON-NLS-1$ + rm.done(); + } + }; + + // Cancel before invoking the query. + q.cancel(false); + + Assert.assertTrue(q.isDone()); + Assert.assertTrue(q.isCancelled()); + + // Start the query. + fExecutor.execute(q); + + // Block to retrieve data + try { + q.get(); + } catch (CancellationException e) { + return; // Success + } finally { + Assert.assertTrue(q.isDone()); + Assert.assertTrue(q.isCancelled()); + } + Assert.assertTrue("CancellationException should have been thrown", false); //$NON-NLS-1$ + } + + @Test + public void getTimeoutTest() throws InterruptedException, ExecutionException { + final Query<Integer> q = new Query<Integer>() { + @Override + protected void execute(final DataRequestMonitor<Integer> rm) { + // Call done with a delay of 1 second, to avoid stalling the tests. + fExecutor.schedule( + new DsfRunnable() { + public void run() { rm.done(); } + }, + 1, TimeUnit.SECONDS); + } + }; + + fExecutor.execute(q); + + // Note: no point in checking isDone() and isCancelled() here, because + // the value could change on timing. + + try { + q.get(1, TimeUnit.MILLISECONDS); + } catch (TimeoutException e) { + return; // Success + } finally { + Assert.assertFalse("Query should not be done yet, it should have timed out first.", q.isDone()); //$NON-NLS-1$ + } + Assert.assertTrue("TimeoutException should have been thrown", false); //$NON-NLS-1$ + } + +} diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/concurrent/DsfSequenceProgressTests.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/concurrent/DsfSequenceProgressTests.java new file mode 100644 index 00000000000..fb55ecf1985 --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/concurrent/DsfSequenceProgressTests.java @@ -0,0 +1,271 @@ +/******************************************************************************* + * Copyright (c) 2008 Nokia and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Nokia - initial implementation. Oct. 2008 + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.concurrent; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ExecutionException; + +import junit.framework.Assert; +import junit.framework.AssertionFailedError; + +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Sequence; +import org.eclipse.cdt.tests.dsf.TestDsfExecutor; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.swt.widgets.Display; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * Test whether a step in a sequence can control the progress monitor. + */ +public class DsfSequenceProgressTests { + private List<Throwable> fExceptions = Collections.synchronizedList(new ArrayList<Throwable>()); + TestDsfExecutor fExecutor; + + @Before + public void startExecutor() throws ExecutionException, InterruptedException { + fExecutor = new TestDsfExecutor(); + } + + @After + public void shutdownExecutor() throws ExecutionException, InterruptedException { + fExecutor.submit(new DsfRunnable() { public void run() { + fExecutor.shutdown(); + }}).get(); + if (fExecutor.exceptionsCaught()) { + Throwable[] exceptions = fExecutor.getExceptions(); + throw new ExecutionException(exceptions[0]); + } + fExecutor = null; + } + + // Create a counter for tracking number of steps performed and steps + // rolled back. + class IntegerHolder { int fInteger; } + final IntegerHolder stepCounter = new IntegerHolder(); + final IntegerHolder rollBackCounter = new IntegerHolder(); + + class SleepStep extends Sequence.Step { + + @Override + public int getTicks() { + return 6; + } + + @Override public void execute(RequestMonitor requestMonitor) { + stepCounter.fInteger++; + + sleep(getTicks(), requestMonitor, null); + + requestMonitor.done(); + } + + @Override public void rollBack(RequestMonitor requestMonitor) { + rollBackCounter.fInteger++; + + sleep(1, null, null); + + requestMonitor.done(); + } + + } + + class SleepStepWithProgress extends Sequence.StepWithProgress { + + @Override + public int getTicks() { + return 6; + } + + private final static int SUB_TICKS = 3; + + @Override + public void execute(RequestMonitor rm, IProgressMonitor pm) { + stepCounter.fInteger++; + + + // step has its own sub-progress ticks which take the total ticks + // of this step and divides them into subticks + pm.beginTask(getTaskName() + ": ", SUB_TICKS); + sleep(SUB_TICKS, rm, pm); + + rm.done(); + pm.done(); + } + + @Override + public void rollBack(RequestMonitor rm) { + rollBackCounter.fInteger++; + + sleep(2, null, null); + rm.done(); + } + + } + + @Test + /** + * It's better to run this as a manual interactive test. Run this as a JUnit + * plugin test.<br> + * <br> + * In the test workbench, watch the progress bar in the Progress View.<br> + * <br> + * During execution of a StepWithProgress, you should see the progress bar + * is growing and you can have more responsive cancel.<br> + * <br> + * Meanwhile, during execution of a step without progress, you should see + * that progress bar does not grow and cancel does not work until end of the + * step.<br> + * <br> + * Also watch that when you cancel the progress bar during the execution of + * the sequence, you should see that "Rollback.." appears in the progress bar + * label.<br> + */ + public void sequenceProgressTest() throws InterruptedException, ExecutionException { + + final Sequence.Step[] steps = new Sequence.Step[] { + + new SleepStepWithProgress() { + @Override + public String getTaskName() { + return "StepWithProgress #1"; + }}, + + new SleepStepWithProgress() { + @Override + public String getTaskName() { + return "StepWithProgress #2"; + }}, + + new SleepStep() { + @Override + public String getTaskName() { + return "Step #3"; + }}, + + new SleepStep() { + @Override + public String getTaskName() { + return "Step #4"; + }}, + }; + + + fExceptions.clear(); + + Job myJob = new Job("Run test sequence") { + + @Override + protected IStatus run(IProgressMonitor monitor) { + // Create and start. + Sequence sequence = new Sequence(fExecutor, monitor, "Run my sequence", "Rollback my sequence") { + @Override public Step[] getSteps() { return steps; } + }; + fExecutor.execute(sequence); + + // Block and wait for sequence to complete. + try { + sequence.get(); + } catch (InterruptedException e) { + // ignore here. + } catch (ExecutionException e) { + // Expected exception, ignore here. + } finally { + try { + System.out.println("StepCounter: " + stepCounter.fInteger); + System.out.println("RollBackCounter: " + rollBackCounter.fInteger); + + if (sequence.isCancelled()) + Assert.assertTrue( + "Wrong number of steps were rolled back after cancellation.", + stepCounter.fInteger == rollBackCounter.fInteger); + else { + Assert.assertTrue( + "Wrong number of steps executed.", + stepCounter.fInteger == steps.length); + Assert.assertTrue( + "Some steps are mistakenly rolled back", + rollBackCounter.fInteger == 0); + } + + // Check state from Future interface + Assert.assertTrue(sequence.isDone()); + } catch (AssertionFailedError e) { + fExceptions.add(e); + } + } + return null; + }}; + + myJob.schedule(); + + // Wait for the job to finish + waitForJob(myJob); + + // now throw any assertion errors. + if (fExceptions.size() > 0) + throw (AssertionFailedError)fExceptions.get(0); + + } + + private static void sleep(int seconds, RequestMonitor rm, IProgressMonitor pm) { + try { + for (int i = 0; i < seconds; i++) { + if (pm != null) + pm.subTask("subStep - " + (i+1)); + + Thread.sleep(1000); + + if (pm != null) { + pm.worked(1); + + if (pm.isCanceled()) { + return; + } + } + + if (rm != null && rm.isCanceled()) { + return; + } + } + + } catch (InterruptedException e) { + // ignore + } + } + + // Wait for a job to finish without possible blocking of UI thread. + // + private static void waitForJob(Job job) { + Display display = Display.getCurrent(); + while (true) { + IStatus status = job.getResult(); + if (status != null) + break; + if (display != null) { + while (display.readAndDispatch()) ; + } + try { + Thread.sleep(50); + } catch (InterruptedException e) { + job.cancel(); + break; + } + } + } +} diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/concurrent/DsfSequenceTests.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/concurrent/DsfSequenceTests.java new file mode 100644 index 00000000000..51e8569376a --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/concurrent/DsfSequenceTests.java @@ -0,0 +1,315 @@ +/******************************************************************************* + * Copyright (c) 2006 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.tests.dsf.concurrent; + +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import junit.framework.Assert; + +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Sequence; +import org.eclipse.cdt.tests.dsf.DsfTestPlugin; +import org.eclipse.cdt.tests.dsf.TestDsfExecutor; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Status; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * Tests that exercise the Sequence object. + */ +public class DsfSequenceTests { + TestDsfExecutor fExecutor; + + @Before + public void startExecutor() throws ExecutionException, InterruptedException { + fExecutor = new TestDsfExecutor(); + } + + @After + public void shutdownExecutor() throws ExecutionException, InterruptedException { + fExecutor.submit(new DsfRunnable() { public void run() { + fExecutor.shutdown(); + }}).get(); + if (fExecutor.exceptionsCaught()) { + Throwable[] exceptions = fExecutor.getExceptions(); + throw new ExecutionException(exceptions[0]); + } + fExecutor = null; + } + + @Test + public void simpleTest() throws InterruptedException, ExecutionException { + // Create a counter for tracking number of steps performed. + class IntegerHolder { int fInteger; } + final IntegerHolder stepCounter = new IntegerHolder(); + + // Create the steps of the sequence + final Sequence.Step[] steps = new Sequence.Step[] { + new Sequence.Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + stepCounter.fInteger++; + requestMonitor.done(); + } + }, + new Sequence.Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + stepCounter.fInteger++; + requestMonitor.done(); + } + } + }; + + // Create, start, and wait for the sequence. + Sequence sequence = new Sequence(fExecutor) { + @Override public Step[] getSteps() { return steps; } + }; + Assert.assertTrue(!sequence.isDone()); + Assert.assertTrue(!sequence.isCancelled()); + + fExecutor.execute(sequence); + sequence.get(); + + // Check the count + Assert.assertTrue(stepCounter.fInteger == 2); + + // Check post conditions + Assert.assertTrue(sequence.isDone()); + Assert.assertTrue(!sequence.isCancelled()); + } + + @Test (expected = ExecutionException.class) + public void rollbackTest() throws InterruptedException, ExecutionException { + // Create a counter for tracking number of steps performed and steps + // rolled back. + class IntegerHolder { int fInteger; } + final IntegerHolder stepCounter = new IntegerHolder(); + final IntegerHolder rollBackCounter = new IntegerHolder(); + + // Create the steps of the sequence + final Sequence.Step[] steps = new Sequence.Step[] { + new Sequence.Step() { + @Override public void execute(RequestMonitor requestMonitor) { + stepCounter.fInteger++; + requestMonitor.done(); + } + @Override public void rollBack(RequestMonitor requestMonitor) { + rollBackCounter.fInteger++; + requestMonitor.done(); + } + }, + new Sequence.Step() { + @Override public void execute(RequestMonitor requestMonitor) { + stepCounter.fInteger++; + requestMonitor.setStatus(new Status(IStatus.ERROR, DsfTestPlugin.PLUGIN_ID, -1, "", null)); //$NON-NLS-1$ + requestMonitor.done(); + } + @Override public void rollBack(RequestMonitor requestMonitor) { + rollBackCounter.fInteger++; + requestMonitor.done(); + } + } + }; + + // Create and start. + Sequence sequence = new Sequence(fExecutor) { + @Override public Step[] getSteps() { return steps; } + }; + fExecutor.execute(sequence); + + // Block and wait for sequence to bomplete. + try { + sequence.get(); + } finally { + // Both steps should be performed + Assert.assertTrue(stepCounter.fInteger == 2); + // Only one step is rolled back, the first one. + Assert.assertTrue(rollBackCounter.fInteger == 1); + + // Check state from Future interface + Assert.assertTrue(sequence.isDone()); + Assert.assertTrue(!sequence.isCancelled()); + } + Assert.assertTrue("Exception should have been thrown", false); //$NON-NLS-1$ + } + + /** + * The goal of this test it to check that if an exception is thrown within + * the Step.execute(), the step will return from the Future.get() method. + */ + @Test (expected = ExecutionException.class) + public void exceptionTest() throws InterruptedException, ExecutionException { + final Sequence.Step[] steps = new Sequence.Step[] { + new Sequence.Step() { + @Override public void execute(RequestMonitor requestMonitor) { + throw new Error("Exception part of unit test."); //$NON-NLS-1$ + } + } + }; + + // Create and start. + Sequence sequence = new Sequence(fExecutor) { + @Override public Step[] getSteps() { return steps; } + }; + fExecutor.execute(sequence); + + // Block and wait for sequence to bomplete. + try { + sequence.get(); + } finally { + // Check state from Future interface + Assert.assertTrue(sequence.isDone()); + Assert.assertTrue(!sequence.isCancelled()); + } + Assert.assertTrue("Exception should have been thrown", false); //$NON-NLS-1$ + } + + + @Test (expected = CancellationException.class) + public void cancelBeforeWaitingTest() throws InterruptedException, ExecutionException { + // Create the sequence + final Sequence.Step[] steps = new Sequence.Step[] { + new Sequence.Step() { + @Override public void execute(RequestMonitor requestMonitor) { + Assert.assertTrue("Sequence was cancelled, it should not be called.", false); //$NON-NLS-1$ + } + } + }; + Sequence sequence = new Sequence(fExecutor) { + @Override public Step[] getSteps() { return steps; } + }; + + // Cancel before invoking the sequence. + sequence.cancel(false); + + Assert.assertTrue(!sequence.isDone()); + Assert.assertTrue(sequence.isCancelled()); + + // Start the sequence + fExecutor.execute(sequence); + + // Block and wait for sequence to bomplete. + try { + sequence.get(); + } finally { + Assert.assertTrue(sequence.isDone()); + Assert.assertTrue(sequence.isCancelled()); + } + Assert.assertTrue("CancellationException should have been thrown", false); //$NON-NLS-1$ + } + + + @Test (expected = CancellationException.class) + public void cancelFromStepTest() throws InterruptedException, ExecutionException { + // Create a counter for tracking number of steps performed and steps + // rolled back. + class IntegerHolder { int fInteger; } + final IntegerHolder stepCounter = new IntegerHolder(); + final IntegerHolder rollBackCounter = new IntegerHolder(); + + // Create the steps of the sequence + final Sequence.Step[] steps = new Sequence.Step[] { + new Sequence.Step() { + @Override public void execute(RequestMonitor requestMonitor) { + stepCounter.fInteger++; + requestMonitor.done(); + } + @Override public void rollBack(RequestMonitor requestMonitor) { + rollBackCounter.fInteger++; + requestMonitor.done(); + } + }, + new Sequence.Step() { + @Override public void execute(RequestMonitor requestMonitor) { + stepCounter.fInteger++; + + // Perform the cancel! + getSequence().cancel(false); + + requestMonitor.done(); + } + @Override public void rollBack(RequestMonitor requestMonitor) { + rollBackCounter.fInteger++; + requestMonitor.done(); + } + } + }; + + // Create and start sequence with a delay. Delay so that we call get() before + // cancel is called. + final Sequence sequence = new Sequence(fExecutor) { + @Override public Step[] getSteps() { return steps; } + }; + fExecutor.schedule(sequence, 1, TimeUnit.MILLISECONDS); + + // Block to retrieve data + try { + sequence.get(); + } finally { + // Both steps should be performed + Assert.assertTrue(stepCounter.fInteger == 2); + // Both roll-backs should be performed since cancel does not take effect until + // after the step is completed. + Assert.assertTrue(rollBackCounter.fInteger == 2); + + Assert.assertTrue(sequence.isDone()); + Assert.assertTrue(sequence.isCancelled()); + } + Assert.assertTrue("CancellationException should have been thrown", false); //$NON-NLS-1$ + } + + @Test (expected = CancellationException.class) + public void cancelBeforeWithProgressManagerTest() throws InterruptedException, ExecutionException { + // Create the sequence + final Sequence.Step[] steps = new Sequence.Step[] { + new Sequence.Step() { + @Override public void execute(RequestMonitor requestMonitor) { + Assert.assertTrue("Sequence was cancelled, it should not be called.", false); //$NON-NLS-1$ + } + } + }; + + // Create the progress monitor that we will cancel. + IProgressMonitor pm = new NullProgressMonitor(); + + // Create the seqeunce with our steps. + Sequence sequence = new Sequence(fExecutor, pm, "", "") { //$NON-NLS-1$ //$NON-NLS-2$ + @Override public Step[] getSteps() { return steps; } + }; + + // Cancel the progress monitor before invoking the sequence. Note + // that the state of the sequence doesn't change yet, because the + // sequence does not check the progress monitor until it is executed. + pm.setCanceled(true); + + // Start the sequence + fExecutor.execute(sequence); + + // Block and wait for sequence to bomplete. Exception is thrown, + // which is expected. + try { + sequence.get(); + } finally { + Assert.assertTrue(sequence.isDone()); + Assert.assertTrue(sequence.isCancelled()); + } + } + + +}
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/dm/DMContextsTest.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/dm/DMContextsTest.java new file mode 100644 index 00000000000..86f33078888 --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/dm/DMContextsTest.java @@ -0,0 +1,185 @@ +/******************************************************************************* + * Copyright (c) 2007 Ericsson 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 - Initial Implementation + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.dm; + +import static org.junit.Assert.assertTrue; + +import java.util.concurrent.ExecutionException; + +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.datamodel.AbstractDMContext; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.tests.dsf.TestDsfExecutor; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + + + +public class DMContextsTest { + + TestDsfExecutor fExecutor; + DsfSession fSession; + + @Before public void startExecutor() throws ExecutionException, InterruptedException { + fExecutor = new TestDsfExecutor(); + fExecutor.submit(new DsfRunnable() { public void run() { + fSession = DsfSession.startSession(fExecutor, "DMContextsTest"); //$NON-NLS-1$ + }}).get(); + + // Build a hierarchy of contexts to run the tests. Note that this hierarchy + // is not valid in the DSF model, but that is ok for these tests. + // Let's build the following: + // + // SecondType4 + // | + // FirstType3 SecondType7 + // | | + // ThirdType2 ThirdType6 + // | / | + // FirstType1 ThirdType5 + // | / + // FirstType0 + c[7] = new SecondType(new IDMContext[0], 7); + c[6] = new ThirdType(new IDMContext[]{c[7]}, 6); + c[5] = new ThirdType(new IDMContext[]{c[6]}, 5); + c[4] = new SecondType(new IDMContext[0], 4); + c[3] = new FirstType(new IDMContext[]{c[4]}, 3); + c[2] = new ThirdType(new IDMContext[]{c[3]}, 2); + c[1] = new FirstType(new IDMContext[]{c[2],c[6]}, 1); + c[0] = new FirstType(new IDMContext[]{c[1],c[5]}, 0); + } + + @After public void shutdownExecutor() throws ExecutionException, InterruptedException { + DsfSession.endSession(fSession); + fSession = null; + + fExecutor.submit(new DsfRunnable() { public void run() { + fExecutor.shutdown(); + }}).get(); + if (fExecutor.exceptionsCaught()) { + Throwable[] exceptions = fExecutor.getExceptions(); + throw new ExecutionException(exceptions[0]); + } + fExecutor = null; + } + + + BaseContextType c[] = new BaseContextType[8]; + + + private class BaseContextType extends AbstractDMContext { + final int fId; + + public BaseContextType(IDMContext[] parents, int id) { + super(fSession.getId(), parents); + fId = id; + } + + @Override + public String toString() { return baseToString() + ".[" + fId + "]"; } //$NON-NLS-1$ //$NON-NLS-2$ + + @Override + public boolean equals(Object obj) { + return super.baseEquals(obj) && ((BaseContextType)obj).fId == fId; + } + + @Override + public int hashCode() { return super.baseHashCode() ^ fId; } + } + + private class FirstType extends BaseContextType { + public FirstType(IDMContext[] parents, int id) { + super(parents, id); + } + } + + private class SecondType extends BaseContextType { + public SecondType(IDMContext[] parents, int id) { + super(parents, id); + } + } + + private class ThirdType extends BaseContextType { + public ThirdType(IDMContext[] parents, int id) { + super(parents, id); + } + } + + private interface UnknownType extends IDMContext {} + + /** + * Test that we get the closest ancestor in terms of depth. + */ + @Test + public void testClosestAncestor() throws Throwable { + BaseContextType ancestor = DMContexts.getAncestorOfType(c[0], FirstType.class); + assertTrue("Got unexpected null ancestor", ancestor != null); + assertTrue("Got ancestor " + ancestor.fId + " intead of 1", ancestor.fId == 0); + + ancestor = DMContexts.getAncestorOfType(c[0], SecondType.class); + assertTrue("Got unexpected null ancestor", ancestor != null); + assertTrue("Got ancestor " + ancestor.fId + " intead of 8", ancestor.fId == 7); + + ancestor = DMContexts.getAncestorOfType(c[0], ThirdType.class); + assertTrue("Got unexpected null ancestor", ancestor != null); + assertTrue("Got ancestor " + ancestor.fId + " intead of 6", ancestor.fId == 5); + + ancestor = DMContexts.getAncestorOfType(c[1], SecondType.class); + assertTrue("Got unexpected null ancestor", ancestor != null); + assertTrue("Got ancestor " + ancestor.fId + " intead of 8", ancestor.fId == 7); + + ancestor = DMContexts.getAncestorOfType(c[1], ThirdType.class); + assertTrue("Got unexpected null ancestor", ancestor != null); + assertTrue("Got ancestor " + ancestor.fId + " intead of 3", ancestor.fId == 2); + + ancestor = DMContexts.getAncestorOfType(c[5], FirstType.class); + assertTrue("Got unexpected non-null ancestor", ancestor == null); + } + + /** + * Test that we get all the ancestors in order of closest in terms of depth. + */ + @Test + public void testAllClosestAncestors() throws Throwable { + + checkAncestors(c[0], BaseContextType.class, new int[]{0,1,5,2,6,3,7,4}); + checkAncestors(c[0], FirstType.class, new int[]{0,1,3}); + checkAncestors(c[0], SecondType.class, new int[]{7,4}); + checkAncestors(c[0], ThirdType.class, new int[]{5,2,6}); + + UnknownType[] exprAncestors = DMContexts.getAllAncestorsOfType(c[0], UnknownType.class); + assertTrue("Got unexpected non-null ancestor list", exprAncestors == null); + } + + private <V extends IDMContext> void checkAncestors(BaseContextType ctx, Class<V> type, int[] expected) { + BaseContextType[] ancestors = (BaseContextType[])DMContexts.getAllAncestorsOfType(ctx, type); + assertTrue("Got unexpected null ancestor", ancestors != null); + + String ancestorsStr = "", expectedStr = ""; + for (int k=0;k<ancestors.length;k++) { + ancestorsStr += ancestors[k].fId + ","; + } + for (int j=0;j<expected.length;j++) { + expectedStr += expected[j] + ","; + } + + assertTrue("Got " + ancestorsStr + " instead of " + expectedStr, ancestors.length == expected.length); + for (int i=0;i<expected.length;i++) { + if (ancestors[i].fId != expected[i]) { + assertTrue("Got " + ancestorsStr + " instead of " + expectedStr, false); + } + } + } + +} diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/events/AbstractService.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/events/AbstractService.java new file mode 100644 index 00000000000..2aa8af6699b --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/events/AbstractService.java @@ -0,0 +1,110 @@ +/******************************************************************************* + * Copyright (c) 2006 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.tests.dsf.events; + +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.tests.dsf.DsfTestPlugin; +import org.osgi.framework.BundleContext; + +/** + * Test service class used to test event behavior. It has three types of events + * and three methods to receive the events. + * + */ +abstract public class AbstractService extends AbstractDsfService +{ + AbstractService(DsfSession session) { + super(session); + } + + @Override protected BundleContext getBundleContext() { + return DsfTestPlugin.getBundleContext(); + } + + @Override public void initialize(final RequestMonitor requestMonitor) { + super.initialize( + new RequestMonitor(getExecutor(), requestMonitor) { + @Override + public void handleSuccess() { + doInitialize(requestMonitor); + } + }); + } + + private void doInitialize(RequestMonitor requestMonitor) { + getSession().addServiceEventListener(this, null); + requestMonitor.done(); + } + + @Override public void shutdown(RequestMonitor requestMonitor) { + getSession().removeServiceEventListener(this); + super.shutdown(requestMonitor); + } + + /////////////////////////////////////////////////////////////////////////// + // Test API + /** Records the number in the event 1 object when this service received the event. */ + int fEvent1RecipientNumber; + + /** Records the number in the event 2 object when this service received the event. */ + int fEvent2RecipientNumber; + + /** Records the number in the event 3 object when this service received the event. */ + int fEvent3RecipientNumber; + + /** Simple event class 1 */ + public class Event1 { + // 1-based counter for the recipient of the event. + int fRecipientNumberCounter = 1; + } + + /** Simple event class 2. Note it doesn't have any relation to event 1 */ + public class Event2 { + int fRecipientNumberCounter = 1; + } + + /** Simple event class 3. Note it does sub-class event 1 */ + public class Event3 extends Event1 {} + + @ThreadSafe + public void dispatchEvent1() { + getSession().dispatchEvent(new Event1(), getProperties()); + } + + @ThreadSafe + public void dispatchEvent2() { + getSession().dispatchEvent(new Event2(), getProperties()); + } + + @ThreadSafe + public void dispatchEvent3() { + getSession().dispatchEvent(new Event3(), getProperties()); + } + + /** Handles event 1 (and event 3 which derives from event 1) */ + @DsfServiceEventHandler public void eventDispatched(Event1 e) { + fEvent1RecipientNumber = e.fRecipientNumberCounter++; + } + + /** Handles event 2 only */ + @DsfServiceEventHandler public void eventDispatched(Event2 e) { + fEvent2RecipientNumber = e.fRecipientNumberCounter++; + } + + /** Handles event 3 only */ + @DsfServiceEventHandler public void eventDispatched(Event3 e) { + fEvent3RecipientNumber = e.fRecipientNumberCounter++; + } +} diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/events/Event1.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/events/Event1.java new file mode 100644 index 00000000000..9ac2e055b40 --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/events/Event1.java @@ -0,0 +1,16 @@ +/******************************************************************************* + * Copyright (c) 2006 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.tests.dsf.events; + + +public class Event1 { + +} diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/events/Event2.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/events/Event2.java new file mode 100644 index 00000000000..1cc5cb11afa --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/events/Event2.java @@ -0,0 +1,15 @@ +/******************************************************************************* + * Copyright (c) 2006 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.tests.dsf.events; + +public class Event2 extends Event1 { + +} diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/events/EventTest.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/events/EventTest.java new file mode 100644 index 00000000000..2f4963c17a4 --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/events/EventTest.java @@ -0,0 +1,147 @@ +/******************************************************************************* + * Copyright (c) 2006 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.tests.dsf.events; + +import java.util.concurrent.ExecutionException; + +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.tests.dsf.DsfTestPlugin; +import org.eclipse.cdt.tests.dsf.TestDsfExecutor; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class EventTest { + + DsfSession fSession; + TestDsfExecutor fExecutor; + DsfServicesTracker fTracker; + Service1 fService1; + Service2 fService2; + Service3 fService3; + + @Before public void startServices() throws ExecutionException, InterruptedException { + fExecutor = new TestDsfExecutor(); + + fExecutor.submit(new DsfRunnable() { public void run() { + fSession = DsfSession.startSession(fExecutor, "org.eclipse.cdt.tests.dsf"); //$NON-NLS-1$ + }}).get(); + + StartupSequence startupSeq = new StartupSequence(fSession); + fExecutor.execute(startupSeq); + startupSeq.get(); + + fExecutor.submit(new DsfRunnable() { public void run() { + fTracker = new DsfServicesTracker(DsfTestPlugin.getBundleContext(), fSession.getId()); + fService1 = fTracker.getService(Service1.class); + fService2 = fTracker.getService(Service2.class); + fService3 = fTracker.getService(Service3.class); + }}).get(); + Assert.assertNotNull(fService1); + Assert.assertNotNull(fService2); + Assert.assertNotNull(fService3); + } + + @After public void shutdownServices() throws ExecutionException, InterruptedException { + ShutdownSequence shutdownSeq = new ShutdownSequence(fSession); + fExecutor.execute(shutdownSeq); + shutdownSeq.get(); + + fExecutor.submit(new DsfRunnable() { public void run() { + fService1 = null; + fService2 = null; + fService3 = null; + fTracker.dispose(); + fTracker = null; + DsfSession.endSession(fSession); + fSession = null; + fExecutor.shutdown(); + }}).get(); + + if (fExecutor.exceptionsCaught()) { + Throwable[] exceptions = fExecutor.getExceptions(); + throw new ExecutionException(exceptions[0]); + } + fExecutor = null; + } + + /** + * Test only the startup and shutdown sequences. + */ + @Test public void startStopTest() { + } + + /** + * Tests dispatching event 1. The goal of the test is to make sure that + * recipients are called in the correct order. + */ + @Test public void event1Test() throws ExecutionException, InterruptedException { + fService1.dispatchEvent1(); + fExecutor.submit(new DsfRunnable() { public void run() { + Assert.assertTrue(1 == fService1.fEvent1RecipientNumber); + Assert.assertTrue(2 == fService2.fEvent1RecipientNumber); + Assert.assertTrue(3 == fService3.fEvent1RecipientNumber); + Assert.assertTrue(0 == fService1.fEvent2RecipientNumber); + Assert.assertTrue(0 == fService2.fEvent2RecipientNumber); + Assert.assertTrue(0 == fService3.fEvent2RecipientNumber); + Assert.assertTrue(0 == fService1.fEvent3RecipientNumber); + Assert.assertTrue(0 == fService2.fEvent3RecipientNumber); + Assert.assertTrue(0 == fService3.fEvent3RecipientNumber); + }}).get(); + } + + /** + * Tests dispatching event 2. The goal of the test is to make sure that + * recipients are called in the correct order, and that the other events + * are not registered. + */ + @Test public void event2Test() throws ExecutionException, InterruptedException { + fService1.dispatchEvent2(); + fExecutor.submit(new DsfRunnable() { public void run() { + Assert.assertTrue(0 == fService1.fEvent1RecipientNumber); + Assert.assertTrue(0 == fService2.fEvent1RecipientNumber); + Assert.assertTrue(0 == fService3.fEvent1RecipientNumber); + Assert.assertTrue(1 == fService1.fEvent2RecipientNumber); + Assert.assertTrue(2 == fService2.fEvent2RecipientNumber); + Assert.assertTrue(3 == fService3.fEvent2RecipientNumber); + Assert.assertTrue(0 == fService1.fEvent3RecipientNumber); + Assert.assertTrue(0 == fService2.fEvent3RecipientNumber); + Assert.assertTrue(0 == fService3.fEvent3RecipientNumber); + }}).get(); + } + + /** + * Tests dispatching event 2. The goal of the test is to make sure that + * both event 2 and even 3 recipients are called for this event. + * <br> + * Note: When a single listener object has more than one method that that + * matches the event, both methods will be called. But there is currently + * no guaranteed order in which they should be called. + */ + @Test public void event3Test() throws ExecutionException, InterruptedException { + fService1.dispatchEvent3(); + fExecutor.submit(new DsfRunnable() { public void run() { + Assert.assertTrue(1 == fService1.fEvent1RecipientNumber || 2 == fService1.fEvent1RecipientNumber); + Assert.assertTrue(3 == fService2.fEvent1RecipientNumber || 4 == fService2.fEvent1RecipientNumber); + Assert.assertTrue(5 == fService3.fEvent1RecipientNumber || 6 == fService3.fEvent1RecipientNumber); + Assert.assertTrue(0 == fService1.fEvent2RecipientNumber); + Assert.assertTrue(0 == fService2.fEvent2RecipientNumber); + Assert.assertTrue(0 == fService3.fEvent2RecipientNumber); + Assert.assertTrue(1 == fService1.fEvent3RecipientNumber || 2 == fService1.fEvent3RecipientNumber); + Assert.assertTrue(3 == fService2.fEvent3RecipientNumber || 4 == fService2.fEvent3RecipientNumber); + Assert.assertTrue(5 == fService3.fEvent3RecipientNumber || 6 == fService3.fEvent3RecipientNumber); + }}).get(); + } + +} diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/events/Service1.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/events/Service1.java new file mode 100644 index 00000000000..89ab8c771b8 --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/events/Service1.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2006 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.tests.dsf.events; + +import java.util.Hashtable; + +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.service.DsfSession; + +public class Service1 extends AbstractService { + Service1(DsfSession session) { + super(session); + } + + @Override public void initialize(final RequestMonitor requestMonitor) { + super.initialize( + new RequestMonitor(getExecutor(), requestMonitor) { + @Override + public void handleSuccess() { + doInitialize(requestMonitor); + } + }); + } + + private void doInitialize(RequestMonitor requestMonitor) { + register(new String[]{Service1.class.getName()}, new Hashtable<String,String>()); + requestMonitor.done(); + } + + @Override public void shutdown(RequestMonitor requestMonitor) { + unregister(); + super.shutdown(requestMonitor); + } +} diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/events/Service2.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/events/Service2.java new file mode 100644 index 00000000000..bb70dc51186 --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/events/Service2.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2006 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.tests.dsf.events; + +import java.util.Hashtable; + +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.service.DsfSession; + +public class Service2 extends AbstractService { + Service2(DsfSession session) { + super(session); + } + + @Override public void initialize(final RequestMonitor requestMonitor) { + super.initialize( + new RequestMonitor(getExecutor(), requestMonitor) { + @Override + public void handleSuccess() { + doInitialize(requestMonitor); + } + }); + } + + private void doInitialize(RequestMonitor requestMonitor) { + getServicesTracker().getService(Service1.class); + register(new String[]{Service2.class.getName()}, new Hashtable<String,String>()); + requestMonitor.done(); + } + + @Override public void shutdown(RequestMonitor requestMonitor) { + unregister(); + super.shutdown(requestMonitor); + } +} diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/events/Service3.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/events/Service3.java new file mode 100644 index 00000000000..c16057e288c --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/events/Service3.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2006 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.tests.dsf.events; + +import java.util.Hashtable; + +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.tests.dsf.DsfTestPlugin; +import org.osgi.framework.BundleContext; + +public class Service3 extends AbstractService { + Service3(DsfSession session) { + super(session); + } + + @Override protected BundleContext getBundleContext() { + return DsfTestPlugin.getBundleContext(); + } + + @Override public void initialize(final RequestMonitor requestMonitor) { + super.initialize( + new RequestMonitor(getExecutor(), requestMonitor) { + @Override + public void handleSuccess() { + doInitialize(requestMonitor); + } + }); + } + + private void doInitialize(RequestMonitor requestMonitor) { + getServicesTracker().getService(Service1.class); + getServicesTracker().getService(Service2.class); + register(new String[]{Service3.class.getName()}, new Hashtable<String,String>()); + requestMonitor.done(); + } + + @Override public void shutdown(RequestMonitor requestMonitor) { + unregister(); + super.shutdown(requestMonitor); + } +} diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/events/ShutdownSequence.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/events/ShutdownSequence.java new file mode 100644 index 00000000000..06f05dc329d --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/events/ShutdownSequence.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2006 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.tests.dsf.events; + +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Sequence; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.service.IDsfService; +import org.eclipse.cdt.tests.dsf.DsfTestPlugin; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +class ShutdownSequence extends Sequence { + + DsfSession fSession; + DsfServicesTracker fTracker; + + ShutdownSequence(DsfSession session) { + super(session.getExecutor()); + fSession = session; + } + + @Override + public Step[] getSteps() { return fSteps; } + + final Step[] fSteps = new Step[] { + new Step() { + @Override public void execute(RequestMonitor requestMonitor) { + fTracker = new DsfServicesTracker(DsfTestPlugin.getBundleContext(), fSession.getId()); + requestMonitor.done(); + } + + @Override public void rollBack(RequestMonitor requestMonitor) { + fTracker.dispose(); + fTracker = null; + requestMonitor.done(); + } + }, + new Step() { @Override public void execute(RequestMonitor requestMonitor) { + shutdownService(Service3.class, requestMonitor); + }}, + new Step() { @Override public void execute(RequestMonitor requestMonitor) { + shutdownService(Service2.class, requestMonitor); + }}, + new Step() { @Override public void execute(RequestMonitor requestMonitor) { + shutdownService(Service1.class, requestMonitor); + }}, + new Step() { @Override public void execute(RequestMonitor requestMonitor) { + fTracker.dispose(); + fTracker = null; + requestMonitor.done(); + }} + }; + + private void shutdownService(Class<? extends IDsfService> clazz, RequestMonitor requestMonitor) { + IDsfService service = fTracker.getService(clazz); + if (service != null) { + service.shutdown(requestMonitor); + } + else { + requestMonitor.setStatus(new Status(IStatus.ERROR, DsfTestPlugin.PLUGIN_ID, -1, "Service '" + clazz.getName() + "' not found.", null)); //$NON-NLS-1$//$NON-NLS-2$ + requestMonitor.done(); + } + } + +} diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/events/StartupSequence.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/events/StartupSequence.java new file mode 100644 index 00000000000..408755c448b --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/events/StartupSequence.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2006 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.tests.dsf.events; + +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Sequence; +import org.eclipse.cdt.dsf.service.DsfSession; + +class StartupSequence extends Sequence { + DsfSession fSession; + + StartupSequence(DsfSession session) { + super(session.getExecutor()); + fSession = session; + } + + @Override + public Step[] getSteps() { return fSteps; } + + final Step[] fSteps = new Step[] { + new Step() { @Override public void execute(RequestMonitor requestMonitor) { + new Service1(fSession).initialize(requestMonitor); + }}, + new Step() { @Override public void execute(RequestMonitor requestMonitor) { + new Service2(fSession).initialize(requestMonitor); + }}, + new Step() { @Override public void execute(RequestMonitor requestMonitor) { + new Service3(fSession).initialize(requestMonitor); + }} + }; +} diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/service/MultiInstanceTestService.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/service/MultiInstanceTestService.java new file mode 100644 index 00000000000..283aae2e8ee --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/service/MultiInstanceTestService.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2006 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.tests.dsf.service; + +import java.util.Hashtable; + +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.tests.dsf.DsfTestPlugin; +import org.osgi.framework.BundleContext; + +public class MultiInstanceTestService extends AbstractDsfService { + + public static String PROP_INSTANCE_ID = "org.eclipse.cdt.dsf.tests.service.MultiInstanceTestService.id"; //$NON-NLS-1$ + String fInstanceId; + + public MultiInstanceTestService(DsfSession session, String instanceId) { + super(session); + fInstanceId = instanceId; + } + + @Override + protected BundleContext getBundleContext() { + return DsfTestPlugin.getBundleContext(); + } + + @Override + public void initialize(final RequestMonitor requestMonitor) { + super.initialize( + new RequestMonitor(getExecutor(), requestMonitor) { + @Override + public void handleSuccess() { + doInitialize(requestMonitor); + } + }); + } + + private void doInitialize(RequestMonitor requestMonitor) { + Hashtable<String,String> properties = new Hashtable<String,String>(); + properties.put(PROP_INSTANCE_ID, fInstanceId); + register(new String[]{MultiInstanceTestService.class.getName()}, properties); + requestMonitor.done(); + } + + @Override + public void shutdown(RequestMonitor requestMonitor) { + unregister(); + super.shutdown(requestMonitor); + } +} diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/service/ServiceTests.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/service/ServiceTests.java new file mode 100644 index 00000000000..9f6b830908b --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/service/ServiceTests.java @@ -0,0 +1,277 @@ +/******************************************************************************* + * Copyright (c) 2006 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.tests.dsf.service; + +import java.lang.reflect.Constructor; +import java.util.concurrent.ExecutionException; + +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Sequence; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.service.IDsfService; +import org.eclipse.cdt.tests.dsf.DsfTestPlugin; +import org.eclipse.cdt.tests.dsf.TestDsfExecutor; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; + +public class ServiceTests { + TestDsfExecutor fExecutor; + + @Before public void startExecutor() throws ExecutionException, InterruptedException { + fExecutor = new TestDsfExecutor(); + } + + @After public void shutdownExecutor() throws ExecutionException, InterruptedException { + fExecutor.submit(new DsfRunnable() { public void run() { + fExecutor.shutdown(); + }}).get(); + if (fExecutor.exceptionsCaught()) { + Throwable[] exceptions = fExecutor.getExceptions(); + throw new ExecutionException(exceptions[0]); + } + fExecutor = null; + } + + private class CreateSessionStep extends Sequence.Step { + private DsfSession fSession; + @Override public void execute(RequestMonitor requestMonitor) { + fSession = DsfSession.startSession(fExecutor, "org.eclipse.cdt.dsf.tests"); //$NON-NLS-1$ + requestMonitor.done(); + } + + DsfSession getSession() { return fSession; } + } + + private class ShutdownSessionStep extends Sequence.Step { + private CreateSessionStep fCreateSessionStep; + + ShutdownSessionStep(CreateSessionStep createSessionStep) { + fCreateSessionStep = createSessionStep; + } + + @Override + public void execute(RequestMonitor requestMonitor) { + DsfSession.endSession(fCreateSessionStep.getSession()); + requestMonitor.done(); + } + } + + private class InitializeServiceStep extends Sequence.Step { + CreateSessionStep fCreateSessionStep; + Class<? extends IDsfService> fServiceClass; + IDsfService fService; + + InitializeServiceStep(CreateSessionStep createSessionStep, Class<? extends IDsfService> serviceClass) { + fCreateSessionStep = createSessionStep; + fServiceClass = serviceClass; + } + IDsfService getService() { return fService; } + + @Override + public void execute(RequestMonitor requestMonitor) { + try { + Constructor<? extends IDsfService> c = fServiceClass.getConstructor(new Class[] {DsfSession.class}); + fService = c.newInstance(new Object[] {fCreateSessionStep.getSession()}); + } catch (Exception e) { + Assert.fail("Unexpected exception"); //$NON-NLS-1$ + } + fService.initialize(requestMonitor); + } + } + + private class InitializeMultiInstanceServiceStep extends InitializeServiceStep { + String fServiceId; + + InitializeMultiInstanceServiceStep(CreateSessionStep createSessionStep, Class<? extends IDsfService> serviceClass, String serviceId) { + super(createSessionStep, serviceClass); + fServiceId = serviceId; + } + @Override + IDsfService getService() { return fService; } + + @Override + public void execute(RequestMonitor requestMonitor) { + try { + Constructor<? extends IDsfService> c = + fServiceClass.getConstructor(new Class[] {DsfSession.class, String.class}); + fService = c.newInstance(new Object[] {fCreateSessionStep.getSession(), fServiceId}); + } catch (Exception e) { + Assert.fail("Unexpected exception"); //$NON-NLS-1$ + } + fService.initialize(requestMonitor); + } + } + + private class ShutdownServiceStep extends Sequence.Step { + InitializeServiceStep fInitializeServiceStep; + ShutdownServiceStep(InitializeServiceStep initStep) { + fInitializeServiceStep = initStep; + } + + @Override + public void execute(RequestMonitor requestMonitor) { + fInitializeServiceStep.getService().shutdown(requestMonitor); + } + } + + + abstract private class TestRetrievingReferenceStep extends Sequence.Step { + String fClass; + boolean fShouldSucceed; + + TestRetrievingReferenceStep(Class<?> clazz, boolean shouldSucceed) { + fClass = clazz.getName(); + fShouldSucceed = shouldSucceed; + } + + abstract String getFilter(); + + @Override + public void execute(RequestMonitor requestMonitor) { + ServiceReference[] refs = null; + try { + refs = DsfTestPlugin.getBundleContext().getServiceReferences(fClass, getFilter()); + } catch (InvalidSyntaxException e) { + Assert.fail("Unexpected exception"); //$NON-NLS-1$ + } + if (fShouldSucceed) { + Assert.assertTrue(refs != null); + Assert.assertTrue(refs.length == 1); + IDsfService service = (IDsfService)DsfTestPlugin.getBundleContext().getService(refs[0]); + Assert.assertTrue(service != null); + DsfTestPlugin.getBundleContext().ungetService(refs[0]); + } else { + Assert.assertTrue(refs == null); + } + requestMonitor.done(); + } + } + + private class TestRetrievingSimpleServiceReferenceStep extends TestRetrievingReferenceStep { + CreateSessionStep fCreateSessionStep; + TestRetrievingSimpleServiceReferenceStep(Class<?> clazz, boolean shouldSucceed, CreateSessionStep createSessionStep) { + super(clazz, shouldSucceed); + fCreateSessionStep = createSessionStep; + } + @Override + String getFilter() { + return "(" + IDsfService.PROP_SESSION_ID + "=" + fCreateSessionStep.getSession().getId() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + } + + private class TestRetrievingMultiSessionServiceReferenceStep extends TestRetrievingSimpleServiceReferenceStep { + String fServiceId; + TestRetrievingMultiSessionServiceReferenceStep(Class<?> clazz, boolean shouldSucceed, CreateSessionStep createSessionStep, + String serviceId) { + super(clazz, shouldSucceed, createSessionStep); + fServiceId = serviceId; + } + @Override + String getFilter() { + return "(&" + //$NON-NLS-1$ + "(" + IDsfService.PROP_SESSION_ID + "=" + fCreateSessionStep.getSession().getId() + ")" + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + "(" + MultiInstanceTestService.PROP_INSTANCE_ID + "=" + fServiceId + ")" + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + ")"; //$NON-NLS-1$ + } + } + + @Test + public void singleServiceTest() throws InterruptedException, ExecutionException { + Sequence seq = new Sequence(fExecutor) { + CreateSessionStep fSessionStep; + InitializeServiceStep fServiceStep; + + @Override + public Step[] getSteps() { return fSteps; } + + final private Step[] fSteps = new Step[] + { + fSessionStep = new CreateSessionStep(), + fServiceStep = new InitializeServiceStep(fSessionStep, SimpleTestService.class), + new TestRetrievingSimpleServiceReferenceStep(SimpleTestService.class, true, fSessionStep), + new ShutdownServiceStep(fServiceStep), + new TestRetrievingSimpleServiceReferenceStep(SimpleTestService.class, false, fSessionStep), + new ShutdownSessionStep(fSessionStep) + }; + }; + fExecutor.execute(seq); + seq.get(); + } + + /** + * Creates two sessions and starts a single service within each session. + * Then it tests retrieving the reference to the service. + */ + @Test + public void singleServiceMultiSessionTest() throws InterruptedException, ExecutionException { + Sequence seq = new Sequence(fExecutor) { + CreateSessionStep fSession1Step; + CreateSessionStep fSession2Step; + InitializeServiceStep fSession1ServiceStep; + InitializeServiceStep fSession2ServiceStep; + + @Override + public Step[] getSteps() { return fSteps; } + + final private Step[] fSteps = new Step[] + { + fSession1Step = new CreateSessionStep(), + fSession2Step = new CreateSessionStep(), + fSession1ServiceStep = new InitializeServiceStep(fSession1Step, SimpleTestService.class), + fSession2ServiceStep = new InitializeServiceStep(fSession2Step, SimpleTestService.class), + new TestRetrievingSimpleServiceReferenceStep(SimpleTestService.class, true, fSession1Step), + new TestRetrievingSimpleServiceReferenceStep(SimpleTestService.class, true, fSession2Step), + new ShutdownServiceStep(fSession1ServiceStep), + new ShutdownServiceStep(fSession2ServiceStep), + new TestRetrievingSimpleServiceReferenceStep(SimpleTestService.class, false, fSession1Step), + new TestRetrievingSimpleServiceReferenceStep(SimpleTestService.class, false, fSession2Step), + new ShutdownSessionStep(fSession1Step), + new ShutdownSessionStep(fSession2Step) + }; + }; + fExecutor.execute(seq); + seq.get(); + } + + @Test + public void multiServiceServiceTest() throws InterruptedException, ExecutionException { + Sequence seq = new Sequence(fExecutor) { + CreateSessionStep fSessionStep; + InitializeServiceStep fService1Step; + InitializeServiceStep fService2Step; + + @Override + public Step[] getSteps() { return fSteps; } + + final private Step[] fSteps = new Step[] + { + fSessionStep = new CreateSessionStep(), + fService1Step = new InitializeMultiInstanceServiceStep(fSessionStep, MultiInstanceTestService.class, "1"), //$NON-NLS-1$ + fService2Step = new InitializeMultiInstanceServiceStep(fSessionStep, MultiInstanceTestService.class, "2"), //$NON-NLS-1$ + new TestRetrievingMultiSessionServiceReferenceStep(MultiInstanceTestService.class, true, fSessionStep, "1"), //$NON-NLS-1$ + new TestRetrievingMultiSessionServiceReferenceStep(MultiInstanceTestService.class, true, fSessionStep, "2"), //$NON-NLS-1$ + new ShutdownServiceStep(fService1Step), + new ShutdownServiceStep(fService2Step), + new TestRetrievingMultiSessionServiceReferenceStep(MultiInstanceTestService.class, false, fSessionStep, "1"), //$NON-NLS-1$ + new TestRetrievingMultiSessionServiceReferenceStep(MultiInstanceTestService.class, false, fSessionStep, "2"), //$NON-NLS-1$ + new ShutdownSessionStep(fSessionStep) + }; + }; + fExecutor.execute(seq); + seq.get(); + } + +} diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/service/SimpleTestService.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/service/SimpleTestService.java new file mode 100644 index 00000000000..83737c13967 --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/service/SimpleTestService.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2006 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.tests.dsf.service; + +import java.util.Hashtable; + +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.tests.dsf.DsfTestPlugin; +import org.osgi.framework.BundleContext; + +public class SimpleTestService extends AbstractDsfService { + + public SimpleTestService(DsfSession session) { + super(session); + } + + @Override + protected BundleContext getBundleContext() { + return DsfTestPlugin.getBundleContext(); + } + + @Override + public void initialize(final RequestMonitor requestMonitor) { + super.initialize( + new RequestMonitor(getExecutor(), requestMonitor) { + @Override + public void handleSuccess() { + doInitialize(requestMonitor); + } + }); + } + + private void doInitialize(RequestMonitor requestMonitor) { + register(new String[]{SimpleTestService.class.getName()}, new Hashtable<String,String>()); + requestMonitor.done(); + } + + @Override + public void shutdown(RequestMonitor requestMonitor) { + unregister(); + super.shutdown(requestMonitor); + } +} |