org.mule.mvel2.ParserContext Maven / Gradle / Ivy
Go to download
MVEL is a powerful expression language for Java-based applications.
It provides a plethora of features and is suited for everything
from the smallest property binding and extraction, to full blown scripts.
This is a fork of MVEL customized for use in Mule.
/**
* MVEL 2.0
* Copyright (C) 2007 The Codehaus
* Mike Brock, Dhanji Prasanna, John Graham, Mark Proctor
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mule.mvel2;
import org.mule.mvel2.ast.Function;
import org.mule.mvel2.ast.LineLabel;
import org.mule.mvel2.ast.Proto;
import org.mule.mvel2.compiler.AbstractParser;
import org.mule.mvel2.compiler.Parser;
import org.mule.mvel2.integration.Interceptor;
import org.mule.mvel2.util.LineMapper;
import org.mule.mvel2.util.MethodStub;
import org.mule.mvel2.util.ReflectionUtil;
import java.io.Serializable;
import java.lang.reflect.*;
import java.util.*;
/**
* The ParserContext is the main environment object used for sharing state throughout the entire
* parser/compileShared process.
* The ParserContext is used to configure the parser/compiler. For example:
*
* ParserContext parserContext = new ParserContext();
* parserContext.setStrongTyping(true); // turn on strong typing.
*
* Serializable comp = MVEL.compileExpression("foo.bar", parserContext);
*
*/
public class ParserContext implements Serializable {
private String sourceFile;
private int lineCount = 1;
private int lineOffset;
private ParserContext parent;
private ParserConfiguration parserConfiguration = new ParserConfiguration();
private ArrayList indexedInputs;
private ArrayList indexedLocals;
private ArrayList> variableVisibility;
private HashMap variables;
private Map inputs;
private transient HashMap> typeParameters;
private transient Type[] lastTypeParameters;
private HashMap globalFunctions;
private transient List errorList;
private transient Map sourceLineLookups;
private transient Map> visitedLines;
private LineLabel lastLineLabel;
private transient Parser rootParser;
private boolean compiled = false;
private boolean strictTypeEnforcement = false;
private boolean strongTyping = false;
private boolean optimizationMode = false;
private boolean fatalError = false;
private boolean retainParserState = false;
private boolean debugSymbols = false;
private boolean blockSymbols = false;
private boolean executableCodeReached = false;
private boolean indexAllocation = false;
protected boolean variablesEscape = false;
public ParserContext() {
}
public ParserContext(boolean debugSymbols) {
this.debugSymbols = debugSymbols;
}
public ParserContext(Parser rootParser) {
this.rootParser = rootParser;
}
public ParserContext(ParserConfiguration parserConfiguration) {
this.parserConfiguration = parserConfiguration;
}
public ParserContext(Map imports, Map interceptors, String sourceFile) {
this.sourceFile = sourceFile;
this.parserConfiguration = new ParserConfiguration(imports, interceptors);
}
public ParserContext createSubcontext() {
ParserContext ctx = new ParserContext(parserConfiguration);
ctx.sourceFile = sourceFile;
ctx.parent = this;
ctx.addInputs(inputs);
ctx.addVariables(variables);
ctx.addIndexedInputs(indexedInputs);
ctx.addTypeParameters(typeParameters);
ctx.sourceLineLookups = sourceLineLookups;
ctx.lastLineLabel = lastLineLabel;
ctx.variableVisibility = variableVisibility;
ctx.globalFunctions = globalFunctions;
ctx.lastTypeParameters = lastTypeParameters;
ctx.errorList = errorList;
ctx.rootParser = rootParser;
ctx.lineCount = lineCount;
ctx.lineOffset = lineOffset;
ctx.compiled = compiled;
ctx.strictTypeEnforcement = strictTypeEnforcement;
ctx.strongTyping = strongTyping;
ctx.fatalError = fatalError;
ctx.retainParserState = retainParserState;
ctx.debugSymbols = debugSymbols;
ctx.blockSymbols = blockSymbols;
ctx.executableCodeReached = executableCodeReached;
ctx.indexAllocation = indexAllocation;
return ctx;
}
public ParserContext createColoringSubcontext() {
if (parent == null) {
throw new RuntimeException("create a subContext first");
}
ParserContext ctx = new ParserContext(parserConfiguration) {
@Override
public void addVariable(String name, Class type) {
if ((parent.variables != null && parent.variables.containsKey(name))
|| (parent.inputs != null && parent.inputs.containsKey(name))) {
this.variablesEscape = true;
}
super.addVariable(name, type);
}
@Override
public void addVariable(String name, Class type, boolean failIfNewAssignment) {
if ((parent.variables != null && parent.variables.containsKey(name))
|| (parent.inputs != null && parent.inputs.containsKey(name))) {
this.variablesEscape = true;
}
super.addVariable(name, type, failIfNewAssignment);
}
@Override
public Class getVarOrInputType(String name) {
if ((parent.variables != null && parent.variables.containsKey(name))
|| (parent.inputs != null && parent.inputs.containsKey(name))) {
this.variablesEscape = true;
}
return super.getVarOrInputType(name);
}
};
ctx.initializeTables();
ctx.sourceFile = sourceFile;
ctx.inputs = inputs;
ctx.variables = variables;
ctx.indexedInputs = indexedInputs;
ctx.typeParameters = typeParameters;
ctx.sourceLineLookups = sourceLineLookups;
ctx.lastLineLabel = lastLineLabel;
ctx.variableVisibility = variableVisibility;
ctx.globalFunctions = globalFunctions;
ctx.lastTypeParameters = lastTypeParameters;
ctx.errorList = errorList;
ctx.rootParser = rootParser;
ctx.lineCount = lineCount;
ctx.lineOffset = lineOffset;
ctx.compiled = compiled;
ctx.strictTypeEnforcement = strictTypeEnforcement;
ctx.strongTyping = strongTyping;
ctx.fatalError = fatalError;
ctx.retainParserState = retainParserState;
ctx.debugSymbols = debugSymbols;
ctx.blockSymbols = blockSymbols;
ctx.executableCodeReached = executableCodeReached;
ctx.indexAllocation = indexAllocation;
return ctx;
}
/**
* Tests whether or not a variable or input exists in the current parser context.
*
* @param name The name of the identifier.
* @return boolean
*/
public boolean hasVarOrInput(String name) {
return (variables != null && variables.containsKey(name))
|| (inputs != null && inputs.containsKey(name));
}
/**
* Return the variable or input type froom the current parser context. Returns Object.class if the
* type cannot be determined.
*
* @param name The name of the identifier
* @return boolean
*/
public Class getVarOrInputType(String name) {
if (variables != null && variables.containsKey(name)) {
return variables.get(name);
} else if (inputs != null && inputs.containsKey(name)) {
return inputs.get(name);
}
return Object.class;
}
public Class getVarOrInputTypeOrNull(String name) {
if (variables != null && variables.containsKey(name)) {
return variables.get(name);
} else if (inputs != null && inputs.containsKey(name)) {
return inputs.get(name);
}
return null;
}
/**
* Get total number of lines declared in the current context.
*
* @return int of lines
*/
public int getLineCount() {
return lineCount;
}
/**
* Set the current number of lines in the current context. (Generally only used by the compiler)
*
* @param lineCount The number of lines
* @return int of lines
*/
public int setLineCount(int lineCount) {
return this.lineCount = lineCount;
}
/**
* Increments the current line count by the specified amount
*
* @param increment The number of lines to increment
* @return int of lines
*/
public int incrementLineCount(int increment) {
return this.lineCount += increment;
}
/**
* Get the current line offset. This measures the number of cursor positions back to the beginning of the line.
*
* @return int offset
*/
public int getLineOffset() {
return lineOffset;
}
/**
* Sets the current line offset. (Generally only used by the compiler)
*
* @param lineOffset The offset amount
*/
public void setLineOffset(int lineOffset) {
this.lineOffset = lineOffset;
}
/**
* Sets both the current line count and line offset
*
* @param lineCount The line count
* @param lineOffset The line offset
*/
public void setLineAndOffset(int lineCount, int lineOffset) {
//addKnownLine(this.lineCount = lineCount);
this.lineOffset = lineOffset;
}
/**
* Get an import that has been declared, either in the parsed script or programatically
*
* @param name The name identifier for the imported class (ie. "HashMap")
* @return An instance of Class denoting the imported class.
*/
public Class getImport(String name) {
return parserConfiguration.getImport(name);
}
/**
* Get a {@link MethodStub} which wraps a static method import.
*
* @param name The name identifier
* @return An instance of {@link MethodStub}
*/
public MethodStub getStaticImport(String name) {
return parserConfiguration.getStaticImport(name);
}
/**
* Returns either an instance of Class or {@link MethodStub} (whichever matches).
*
* @param name The name identifier.
* @return An instance of Class or {@link MethodStub}
*/
public Object getStaticOrClassImport(String name) {
return parserConfiguration.getStaticOrClassImport(name);
}
/**
* Adds a package import to a parse session.
*
* @param packageName A fully qualified package (eg. java.util.concurrent).
*/
public void addPackageImport(String packageName) {
parserConfiguration.addPackageImport(packageName);
}
/**
* Tests to see if the specified import exists.
*
* @param name A name identifier
* @return boolean
*/
public boolean hasImport(String name) {
return parserConfiguration.hasImport(name);
}
public boolean hasProtoImport(String name) {
if (parserConfiguration.getImports() == null) return false;
Object o = parserConfiguration.getImports().get(name);
return o != null && o instanceof Proto;
}
public Proto getProtoImport(String name) {
return (Proto) parserConfiguration.getImports().get(name);
}
/**
* Adds an import for the specified Class.
*
* @param cls The instance of the Class which represents the imported class.
*/
public void addImport(Class cls) {
addImport(cls.getSimpleName(), cls);
}
public void addImport(Proto proto) {
parserConfiguration.addImport(proto.getName(), proto);
}
/**
* Adds an import for a specified Class using an alias. For example:
*
* parserContext.addImport("sys", System.class);
*
* ... doing this would allow an MVEL script to be written as such:
*
* sys.currentTimeMillis();
*
*
* @param name The alias to use
* @param cls The instance of the Class which represents the imported class.
*/
public void addImport(String name, Class cls) {
parserConfiguration.addImport(name, cls);
// addInput(name, cls);
}
/**
* Adds an import for a specified Method representing a static method import using an alias. For example:
*
* parserContext.addImport("time", MVEL.getStaticMethod(System.class, "currentTimeMillis", new Class[0]));
*
* ... doing this allows the System.currentTimeMillis() method to be executed in a script simply by writing
* time().
*
* @param name The alias to use
* @param method The instance of Method which represents the static import.
*/
public void addImport(String name, Method method) {
addImport(name, new MethodStub(method));
// addInput(name, MethodStub.class);
}
/**
* Adds a static import for the specified {@link MethodStub} with an alias.
*
* @param name The alias to use
* @param method The instance of Method which represents the static import.
* @see #addImport(String, org.mule.mvel2.util.MethodStub)
*/
public void addImport(String name, MethodStub method) {
parserConfiguration.addImport(name, method);
}
/**
* Initializes internal Maps. Called by the compiler.
*/
public void initializeTables() {
if (variables == null) variables = new LinkedHashMap();
if (inputs == null) inputs = new LinkedHashMap();
if (variableVisibility == null) {
initVariableVisibility();
pushVariableScope();
Set scope = getVariableScope();
scope.addAll(variables.keySet());
scope.addAll(inputs.keySet());
if (parserConfiguration.getImports() != null)
scope.addAll(parserConfiguration.getImports().keySet());
if (inputs.containsKey("this")) {
Class> ctxType = inputs.get("this");
for (Field field : ctxType.getFields()) {
if ((field.getModifiers() & (Modifier.PUBLIC | Modifier.STATIC)) != 0) {
scope.add(field.getName());
}
}
for (Method m : ctxType.getMethods()) {
if ((m.getModifiers() & Modifier.PUBLIC) != 0) {
if (m.getName().startsWith("get")
|| (m.getName().startsWith("is")
&& (m.getReturnType().equals(boolean.class) || m.getReturnType().equals(Boolean.class)))) {
String propertyName = ReflectionUtil.getPropertyFromAccessor(m.getName());
scope.add(propertyName);
propertyName = propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
scope.add(propertyName);
} else {
scope.add(m.getName());
}
}
}
}
}
}
public void addVariable(String name, Class type, boolean failIfNewAssignment) {
initializeTables();
if (variables.containsKey(name) && failIfNewAssignment)
throw new RuntimeException("statically-typed variable already defined in scope: " + name);
if (type == null) type = Object.class;
variables.put(name, type);
makeVisible(name);
}
public void addVariable(String name, Class type) {
initializeTables();
if (variables.containsKey(name) || inputs.containsKey(name)) return;
if (type == null) type = Object.class;
variables.put(name, type);
makeVisible(name);
}
public void addVariables(Map variables) {
if (variables == null) return;
initializeTables();
for (Map.Entry entry : variables.entrySet()) {
addVariable(entry.getKey(), entry.getValue());
}
}
public void addInput(String name, Class type) {
if (inputs == null) inputs = new LinkedHashMap();
if (inputs.containsKey(name) || (variables != null && variables.containsKey(name))) return;
if (type == null) type = Object.class;
inputs.put(name, type);
}
public void addInput(String name, Class type, Class[] typeParameters) {
if (type == null) type = Object.class;
addInput(name, type);
if (this.typeParameters == null) {
this.typeParameters = new LinkedHashMap>();
}
if (this.typeParameters.get(name) == null) {
this.typeParameters.put(name, new LinkedHashMap());
}
Map t = this.typeParameters.get(name);
if (typeParameters.length != type.getTypeParameters().length) {
throw new RuntimeException("wrong number of type parameters for: " + type.getName());
}
TypeVariable[] tvs = type.getTypeParameters();
for (int i = 0; i < typeParameters.length; i++) {
t.put(tvs[i].getName(), typeParameters[i]);
}
}
public void addInputs(Map inputs) {
if (inputs == null) return;
for (Map.Entry entry : inputs.entrySet()) {
addInput(entry.getKey(), entry.getValue());
}
}
public void processTables() {
for (String name : variables.keySet()) {
inputs.remove(name);
}
}
public Map getInputs() {
return inputs;
}
public void setInputs(Map inputs) {
this.inputs = inputs;
}
public List getErrorList() {
return errorList == null ? Collections.emptyList() : errorList;
}
public void setErrorList(List errorList) {
this.errorList = errorList;
}
public void addError(ErrorDetail errorDetail) {
if (errorList == null) errorList = new ArrayList();
else {
for (ErrorDetail detail : errorList) {
if (detail.getMessage().equals(errorDetail.getMessage())
&& detail.getColumn() == errorDetail.getColumn()
&& detail.getLineNumber() == errorDetail.getLineNumber()) {
return;
}
}
}
if (errorDetail.isCritical()) fatalError = true;
errorList.add(errorDetail);
}
public boolean isFatalError() {
return fatalError;
}
public void setFatalError(boolean fatalError) {
this.fatalError = fatalError;
}
public boolean isStrictTypeEnforcement() {
return strictTypeEnforcement;
}
/**
* Enables strict type enforcement -
*
* @param strictTypeEnforcement -
*/
public void setStrictTypeEnforcement(boolean strictTypeEnforcement) {
this.strictTypeEnforcement = strictTypeEnforcement;
}
public boolean isStrongTyping() {
return strongTyping;
}
/**
* Enables strong type enforcement.
*
* @param strongTyping -
*/
public void setStrongTyping(boolean strongTyping) {
if (this.strongTyping = strongTyping) {
// implies strict-type enforcement too
this.strictTypeEnforcement = true;
}
}
public boolean isRetainParserState() {
return retainParserState;
}
public void setRetainParserState(boolean retainParserState) {
this.retainParserState = retainParserState;
}
public Parser getRootParser() {
return rootParser;
}
public void setRootParser(Parser rootParser) {
this.rootParser = rootParser;
}
public String getSourceFile() {
return sourceFile;
}
public void setSourceFile(String sourceFile) {
if (sourceFile != null)
this.sourceFile = sourceFile;
}
public Map getInterceptors() {
return this.parserConfiguration.getInterceptors();
}
public void setInterceptors(Map interceptors) {
this.parserConfiguration.setInterceptors(interceptors);
}
public Map getImports() {
return this.parserConfiguration.getImports();
}
public void setImports(Map imports) {
if (imports == null) return;
Object val;
for (Map.Entry entry : imports.entrySet()) {
if ((val = entry.getValue()) instanceof Class) {
addImport(entry.getKey(), (Class) val);
} else if (val instanceof Method) {
addImport(entry.getKey(), (Method) val);
} else if (val instanceof MethodStub) {
addImport(entry.getKey(), (MethodStub) val);
} else {
throw new RuntimeException("invalid element in imports map: " + entry.getKey() + " (" + val + ")");
}
}
}
private void initVariableVisibility() {
if (variableVisibility == null) {
variableVisibility = new ArrayList>();
}
}
public void pushVariableScope() {
initVariableVisibility();
variableVisibility.add(new HashSet());
}
public void popVariableScope() {
if (variableVisibility != null && !variableVisibility.isEmpty()) {
variableVisibility.remove(variableVisibility.size() - 1);
setLastTypeParameters(null);
}
}
public void makeVisible(String var) {
if (variableVisibility == null || variableVisibility.isEmpty()) {
throw new RuntimeException("no context");
}
getVariableScope().add(var);
}
public Set getVariableScope() {
if (variableVisibility == null || variableVisibility.isEmpty()) {
throw new RuntimeException("no context");
}
return variableVisibility.get(variableVisibility.size() - 1);
}
public boolean isVariableVisible(String var) {
if (variableVisibility == null || variableVisibility.isEmpty()) {
return false;
}
if (AbstractParser.LITERALS.containsKey(var) || hasImport(var)) {
return true;
}
int pos = variableVisibility.size() - 1;
do {
if (variableVisibility.get(pos).contains(var)) {
return true;
}
}
while (pos-- != 0);
return false;
}
public HashMap getVariables() {
return variables;
}
public void setVariables(HashMap variables) {
this.variables = variables;
}
public boolean isCompiled() {
return compiled;
}
public void setCompiled(boolean compiled) {
this.compiled = compiled;
}
public boolean isDebugSymbols() {
return debugSymbols;
}
public void setDebugSymbols(boolean debugSymbols) {
this.debugSymbols = debugSymbols;
}
public boolean isLineMapped(String sourceName) {
return sourceLineLookups != null && sourceLineLookups.containsKey(sourceName);
}
public void initLineMapping(String sourceName, char[] expr) {
if (sourceLineLookups == null) {
sourceLineLookups = new HashMap();
}
sourceLineLookups.put(sourceName, new LineMapper(expr).map());
}
public int getLineFor(String sourceName, int cursor) {
return (sourceLineLookups != null
&& sourceLineLookups.containsKey(sourceName)) ?
sourceLineLookups.get(sourceName).getLineFromCursor(cursor) : -1;
}
public boolean isVisitedLine(String sourceName, int lineNumber) {
return visitedLines != null
&& visitedLines.containsKey(sourceName)
&& visitedLines.get(sourceName).contains(lineNumber);
}
public void visitLine(String sourceName, int lineNumber) {
if (visitedLines == null) {
visitedLines = new HashMap>();
}
if (!visitedLines.containsKey(sourceName)) {
visitedLines.put(sourceName, new TreeSet());
}
visitedLines.get(sourceName).add(lineNumber);
}
public LineLabel getLastLineLabel() {
return lastLineLabel;
}
public LineLabel setLastLineLabel(LineLabel lastLineLabel) {
return this.lastLineLabel = lastLineLabel;
}
public boolean hasImports() {
return parserConfiguration.hasImports();
}
public void declareFunction(Function function) {
if (globalFunctions == null) globalFunctions = new LinkedHashMap();
globalFunctions.put(function.getName(), function);
}
public Function getFunction(String name) {
if (globalFunctions == null) return null;
return globalFunctions.get(name);
}
public Map getFunctions() {
return globalFunctions;
}
public boolean hasFunction(String name) {
return globalFunctions != null && globalFunctions.containsKey(name);
}
public boolean hasFunction() {
return globalFunctions != null && globalFunctions.size() != 0;
}
public void addTypeParameters(String name, Class type) {
if (typeParameters == null) typeParameters = new HashMap>();
Map newPkg = new HashMap();
for (Type t : type.getTypeParameters()) {
newPkg.put(t.toString(), Object.class);
}
typeParameters.put(name, newPkg);
}
public void addTypeParameters(Map> typeParameters) {
if (typeParameters == null) return;
if (this.typeParameters == null) typeParameters = new HashMap>();
Map iMap;
for (Map.Entry> e : typeParameters.entrySet()) {
iMap = new HashMap();
for (Map.Entry ie : e.getValue().entrySet()) {
iMap.put(ie.getKey(), ie.getValue());
}
typeParameters.put(e.getKey(), iMap);
}
}
public Map getTypeParameters(String name) {
if (typeParameters == null) return null;
return typeParameters.get(name);
}
public Type[] getTypeParametersAsArray(String name) {
Class c = (variables != null && variables.containsKey(name)) ? variables.get(name) : inputs.get(name);
if (c == null) return null;
Type[] tp = c.getTypeParameters();
Type[] types = new Type[tp.length];
Map typeVars = getTypeParameters(name);
if (typeVars == null) {
return null;
}
for (int i = 0; i < tp.length; i++) {
types[i] = typeVars.get(tp[i].toString());
}
return types;
}
public boolean isBlockSymbols() {
return blockSymbols;
}
public void setBlockSymbols(boolean blockSymbols) {
this.blockSymbols = blockSymbols;
}
public boolean isVariablesEscape() {
return variablesEscape;
}
public boolean isExecutableCodeReached() {
return executableCodeReached;
}
public void setExecutableCodeReached(boolean executableCodeReached) {
this.executableCodeReached = executableCodeReached;
}
public void optimizationNotify() {
this.optimizationMode = true;
}
public boolean isOptimizerNotified() {
return optimizationMode;
}
private void initIndexedVariables() {
if (indexedInputs == null) indexedInputs = new ArrayList();
if (indexedLocals == null) indexedLocals = new ArrayList();
}
public ArrayList getIndexedInputs() {
initIndexedVariables();
return indexedInputs;
}
public void addIndexedInput(String[] variables) {
initIndexedVariables();
for (String s : variables) {
if (!indexedInputs.contains(s))
indexedInputs.add(s);
}
}
public void addIndexedLocals(String[] variables) {
initIndexedVariables();
for (String s : indexedLocals) {
if (!indexedLocals.contains(s))
indexedLocals.add(s);
}
}
public void addIndexedLocals(Collection variables) {
if (variables == null) return;
initIndexedVariables();
for (String s : variables) {
if (!indexedLocals.contains(s))
indexedLocals.add(s);
}
}
public void addIndexedInput(String variable) {
initIndexedVariables();
if (!indexedInputs.contains(variable)) indexedInputs.add(variable);
}
public void addIndexedInputs(Collection variables) {
if (variables == null) return;
initIndexedVariables();
for (String s : variables) {
if (!indexedInputs.contains(s))
indexedInputs.add(s);
}
}
public int variableIndexOf(String name) {
if (indexedInputs != null) {
int idx = indexedInputs.indexOf(name);
if (idx == -1 && indexedLocals != null) {
idx = indexedLocals.indexOf(name);
if (idx != -1) {
idx += indexedInputs.size();
}
}
return idx;
}
return -1;
}
public boolean hasIndexedInputs() {
return indexedInputs != null && indexedInputs.size() != 0;
}
public boolean isIndexAllocation() {
return indexAllocation;
}
public void setIndexAllocation(boolean indexAllocation) {
this.indexAllocation = indexAllocation;
}
public ParserConfiguration getParserConfiguration() {
return parserConfiguration;
}
public ClassLoader getClassLoader() {
return parserConfiguration.getClassLoader();
}
public Type[] getLastTypeParameters() {
return lastTypeParameters;
}
public void setLastTypeParameters(Type[] lastTypeParameters) {
this.lastTypeParameters = lastTypeParameters;
}
public boolean isAllowBootstrapBypass() {
return parserConfiguration.isAllowBootstrapBypass();
}
public void setAllowBootstrapBypass(boolean allowBootstrapBypass) {
parserConfiguration.setAllowBootstrapBypass(allowBootstrapBypass);
}
public String[] getIndexedVarNames() {
if (indexedInputs == null) return new String[0];
String[] s = new String[indexedInputs.size()];
indexedInputs.toArray(s);
return s;
}
// Introduce some new Fluent API stuff here.
public static ParserContext create() {
return new ParserContext();
}
public ParserContext stronglyTyped() {
setStrongTyping(true);
return this;
}
public ParserContext withInput(String name, Class type) {
addInput(name, type);
return this;
}
public ParserContext withInputs(Map inputs) {
setInputs(inputs);
return this;
}
public ParserContext withTypeParameter(String name, Class type) {
addTypeParameters(name, type);
return this;
}
public ParserContext withTypeParameters(Map> typeParameters) {
addTypeParameters(typeParameters);
return this;
}
public ParserContext withImport(Class clazz) {
addImport(clazz);
return this;
}
public ParserContext withIndexedVars(String[] varNames) {
indexedInputs = new ArrayList();
Collections.addAll(indexedInputs, varNames);
return this;
}
}