blob: 3edaa9b87a35612e6f6a4b7c2b7db90965ac99b3 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Tomasz Stanczak - Fix for Bug 29504
* Keith Seitz (keiths@redhat.com) - environment variables contribution (Bug 27243)
*******************************************************************************/
package org.eclipse.debug.core.variables;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
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.ILaunchConfiguration;
import org.eclipse.debug.internal.core.DebugCoreMessages;
/**
* Utility for dealing with launch variables.
*
* @since 3.0
*/
public class LaunchVariableUtil {
/**
* Variable tag indentifiers
*/
private static final char VAR_TAG_START_CHAR1 = '$'; //$NON-NLS-1$
private static final char VAR_TAG_START_CHAR2 = '{'; //$NON-NLS-1$
private static final char VAR_TAG_END_CHAR1 = '}'; //$NON-NLS-1$
private static final String VAR_TAG_START = "${"; //$NON-NLS-1$
private static final String VAR_TAG_END = "}"; //$NON-NLS-1$
private static final String VAR_TAG_SEP = ":"; //$NON-NLS-1$
/**
* Argument parsing constants
*/
private static final char ARG_DELIMITER = ' '; //$NON-NLS-1$
private static final char ARG_DBL_QUOTE = '"'; //$NON-NLS-1$
/**
* Launch configuration attribute - a map of variables passed into
* Runtime.exec(...) when a launch configuration is launched.
*
* @since 3.0
*/
public static final String ATTR_ENVIRONMENT_VARIABLES = DebugPlugin.getUniqueIdentifier() + ".environmentVariables"; //$NON-NLS-1$
/**
* Boolean attribute indicating if a refresh scope is recursive. Default
* value is <code>false</code>.
*
* @since 3.0
*/
public static final String ATTR_REFRESH_RECURSIVE = DebugPlugin.getUniqueIdentifier() + ".ATTR_REFRESH_RECURSIVE"; //$NON-NLS-1$
/**
* String attribute identifying the scope of resources that should be
* refreshed after an external tool is run. The value is either a refresh
* variable or the default value, <code>null</code>, indicating no refresh.
*
* @since 3.0
*/
public static final String ATTR_REFRESH_SCOPE = DebugPlugin.getUniqueIdentifier() + ".ATTR_REFRESH_SCOPE"; //$NON-NLS-1$
/**
* Structure to represent a variable definition within a
* source string.
*/
public static final class VariableDefinition {
/**
* Index in the source text where the variable started
* or <code>-1</code> if no valid variable start tag
* identifier found.
*/
public int start = -1;
/**
* Index in the source text of the character following
* the end of the variable or <code>-1</code> if no
* valid variable end tag found.
*/
public int end = -1;
/**
* The variable's name found in the source text, or
* <code>null</code> if no valid variable found.
*/
public String name = null;
/**
* The variable's argument found in the source text, or
* <code>null</code> if no valid variable found or if
* the variable did not specify an argument
*/
public String argument = null;
/**
* Create an initialized variable definition.
*/
private VariableDefinition() {
super();
}
}
/**
* Extracts a variable name and argument from the given string.
*
* @param text the source text to parse for a variable tag
* @param start the index in the string to start the search
* @return the variable definition
*/
public static VariableDefinition extractVariableDefinition(String text, int start) {
VariableDefinition varDef = new VariableDefinition();
varDef.start = text.indexOf(VAR_TAG_START, start);
if (varDef.start < 0){
return varDef;
}
start = varDef.start + VAR_TAG_START.length();
int end = text.indexOf(VAR_TAG_END, start);
if (end < 0) {
return varDef;
}
varDef.end = end + VAR_TAG_END.length();
if (end == start) {
return varDef;
}
int mid = text.indexOf(VAR_TAG_SEP, start);
if (mid < 0 || mid > end) {
varDef.name = text.substring(start, end);
} else {
if (mid > start) {
varDef.name = text.substring(start, mid);
}
mid = mid + VAR_TAG_SEP.length();
if (mid < end) {
varDef.argument = text.substring(mid, end);
}
}
return varDef;
}
/**
* Returns an expression referencing the given variable and argument.
*
* @param varName the name of a variable
* @param argument an optional argument for the variable, <code>null</code> if none
* @return an expression referencing the given variable and argument
*/
public static String newVariableExpression(String varName, String argument) {
StringBuffer buf = new StringBuffer();
appendVariableExpression(varName,argument, buf);
return buf.toString();
}
/**
* Builds and appends a variable expression referencing the given variable and
* argument, to the given String buffer.
*
* @param varName the name of a variable
* @param argument an optional argument for the variable, <code>null</code> if none
* @param buffer the buffer to append the expression to
*/
private static void appendVariableExpression(String varName, String argument, StringBuffer buffer) {
buffer.append(VAR_TAG_START);
buffer.append(varName);
if (argument != null && argument.length() > 0) {
buffer.append(VAR_TAG_SEP);
buffer.append(argument);
}
buffer.append(VAR_TAG_END);
}
/**
* Expands all the variables found in the given string.
*
* @param argument the string whose variables should be expanded
* @param context the context to use for expanding variables or
* <code>null</code> if none.
* @param status multi status to report any problems expanding variables or
* <code>null</code> if none.
* @return the argument text with all variables expanded, or <code>null</code> if not possible
*/
public static String expandVariables(String argument, MultiStatus status, ExpandVariableContext context) {
StringBuffer buffer = new StringBuffer();
int start = 0;
VariableDefinition varDef= extractVariableDefinition(argument, start);
while (varDef.start > -1) {
if (varDef.end == -1 || varDef.name == null || varDef.name.length() == 0) {
// Invalid variable format
if (status != null) {
status.merge(newErrorStatus(MessageFormat.format(DebugCoreMessages.getString("VariableUtil.4"), new String[] {argument.substring(varDef.start)}), null)); //$NON-NLS-1$
}
return null;
}
// Copy text between start and variable.
if (varDef.start > start) {
buffer.append(argument.substring(start, varDef.start));
}
start = varDef.end;
// Look up the context variable if it exists
ILaunchVariableManager manager = DebugPlugin.getDefault().getLaunchVariableManager();
IContextLaunchVariable contextVariable = manager.getContextVariable(varDef.name);
if (contextVariable != null) {
String text = null;
if (context == null) {
context= new ExpandVariableContext(null);
}
try {
text= contextVariable.getExpander().getText(varDef.name, varDef.argument, context);
} catch (CoreException exception) {
if (status != null) {
status.merge(exception.getStatus());
}
return null;
}
buffer.append(text);
} else {
// If no context variable found, look up a simple variable
ISimpleLaunchVariable simpleVariable= manager.getSimpleVariable(varDef.name);
if (simpleVariable == null) {
if (status != null) {
status.merge(newErrorStatus(MessageFormat.format(DebugCoreMessages.getString("VariableUtil.5"), new Object[] {varDef.name}), null)); //$NON-NLS-1$
}
return null;
}
buffer.append(simpleVariable.getValue());
}
varDef = extractVariableDefinition(argument, start);
}
// No more variables
if (start == 0) {
buffer.append(argument);
} else {
buffer.append(argument.substring(start));
}
return buffer.toString();
}
/**
* Return a list of all the environment variables (suitable for passing to
* Process.exec) in which variable expansion has been performed.
*
* @param envMap Map of all the environment variables (key=name,value=value)
* @param context the context used to expand the variable or <code>null</code>
* if none.
* @param status multi status to report any problems expanding variables
* @return String[] the list of variables in "variable=value" form
*/
public static String[] expandEnvironment(Map envMap, MultiStatus status, ExpandVariableContext context) {
String[] vars = null;
if (envMap != null && envMap.size() > 0) {
Map.Entry e;
Iterator iter = envMap.entrySet().iterator();
vars = new String[envMap.size()];
int i = 0;
while (iter.hasNext()) {
e = (Map.Entry) iter.next();
vars[i++] = (String) e.getKey() + '=' + expandVariables((String) e.getValue(), status, context);
}
}
return vars;
}
/**
* Returns a list of individual strings where all
* variables have been expanded. The given string
* is separated into individual strings based on whitespace
* deliniation.
*
* @param sourceString the source string with leading and trailing
* spaces already removed.
* @param context the context used to expand the variable(s)
* @param status multi status to report any problems expanding variables or
* <code>null</code> if none.
* @return the list of individual arguments where some elements in the
* list maybe <code>null</code> if problems expanding variable(s).
*/
public static String[] expandStrings(String sourceString, MultiStatus status, ExpandVariableContext context) {
if (sourceString == null || sourceString.length() == 0) {
return new String[0];
}
String[] argList = parseStringIntoList(sourceString);
for (int i = 0; i < argList.length; i++) {
argList[i] = expandVariables(argList[i], status, context);
}
return argList;
}
/**
* Parses the argument text into an array of individual
* strings using the space character as the delimiter.
* An individual argument containing spaces must have a
* double quote (") at the start and end. Two double
* quotes together is taken to mean an embedded double
* quote in the argument text. Variables are treated as
* a single unit and therefore spaces and double quotes
* inside a variable are copied as is and not parsed.
*
* @param arguments the arguments as one string
* @return the array of arguments
*/
public static String[] parseStringIntoList(String arguments) {
if (arguments == null || arguments.length() == 0) {
return new String[0];
}
List list = new ArrayList(10);
boolean inQuotes = false;
boolean inVar = false;
int start = 0;
int end = arguments.length();
StringBuffer buffer = new StringBuffer(end);
while (start < end) {
char ch = arguments.charAt(start);
start++;
switch (ch) {
case ARG_DELIMITER :
if (inQuotes || inVar) {
buffer.append(ch);
} else {
if (buffer.length() > 0) {
list.add(buffer.toString());
buffer.setLength(0);
}
}
break;
case ARG_DBL_QUOTE :
if (inVar) {
buffer.append(ch);
} else {
if (start < end) {
if (arguments.charAt(start) == ARG_DBL_QUOTE) {
// Two quotes together represents one quote
buffer.append(ch);
start++;
} else {
inQuotes = !inQuotes;
}
} else {
// A lone quote at the end, just drop it.
inQuotes = false;
}
}
break;
case VAR_TAG_START_CHAR1 :
buffer.append(ch);
if (!inVar && start < end) {
if (arguments.charAt(start) == VAR_TAG_START_CHAR2) {
buffer.append(VAR_TAG_START_CHAR2);
inVar = true;
start++;
}
}
break;
case VAR_TAG_END_CHAR1 :
buffer.append(ch);
inVar = false;
break;
default :
buffer.append(ch);
break;
}
}
if (buffer.length() > 0) {
list.add(buffer.toString());
}
String[] results = new String[list.size()];
list.toArray(results);
return results;
}
/**
* Returns an array of (expanded) environment variables to be used when
* running the launch configuration or <code>null</code> if unspecified
*
* @param configuration launch configuration
* @param context context used to expand environment variable values
* or <code>null</code> if none
* @return String[] the array of "variable=value" pairs, suitable for
* passing to Runtime.exec(...)
* @throws CoreException if unable to access associated attribute or if
* unable to resolve a variable in an environment variable's value
*/
public static String[] getEnvironment(ILaunchConfiguration configuration, ExpandVariableContext context) throws CoreException {
Map envMap = configuration.getAttribute(ATTR_ENVIRONMENT_VARIABLES, (Map) null);
if (envMap != null) {
MultiStatus status = new MultiStatus(DebugPlugin.getUniqueIdentifier(), 0, DebugCoreMessages.getString("VariableUtil.6"), null); //$NON-NLS-1$
String[] expandedEnvironment = LaunchVariableUtil.expandEnvironment(envMap, status, context);
if (status.isOK()) {
if (expandedEnvironment != null && expandedEnvironment.length > 0) {
return expandedEnvironment;
} else {
String message = MessageFormat.format(DebugCoreMessages.getString("VariableUtil.7"), new Object[] { configuration.getName()}); //$NON-NLS-1$
throw new CoreException(newErrorStatus(message, null));
}
} else {
throw new CoreException(status);
}
}
return null;
}
private static IStatus newErrorStatus(String message, Throwable exception) {
return new Status(IStatus.ERROR, DebugPlugin.getUniqueIdentifier(), DebugPlugin.INTERNAL_ERROR, message, exception);
}
/**
* Returns the refresh scope specified by the given launch configuration or
* <code>null</code> if none.
*
* @param configuration
* @return refresh scope
* @throws CoreException if unable to access the associated attribute
*/
public static String getRefreshScope(ILaunchConfiguration configuration) throws CoreException {
return configuration.getAttribute(LaunchVariableUtil.ATTR_REFRESH_SCOPE, (String) null);
}
/**
* Returns whether the refresh scope specified by the given launch
* configuration is recursive.
*
* @param configuration
* @return whether the refresh scope is recursive
* @throws CoreException if unable to access the associated attribute
*/
public static boolean isRefreshRecursive(ILaunchConfiguration configuration) throws CoreException {
return configuration.getAttribute(LaunchVariableUtil.ATTR_REFRESH_RECURSIVE, true);
}
}