diff options
Diffstat (limited to 'debug/org.eclipse.cdt.debug.core/src')
9 files changed, 555 insertions, 45 deletions
diff --git a/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/CDebugAdapter.java b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/CDebugAdapter.java index fb6ad47484d..e80c1b20422 100644 --- a/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/CDebugAdapter.java +++ b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/CDebugAdapter.java @@ -10,8 +10,6 @@ *******************************************************************************/ package org.eclipse.cdt.debug.internal.core; -import com.ibm.icu.text.DateFormat; -import com.ibm.icu.text.MessageFormat; import java.util.Date; import org.eclipse.cdt.core.CCorePlugin; @@ -39,6 +37,9 @@ import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.model.IProcess; +import com.ibm.icu.text.DateFormat; +import com.ibm.icu.text.MessageFormat; + public class CDebugAdapter implements ICDIDebugger { final ICDebugger fDebugger; @@ -59,12 +60,12 @@ public class CDebugAdapter implements ICDIDebugger { public ICDISession createDebuggerSession(ILaunch launch, IBinaryObject exe, IProgressMonitor monitor) throws CoreException { ILaunchConfiguration config = launch.getLaunchConfiguration(); if (exe == null) { - abort(InternalDebugCoreMessages.getString("CDebugAdapter.Program_file_not_specified"), null, //$NON-NLS-1$ + abort(InternalDebugCoreMessages.CDebugAdapter_Program_file_not_specified, null, ICDTLaunchConfigurationConstants.ERR_UNSPECIFIED_PROGRAM); } IFile[] exeFile = ResourcesPlugin.getWorkspace().getRoot().findFilesForLocation(exe.getPath()); if (exeFile.length == 0) { - abort(InternalDebugCoreMessages.getString("CDebugAdapter.0"), null, -1); //$NON-NLS-1$ + abort(InternalDebugCoreMessages.CDebugAdapter_0, null, -1); } int pid = config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_ATTACH_PROCESS_ID, -1); String coreFile = config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_COREFILE_PATH, (String)null); @@ -92,7 +93,7 @@ public class CDebugAdapter implements ICDIDebugger { protected String renderDebuggerProcessLabel() { String format = "{0} ({1})"; //$NON-NLS-1$ String timestamp = DateFormat.getInstance().format(new Date(System.currentTimeMillis())); - String message = InternalDebugCoreMessages.getString("CDebugAdapter.1"); //$NON-NLS-1$ + String message = InternalDebugCoreMessages.CDebugAdapter_1; return MessageFormat.format(format, message, timestamp); } diff --git a/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/CGlobalVariableManager.java b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/CGlobalVariableManager.java index 90b66d19ef8..99f82142a17 100644 --- a/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/CGlobalVariableManager.java +++ b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/CGlobalVariableManager.java @@ -239,7 +239,7 @@ public class CGlobalVariableManager implements ICGlobalVariableManager { catch( IOException e ) { ex = e; } - abort( InternalDebugCoreMessages.getString( "CGlobalVariableManager.0" ), ex ); //$NON-NLS-1$ + abort(InternalDebugCoreMessages.CGlobalVariableManager_0, ex); } private void initialize() { diff --git a/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/CMemoryBlockRetrievalExtension.java b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/CMemoryBlockRetrievalExtension.java index ed2eeb10f73..e780f85d28e 100644 --- a/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/CMemoryBlockRetrievalExtension.java +++ b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/CMemoryBlockRetrievalExtension.java @@ -136,7 +136,7 @@ public class CMemoryBlockRetrievalExtension extends PlatformObject implements IM return; } - abort( InternalDebugCoreMessages.getString( "CMemoryBlockRetrievalExtension.3" ), null ); //$NON-NLS-1$ + abort(InternalDebugCoreMessages.CMemoryBlockRetrievalExtension_3, null); } /** @@ -157,7 +157,7 @@ public class CMemoryBlockRetrievalExtension extends PlatformObject implements IM CDebugTarget target = getDebugTarget(); if (target == null) { throw new DebugException(new Status(IStatus.ERROR, CDebugCorePlugin.getUniqueIdentifier(), DebugException.REQUEST_FAILED, - InternalDebugCoreMessages.getString("CMemoryBlockRetrievalExtension.CDebugTarget_not_available"), null)); //$NON-NLS-1$ + InternalDebugCoreMessages.CMemoryBlockRetrievalExtension_CDebugTarget_not_available, null)); } IAddressFactory addrFactory = target.getAddressFactory(); if (addrFactory instanceof IAddressFactory2) { @@ -280,11 +280,11 @@ public class CMemoryBlockRetrievalExtension extends PlatformObject implements IM } else { - msg = MessageFormat.format( InternalDebugCoreMessages.getString( "CMemoryBlockRetrievalExtension.1" ), new String[] { expression } ); //$NON-NLS-1$ + msg = MessageFormat.format(InternalDebugCoreMessages.CMemoryBlockRetrievalExtension_1, expression); } } else { - msg = MessageFormat.format( InternalDebugCoreMessages.getString( "CMemoryBlockRetrievalExtension.2" ), new String[] { expression } ); //$NON-NLS-1$ + msg = MessageFormat.format(InternalDebugCoreMessages.CMemoryBlockRetrievalExtension_2, expression); } } } @@ -293,7 +293,7 @@ public class CMemoryBlockRetrievalExtension extends PlatformObject implements IM msg = e.getMessage(); } catch( NumberFormatException e ) { - msg = MessageFormat.format( InternalDebugCoreMessages.getString( "CMemoryBlockRetrievalExtension.0" ), new String[] { expression } ); //$NON-NLS-1$ + msg = MessageFormat.format(InternalDebugCoreMessages.CMemoryBlockRetrievalExtension_0, expression); } finally { if (exp != null) { @@ -439,7 +439,9 @@ public class CMemoryBlockRetrievalExtension extends PlatformObject implements IM // minimum is "<space>:<expression>" if ((index == -1) || (index == str.length()-1)) { - IStatus s = new Status( IStatus.ERROR, CDebugCorePlugin.getUniqueIdentifier(), CDebugCorePlugin.INTERNAL_ERROR, InternalDebugCoreMessages.getString( "CMemoryBlockRetrievalExtension.5" ), null ); //$NON-NLS-1$ + IStatus s = new Status(IStatus.ERROR, CDebugCorePlugin.getUniqueIdentifier(), + CDebugCorePlugin.INTERNAL_ERROR, + InternalDebugCoreMessages.CMemoryBlockRetrievalExtension_invalid_encoded_address, null); throw new CoreException( s ); } @@ -474,7 +476,7 @@ public class CMemoryBlockRetrievalExtension extends PlatformObject implements IM }; } catch (CDIException exc) { - IStatus s = new Status(IStatus.ERROR, CDebugCorePlugin.getUniqueIdentifier(), CDebugCorePlugin.INTERNAL_ERROR, InternalDebugCoreMessages.getString( "CMemoryBlockRetrievalExtension.invalid_encoded_addresses" ), exc); //$NON-NLS-1$ + IStatus s = new Status(IStatus.ERROR, CDebugCorePlugin.getUniqueIdentifier(), CDebugCorePlugin.INTERNAL_ERROR, InternalDebugCoreMessages.CMemoryBlockRetrievalExtension_invalid_encoded_address, exc); throw new CoreException(s); } diff --git a/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/CRegisterManager.java b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/CRegisterManager.java index 5b095513991..25d08577d50 100644 --- a/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/CRegisterManager.java +++ b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/CRegisterManager.java @@ -257,11 +257,11 @@ public class CRegisterManager { private void initializeFromMemento( String memento ) throws CoreException { Node node = DebugPlugin.parseDocument( memento ); if ( node.getNodeType() != Node.ELEMENT_NODE ) { - abort( InternalDebugCoreMessages.getString( "CRegisterManager.0" ), null ); //$NON-NLS-1$ + abort(InternalDebugCoreMessages.CRegisterManager_0, null); } Element element = (Element)node; if ( !ELEMENT_REGISTER_GROUP_LIST.equals( element.getNodeName() ) ) { - abort( InternalDebugCoreMessages.getString( "CRegisterManager.1" ), null ); //$NON-NLS-1$ + abort(InternalDebugCoreMessages.CRegisterManager_1, null); } Node childNode = element.getFirstChild(); while( childNode != null ) { diff --git a/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/DebugConfiguration.java b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/DebugConfiguration.java index ab1c6116edb..79557863650 100644 --- a/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/DebugConfiguration.java +++ b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/DebugConfiguration.java @@ -51,7 +51,8 @@ public class DebugConfiguration implements ICDebugConfiguration { if (debugger instanceof ICDebugger) { return (ICDebugger)debugger; } - throw new CoreException(new Status(IStatus.ERROR, CDebugCorePlugin.getUniqueIdentifier(), -1, InternalDebugCoreMessages.getString("DebugConfiguration.0"), null)); //$NON-NLS-1$ + throw new CoreException(new Status(IStatus.ERROR, CDebugCorePlugin.getUniqueIdentifier(), -1, + InternalDebugCoreMessages.DebugConfiguration_0, null)); } @Override diff --git a/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/DebugStringVariableSubstitutor.java b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/DebugStringVariableSubstitutor.java new file mode 100644 index 00000000000..269df80fa0b --- /dev/null +++ b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/DebugStringVariableSubstitutor.java @@ -0,0 +1,190 @@ +/******************************************************************************* + * Copyright (c) 2013 Google, 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: + * Sergey Prigogin (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.debug.internal.core; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IWorkspaceRoot; +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.core.variables.IDynamicVariable; +import org.eclipse.core.variables.IStringVariable; +import org.eclipse.core.variables.IStringVariableManager; +import org.eclipse.core.variables.IValueVariable; +import org.eclipse.core.variables.IValueVariableListener; +import org.eclipse.core.variables.VariablesPlugin; + +/** + * String variable substitutor that resolves project_name, project_loc and project_path variables + * in the context of the given project. Resolution of all other variables is delegated to + * the default string variable manager. + */ +public class DebugStringVariableSubstitutor implements IStringVariableManager { + private static class ProjectVariable implements IDynamicVariable { + final String name; + final IProject project; + final String description; + + ProjectVariable(String name, String description, IProject project) { + this.name = name; + this.description = description; + this.project = project; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public String getValue(String argument) throws CoreException { + IProject project = this.project; + if (argument != null) { + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + IPath path = Path.fromOSString(argument); + project = path.isEmpty() ? null : root.getProject(path.segment(0)); + } + if (project == null) + return null; + if (name.endsWith("_name")) //$NON-NLS-1$ + return project.getName(); + if (name.endsWith("_loc")) //$NON-NLS-1$ + return project.getLocation().toOSString(); + if (name.endsWith("_path")) //$NON-NLS-1$ + return project.getProjectRelativePath().toString(); + return null; + } + + @Override + public boolean supportsArgument() { + return true; + } + } + + private final IStringVariableManager variableManager; + private final IProject project; + + /** + * Creates a variable substitutor that resolves project_name, project_loc and project_path + * variables in the context of the given project. + * + * @param project the project used to resolve project_name, project_loc and project_path + * variables. If {@code null}, the project is determined based on the current selection. + */ + public DebugStringVariableSubstitutor(IProject project) { + this.variableManager = VariablesPlugin.getDefault().getStringVariableManager(); + this.project = project; + } + + /** + * Creates a variable substitutor that resolves project_name, project_loc and project_path + * variables in the context of the given project. + * + * @param projectName the name of the project used to resolve project_name, project_loc and + * project_path variables. If {@code null}, the project is determined based on the current + * selection. + */ + public DebugStringVariableSubstitutor(String projectName) { + this(projectName == null ? null : ResourcesPlugin.getWorkspace().getRoot().getProject(projectName)); + } + + public IStringVariable[] getVariables() { + IStringVariable[] variables = variableManager.getVariables(); + for (int i = 0; i < variables.length; i++) { + IStringVariable var = variables[i]; + if (var instanceof IDynamicVariable) + variables[i] = substituteVariable((IDynamicVariable) var); + } + return variables; + } + + public IValueVariable[] getValueVariables() { + return variableManager.getValueVariables(); + } + + public IValueVariable getValueVariable(String name) { + return variableManager.getValueVariable(name); + } + + public IDynamicVariable[] getDynamicVariables() { + IDynamicVariable[] variables = variableManager.getDynamicVariables(); + for (int i = 0; i < variables.length; i++) { + variables[i] = substituteVariable(variables[i]); + } + return variables; + } + + public IDynamicVariable getDynamicVariable(String name) { + IDynamicVariable var = variableManager.getDynamicVariable(name); + return substituteVariable(var); + } + + private IDynamicVariable substituteVariable(IDynamicVariable var) { + String name = var.getName(); + if ("project_loc".equals(name) || "project_name".equals(name) || "project_path".equals(name)) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + return new ProjectVariable(name, var.getDescription(), project); + } + return var; + } + + public String getContributingPluginId(IStringVariable variable) { + return variableManager.getContributingPluginId(variable); + } + + public String performStringSubstitution(String expression) throws CoreException { + return performStringSubstitution(expression, true); + } + + public String performStringSubstitution(String expression, boolean reportUndefinedVariables) + throws CoreException { + return new StringSubstitutionEngine().performStringSubstitution(expression, + reportUndefinedVariables, true, this); + } + + public void validateStringVariables(String expression) throws CoreException { + new StringSubstitutionEngine().validateStringVariables(expression, this); + } + + public IValueVariable newValueVariable(String name, String description) { + return variableManager.newValueVariable(name, description); + } + + public IValueVariable newValueVariable(String name, String description, boolean readOnly, + String value) { + return variableManager.newValueVariable(name, description, readOnly, value); + } + + public void addVariables(IValueVariable[] variables) throws CoreException { + variableManager.addVariables(variables); + } + + public void removeVariables(IValueVariable[] variables) { + variableManager.removeVariables(variables); + } + + public void addValueVariableListener(IValueVariableListener listener) { + variableManager.addValueVariableListener(listener); + } + + public void removeValueVariableListener(IValueVariableListener listener) { + variableManager.removeValueVariableListener(listener); + } + + public String generateVariableExpression(String varName, String arg) { + return variableManager.generateVariableExpression(varName, arg); + } +} diff --git a/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/InternalDebugCoreMessages.java b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/InternalDebugCoreMessages.java index b5af135026f..af5a7f0e25d 100644 --- a/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/InternalDebugCoreMessages.java +++ b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/InternalDebugCoreMessages.java @@ -6,28 +6,38 @@ * http://www.eclipse.org/legal/epl-v10.html * * Contributors: - * QNX Software Systems - Initial API and implementation + * QNX Software Systems - Initial API and implementation + * Sergey Prigogin (Google) *******************************************************************************/ package org.eclipse.cdt.debug.internal.core; -import java.util.MissingResourceException; -import java.util.ResourceBundle; +import org.eclipse.core.internal.variables.VariablesMessages; +import org.eclipse.osgi.util.NLS; -public class InternalDebugCoreMessages { - - private static final String BUNDLE_NAME = "org.eclipse.cdt.debug.internal.core.InternalDebugCoreMessages";//$NON-NLS-1$ - - private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle( BUNDLE_NAME ); +public class InternalDebugCoreMessages extends NLS { + public static String CGlobalVariableManager_0; + public static String CMemoryBlockRetrievalExtension_0; + public static String CMemoryBlockRetrievalExtension_1; + public static String CMemoryBlockRetrievalExtension_2; + public static String CMemoryBlockRetrievalExtension_3; + public static String CMemoryBlockRetrievalExtension_4; + public static String CMemoryBlockRetrievalExtension_invalid_encoded_address; + public static String CMemoryBlockRetrievalExtension_CDebugTarget_not_available; + public static String DebugConfiguration_0; + public static String CDebugAdapter_0; + public static String CDebugAdapter_1; + public static String CDebugAdapter_Program_file_not_specified; + public static String CRegisterManager_0; + public static String CRegisterManager_1; + public static String StringSubstitutionEngine_undefined_variable; + public static String StringSubstitutionEngine_unexpected_argument; private InternalDebugCoreMessages() { } - public static String getString( String key ) { - try { - return RESOURCE_BUNDLE.getString( key ); - } - catch( MissingResourceException e ) { - return '!' + key + '!'; - } + static { + // Load message values from a bundle file. + NLS.initializeMessages(InternalDebugCoreMessages.class.getName(), VariablesMessages.class); } + } diff --git a/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/InternalDebugCoreMessages.properties b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/InternalDebugCoreMessages.properties index cd36701ed9b..959c2572886 100644 --- a/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/InternalDebugCoreMessages.properties +++ b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/InternalDebugCoreMessages.properties @@ -7,18 +7,21 @@ # # Contributors: # QNX Software Systems - initial API and implementation +# Sergey Prigogin (Google) ############################################################################### -CGlobalVariableManager.0=Invalid global variables data. -CMemoryBlockRetrievalExtension.0=Expression ''{0}'' evaluated to invalid address value. -CMemoryBlockRetrievalExtension.1=Invalid expression type: ''{0}'' -CMemoryBlockRetrievalExtension.2=Invalid expression: ''{0}'' -CMemoryBlockRetrievalExtension.3=Memory initialization: invalid memento. -CMemoryBlockRetrievalExtension.4=Invalid address: ''{0}'' -CMemoryBlockRetrievalExtension.invalid_encoded_addresses=Format of encoded address is invalid. -CMemoryBlockRetrievalExtension.CDebugTarget_not_available=Request cannot be handled at this time, probably because debug session has terminated. -DebugConfiguration.0=This debugger no longer supports this operation -CDebugAdapter.0=This debugger does not support debugging external files -CDebugAdapter.1=Debugger Process -CDebugAdapter.Program_file_not_specified=Program file not specified -CRegisterManager.0=Unable to restore register groups - invalid memento. -CRegisterManager.1=Unable to restore register groups - expecting register group list element. +CGlobalVariableManager_0=Invalid global variables data. +CMemoryBlockRetrievalExtension_0=Expression ''{0}'' evaluated to invalid address value. +CMemoryBlockRetrievalExtension_1=Invalid expression type: ''{0}'' +CMemoryBlockRetrievalExtension_2=Invalid expression: ''{0}'' +CMemoryBlockRetrievalExtension_3=Memory initialization: invalid memento. +CMemoryBlockRetrievalExtension_4=Invalid address: ''{0}'' +CMemoryBlockRetrievalExtension_invalid_encoded_address=Format of encoded address is invalid. +CMemoryBlockRetrievalExtension_CDebugTarget_not_available=Request cannot be handled at this time, probably because debug session has terminated. +DebugConfiguration_0=This debugger no longer supports this operation +CDebugAdapter_0=This debugger does not support debugging external files +CDebugAdapter_1=Debugger Process +CDebugAdapter_Program_file_not_specified=Program file not specified +CRegisterManager_0=Unable to restore register groups - invalid memento. +CRegisterManager_1=Unable to restore register groups - expecting register group list element. +StringSubstitutionEngine_undefined_variable=Reference to undefined variable {0} +StringSubstitutionEngine_unexpected_argument=Variable {0} does not accept arguments diff --git a/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/StringSubstitutionEngine.java b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/StringSubstitutionEngine.java new file mode 100644 index 00000000000..219093b867e --- /dev/null +++ b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/StringSubstitutionEngine.java @@ -0,0 +1,303 @@ +/******************************************************************************* + * Copyright (c) 2000, 2013 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 + * Sergey Prigogin (Google) + *******************************************************************************/ +package org.eclipse.cdt.debug.internal.core; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Stack; + +import org.eclipse.cdt.debug.core.CDebugCorePlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.variables.IDynamicVariable; +import org.eclipse.core.variables.IStringVariableManager; +import org.eclipse.core.variables.IValueVariable; +import org.eclipse.osgi.util.NLS; + +/** + * Performs string substitution for context and value variables. + * A clone of {@link org.eclipse.core.internal.variables.StringSubstitutionEngine}. + */ +public class StringSubstitutionEngine { + // Delimiters. + private static final String VARIABLE_START = "${"; //$NON-NLS-1$ + private static final char VARIABLE_END = '}'; + private static final char VARIABLE_ARG = ':'; + // Parsing states. + private static final int SCAN_FOR_START = 0; + private static final int SCAN_FOR_END = 1; + + /** + * Resulting string + */ + private StringBuilder fResult; + + /** + * Whether substitutions were performed + */ + private boolean fSubs; + + /** + * Stack of variables to resolve + */ + private Stack<VariableReference> fStack; + + class VariableReference { + // The text inside the variable reference. + private StringBuilder fText; + + public VariableReference() { + fText = new StringBuilder(); + } + + public void append(String text) { + fText.append(text); + } + + public String getText() { + return fText.toString(); + } + } + + /** + * Performs recursive string substitution and returns the resulting string. + * + * @param expression expression to resolve + * @param reportUndefinedVariables whether to report undefined variables as an error + * @param resolveVariables if the variables should be resolved during the substitution + * @param manager registry of variables + * @return the resulting string with all variables recursively substituted + * @exception CoreException if unable to resolve a referenced variable or if a cycle exists + * in referenced variables + */ + public String performStringSubstitution(String expression, boolean reportUndefinedVariables, + boolean resolveVariables, IStringVariableManager manager) throws CoreException { + substitute(expression, reportUndefinedVariables, resolveVariables, manager); + List<HashSet<String>> resolvedVariableSets = new ArrayList<HashSet<String>>(); + while (fSubs) { + HashSet<String> resolved = substitute(fResult.toString(), reportUndefinedVariables, true, manager); + + for (int i = resolvedVariableSets.size(); --i >= 0;) { + HashSet<String> prevSet = resolvedVariableSets.get(i); + + if (prevSet.equals(resolved)) { + HashSet<String> conflictingSet = new HashSet<String>(); + for (HashSet<String> set : resolvedVariableSets) { + conflictingSet.addAll(set); + } + + StringBuilder problemVariableList = new StringBuilder(); + for (String var : conflictingSet) { + problemVariableList.append(var.toString()); + problemVariableList.append(", "); //$NON-NLS-1$ + } + problemVariableList.setLength(problemVariableList.length() - 2); // Truncate the last ", " + throw new CoreException(new Status(IStatus.ERROR, + CDebugCorePlugin.getUniqueIdentifier(), CDebugCorePlugin.INTERNAL_ERROR, + NLS.bind(InternalDebugCoreMessages.StringSubstitutionEngine_undefined_variable, problemVariableList.toString()), null)); + } + } + + resolvedVariableSets.add(resolved); + } + return fResult.toString(); + } + + /** + * Performs recursive string validation to ensure that all of the variables + * contained in the expression exist. + * + * @param expression expression to validate + * @param manager registry of variables + * @exception CoreException if a referenced variable does not exist or if a cycle exists + * in referenced variables + */ + public void validateStringVariables(String expression, IStringVariableManager manager) + throws CoreException { + performStringSubstitution(expression, true, false, manager); + } + + /** + * Makes a substitution pass of the given expression returns a Set of the variables that were + * resolved in this pass. + * + * @param expression source expression + * @param reportUndefinedVariables whether to report undefined variables as an error + * @param resolveVariables whether to resolve the value of any variables + * @param manager the {@link IStringVariableManager} to use for the substitution + * @return the set of {@link String}s resolved from the given expression + * @exception CoreException if unable to resolve a variable + */ + private HashSet<String> substitute(String expression, boolean reportUndefinedVariables, + boolean resolveVariables, IStringVariableManager manager) throws CoreException { + fResult = new StringBuilder(expression.length()); + fStack = new Stack<VariableReference>(); + fSubs = false; + + HashSet<String> resolvedVariables = new HashSet<String>(); + + int pos = 0; + int state = SCAN_FOR_START; + while (pos < expression.length()) { + switch (state) { + case SCAN_FOR_START: + int start = expression.indexOf(VARIABLE_START, pos); + if (start >= 0) { + int length = start - pos; + // Copy non-variable text to the result. + if (length > 0) { + fResult.append(expression.substring(pos, start)); + } + pos = start + 2; + state = SCAN_FOR_END; + + fStack.push(new VariableReference()); + } else { + // Done - no more variables. + fResult.append(expression.substring(pos)); + pos = expression.length(); + } + break; + case SCAN_FOR_END: + // Be careful of nested variables. + start = expression.indexOf(VARIABLE_START, pos); + int end = expression.indexOf(VARIABLE_END, pos); + if (end < 0) { + // Variables are not completed. + VariableReference tos = fStack.peek(); + tos.append(expression.substring(pos)); + pos = expression.length(); + } else { + if (start >= 0 && start < end) { + // Start of a nested variable. + int length = start - pos; + if (length > 0) { + VariableReference tos = fStack.peek(); + tos.append(expression.substring(pos, start)); + } + pos = start + 2; + fStack.push(new VariableReference()); + } else { + // End of variable reference. + VariableReference tos = fStack.pop(); + String substring = expression.substring(pos, end); + tos.append(substring); + resolvedVariables.add(substring); + + pos = end + 1; + String value= resolve(tos, reportUndefinedVariables, resolveVariables, manager); + if (value == null) { + value = ""; //$NON-NLS-1$ + } + if (fStack.isEmpty()) { + // Append to result. + fResult.append(value); + state = SCAN_FOR_START; + } else { + // Append to previous variable. + tos = fStack.peek(); + tos.append(value); + } + } + } + break; + } + } + // Process incomplete variable references. + while (!fStack.isEmpty()) { + VariableReference tos = fStack.pop(); + if (fStack.isEmpty()) { + fResult.append(VARIABLE_START); + fResult.append(tos.getText()); + } else { + VariableReference var = fStack.peek(); + var.append(VARIABLE_START); + var.append(tos.getText()); + } + } + + + return resolvedVariables; + } + + /** + * Resolve and return the value of the given variable reference, possibly {@code null}. + * + * @param var the {@link VariableReference} to try and resolve + * @param reportUndefinedVariables whether to report undefined variables as an error + * @param resolveVariables whether to resolve the variables value or just to validate that + * this variable is valid + * @param manager variable registry + * @return variable value, possibly {@code null} + * @exception CoreException if unable to resolve a value + */ + private String resolve(VariableReference var, boolean reportUndefinedVariables, + boolean resolveVariables, IStringVariableManager manager) throws CoreException { + String text = var.getText(); + int pos = text.indexOf(VARIABLE_ARG); + String name = null; + String arg = null; + if (pos > 0) { + name = text.substring(0, pos); + pos++; + if (pos < text.length()) { + arg = text.substring(pos); + } + } else { + name = text; + } + IValueVariable valueVariable = manager.getValueVariable(name); + if (valueVariable == null) { + IDynamicVariable dynamicVariable = manager.getDynamicVariable(name); + if (dynamicVariable == null) { + // No variables with the given name. + if (reportUndefinedVariables) { + throw new CoreException(new Status(IStatus.ERROR, + CDebugCorePlugin.getUniqueIdentifier(), CDebugCorePlugin.INTERNAL_ERROR, + NLS.bind(InternalDebugCoreMessages.StringSubstitutionEngine_undefined_variable, name), null)); + } + // Leave as is. + return getOriginalVarText(var); + } + + if (resolveVariables) { + fSubs = true; + return dynamicVariable.getValue(arg); + } + // Leave as is. + return getOriginalVarText(var); + } + + if (arg == null) { + if (resolveVariables) { + fSubs = true; + return valueVariable.getValue(); + } + // Leave as is. + return getOriginalVarText(var); + } + // Error - an argument specified for a value variable. + throw new CoreException(new Status(IStatus.ERROR, + CDebugCorePlugin.getUniqueIdentifier(), CDebugCorePlugin.INTERNAL_ERROR, + NLS.bind(InternalDebugCoreMessages.StringSubstitutionEngine_unexpected_argument, + valueVariable.getName()), null)); + } + + private String getOriginalVarText(VariableReference var) { + StringBuilder res = new StringBuilder(var.getText()); + res.insert(0, VARIABLE_START); + res.append(VARIABLE_END); + return res.toString(); + } +} |