com.hazelcast.org.codehaus.janino.ExpressionEvaluator Maven / Gradle / Ivy
/*
* Janino - An embedded Java[TM] compiler
*
* Copyright (c) 2001-2010 Arno Unkrig. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
* following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the
* following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
* products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.hazelcast.org.codehaus.janino;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.hazelcast.org.codehaus.commons.compiler.CompileException;
import com.hazelcast.org.codehaus.commons.compiler.Cookable;
import com.hazelcast.org.codehaus.commons.compiler.ErrorHandler;
import com.hazelcast.org.codehaus.commons.compiler.IClassBodyEvaluator;
import com.hazelcast.org.codehaus.commons.compiler.ICookable;
import com.hazelcast.org.codehaus.commons.compiler.IExpressionEvaluator;
import com.hazelcast.org.codehaus.commons.compiler.IScriptEvaluator;
import com.hazelcast.org.codehaus.commons.compiler.ISimpleCompiler;
import com.hazelcast.org.codehaus.commons.compiler.InternalCompilerException;
import com.hazelcast.org.codehaus.commons.compiler.Location;
import com.hazelcast.org.codehaus.commons.compiler.MultiCookable;
import com.hazelcast.org.codehaus.commons.compiler.WarningHandler;
import com.hazelcast.org.codehaus.commons.nullanalysis.Nullable;
import com.hazelcast.org.codehaus.janino.util.AbstractTraverser;
/**
* This {@link IExpressionEvaluator} is implemented by creating and compiling a temporary compilation unit defining one
* class with one static method with one RETURN statement.
*
* A number of "convenience constructors" exist that execute the set-up steps described for {@link
* IExpressionEvaluator} instantly.
*
*
* If the expression type and the parameters' types are known at compile time, then a "fast" expression evaluator
* can be instantiated through {@link #createFastEvaluator(String, Class, String...)}.
* Expression evaluation is faster than through {@link #evaluate(Object[])}, because it is not done through
* reflection but through direct method invocation.
*
*
* Example:
*
*
* public interface Foo {
* int bar(int a, int b);
* }
* ...
* Foo f = (Foo) ExpressionEvaluator.createFastExpressionEvaluator(
* "a + b", // expression to evaluate
* Foo.class, // interface that describes the expression's signature
* new String[] { "a", "b" }, // the parameters' names
* (ClassLoader) null // Use current thread's context class loader
* );
* System.out.println("1 + 2 = " + f.bar(1, 2)); // Evaluate the expression
*
*
* Notice: The {@code interfaceToImplement} must either be declared {@code public}, or with package scope in the root
* package (i.e. "no" package).
*
*
* On my system (Intel P4, 2 GHz, MS Windows XP, JDK 1.4.1), expression "x + 1" evaluates as follows:
*
*
* Server JVM Client JVM
* Normal EE 23.7 ns 64.0 ns
* Fast EE 31.2 ns 42.2 ns
*
*/
public
class ExpressionEvaluator extends MultiCookable implements IExpressionEvaluator {
// private static final Logger LOGGER = Logger.getLogger(ExpressionEvaluator.class.getName());
// private static final Class> DEFAULT_EXPRESSION_TYPE = Object.class;
// private static final Class>[] ZERO_CLASSES = new Class>[0];
private int sourceVersion = -1;
@Nullable private WarningHandler warningHandler;
private final ScriptEvaluator se = new ScriptEvaluator();
{
this.se.setClassName(IExpressionEvaluator.DEFAULT_CLASS_NAME);
this.se.setDefaultReturnType(IExpressionEvaluator.DEFAULT_EXPRESSION_TYPE);
}
/**
* Equivalent to
*
* ExpressionEvaluator ee = new ExpressionEvaluator();
* ee.setExpressionType(expressionType);
* ee.setParameters(parameterNames, parameterTypes);
* ee.cook(expression);
*
*
* @see #ExpressionEvaluator()
* @see ExpressionEvaluator#setExpressionType(Class)
* @see ScriptEvaluator#setParameters(String[], Class[])
* @see Cookable#cook(String)
*/
public
ExpressionEvaluator(String expression, Class> expressionType, String[] parameterNames, Class>[] parameterTypes)
throws CompileException {
this.setExpressionType(expressionType);
this.setParameters(parameterNames, parameterTypes);
this.cook(expression);
}
/**
* Equivalent to
*
* ExpressionEvaluator ee = new ExpressionEvaluator();
* ee.setExpressionType(expressionType);
* ee.setParameters(parameterNames, parameterTypes);
* ee.setThrownExceptions(thrownExceptions);
* ee.setParentClassLoader(parentClassLoader);
* ee.cook(expression);
*
*
* @see #ExpressionEvaluator()
* @see ExpressionEvaluator#setExpressionType(Class)
* @see ScriptEvaluator#setParameters(String[], Class[])
* @see ScriptEvaluator#setThrownExceptions(Class[])
* @see SimpleCompiler#setParentClassLoader(ClassLoader)
* @see Cookable#cook(String)
*/
public
ExpressionEvaluator(
String expression,
Class> expressionType,
String[] parameterNames,
Class>[] parameterTypes,
Class>[] thrownExceptions,
@Nullable ClassLoader parentClassLoader
) throws CompileException {
this.setExpressionType(expressionType);
this.setParameters(parameterNames, parameterTypes);
this.setThrownExceptions(thrownExceptions);
this.setParentClassLoader(parentClassLoader);
this.cook(expression);
}
/**
* Equivalent to
*
* ExpressionEvaluator ee = new ExpressionEvaluator();
* ee.setExpressionType(expressionType);
* ee.setParameters(parameterNames, parameterTypes);
* ee.setThrownExceptions(thrownExceptions);
* ee.setExtendedClass(extendedClass);
* ee.setImplementedTypes(implementedTypes);
* ee.setParentClassLoader(parentClassLoader);
* ee.cook(expression);
*
*
* @see #ExpressionEvaluator()
* @see ExpressionEvaluator#setExpressionType(Class)
* @see ScriptEvaluator#setParameters(String[], Class[])
* @see ScriptEvaluator#setThrownExceptions(Class[])
* @see ClassBodyEvaluator#setExtendedClass(Class)
* @see ClassBodyEvaluator#setImplementedInterfaces(Class[])
* @see SimpleCompiler#setParentClassLoader(ClassLoader)
* @see Cookable#cook(String)
*/
public
ExpressionEvaluator(
String expression,
Class> expressionType,
String[] parameterNames,
Class>[] parameterTypes,
Class>[] thrownExceptions,
Class> extendedClass,
Class>[] implementedTypes,
@Nullable ClassLoader parentClassLoader
) throws CompileException {
this.setExpressionType(expressionType);
this.setParameters(parameterNames, parameterTypes);
this.setThrownExceptions(thrownExceptions);
this.setExtendedClass(extendedClass);
this.setImplementedInterfaces(implementedTypes);
this.setParentClassLoader(parentClassLoader);
this.cook(expression);
}
/**
* Creates an expression evaluator with the full configurability.
*
* Equivalent to:
*
*
* ExpressionEvaluator ee = new ExpressionEvaluator();
* ee.setClassName(className);
* ee.setExtendedType(extendedType);
* ee.setImplementedTypes(implementedTypes);
* ee.setStaticMethod(staticMethod);
* ee.setExpressionType(expressionType);
* ee.setMethodName(methodName);
* ee.setParameters(parameterNames, parameterTypes);
* ee.setThrownExceptions(thrownExceptions);
* ee.setParentClassLoader(parentClassLoader);
* ee.cook(scanner);
*
*
* @see IExpressionEvaluator
* @see IClassBodyEvaluator#setClassName(String)
* @see IClassBodyEvaluator#setExtendedClass(Class)
* @see IClassBodyEvaluator#setImplementedInterfaces(Class[])
* @see IScriptEvaluator#setStaticMethod(boolean)
* @see IExpressionEvaluator#setExpressionType(Class)
* @see IScriptEvaluator#setMethodName(String)
* @see IScriptEvaluator#setParameters(String[], Class[])
* @see IScriptEvaluator#setThrownExceptions(Class[])
* @see ISimpleCompiler#setParentClassLoader(ClassLoader)
* @see ICookable#cook(Reader)
*/
public
ExpressionEvaluator(
Scanner scanner,
String className,
@Nullable Class> extendedType,
Class>[] implementedTypes,
boolean staticMethod,
Class> expressionType,
String methodName,
String[] parameterNames,
Class>[] parameterTypes,
Class>[] thrownExceptions,
@Nullable ClassLoader parentClassLoader
) throws CompileException, IOException {
this.setClassName(className);
this.setExtendedClass(extendedType);
this.setImplementedInterfaces(implementedTypes);
this.setStaticMethod(staticMethod);
this.setExpressionType(expressionType);
this.setMethodName(methodName);
this.setParameters(parameterNames, parameterTypes);
this.setThrownExceptions(thrownExceptions);
this.setParentClassLoader(parentClassLoader);
this.cook(scanner);
}
public ExpressionEvaluator() {}
@Override public void
setParentClassLoader(@Nullable ClassLoader parentClassLoader) {
this.se.setParentClassLoader(parentClassLoader);
}
@Override public void
setDebuggingInformation(boolean debugSource, boolean debugLines, boolean debugVars) {
this.se.setDebuggingInformation(debugSource, debugLines, debugVars);
}
@Override public void
setSourceVersion(int version) {
this.se.setSourceVersion(version);
this.sourceVersion = version;
}
@Override public void
setTargetVersion(int version) { this.se.setTargetVersion(version); }
@Override public void
setCompileErrorHandler(@Nullable ErrorHandler compileErrorHandler) {
this.se.setCompileErrorHandler(compileErrorHandler);
}
@Override public void
setWarningHandler(@Nullable WarningHandler warningHandler) {
this.se.setWarningHandler(warningHandler);
this.warningHandler = warningHandler;
}
@Override public void
setDefaultImports(String... defaultImports) { this.se.setDefaultImports(defaultImports); }
@Override public String[]
getDefaultImports() { return this.se.getDefaultImports(); }
/**
* @return A reference to the currently effective compilation options; changes to it take
* effect immediately
*/
public EnumSet
options() { return this.se.options(); }
/**
* Sets the options for all future compilations.
*/
public ExpressionEvaluator
options(EnumSet options) {
this.se.options(options);
return this;
}
@Override public void
setDefaultExpressionType(Class> defaultExpressionType) { this.se.setDefaultReturnType(defaultExpressionType); }
@Override public Class>
getDefaultExpressionType() { return this.se.getDefaultReturnType(); }
@Override public void
setImplementedInterfaces(Class>[] implementedTypes) { this.se.setImplementedInterfaces(implementedTypes); }
@Override public void
setReturnType(Class> returnType) { this.se.setReturnType(returnType); }
@Override public void
setExpressionType(Class> expressionType) { this.se.setReturnType(expressionType); }
@Override public void
setExpressionTypes(Class>[] expressionTypes) { this.se.setReturnTypes(expressionTypes); }
@Override public void
setOverrideMethod(boolean overrideMethod) { this.se.setOverrideMethod(overrideMethod); }
@Override public void
setOverrideMethod(boolean[] overrideMethod) { this.se.setOverrideMethod(overrideMethod); }
@Override public void
setParameters(String[] parameterNames, Class>[] parameterTypes) {
this.se.setParameters(parameterNames, parameterTypes);
}
@Override public void
setParameters(String[][] parameterNames, Class>[][] parameterTypes) {
this.se.setParameters(parameterNames, parameterTypes);
}
@Override public void
setClassName(String className) { this.se.setClassName(className); }
@Override public void
setExtendedClass(@Nullable Class> extendedType) { this.se.setExtendedClass(extendedType); }
@Override public void
setStaticMethod(boolean staticMethod) { this.se.setStaticMethod(staticMethod); }
@Override public void
setStaticMethod(boolean[] staticMethod) { this.se.setStaticMethod(staticMethod); }
@Override public void
setMethodName(String methodName) { this.se.setMethodName(methodName); }
@Override public void
setMethodNames(String[] methodNames) { this.se.setMethodNames(methodNames); }
@Override public void
setThrownExceptions(Class>[] thrownExceptions) { this.se.setThrownExceptions(thrownExceptions); }
@Override public void
setThrownExceptions(Class>[][] thrownExceptions) { this.se.setThrownExceptions(thrownExceptions); }
// /**
// * @throws IllegalArgumentException count is different from previous invocations of
// * this method
// */
// public void
// setExpressionCount(int count) {
//
// Expression[] ss = this.expressions;
//
// if (ss == null) {
// this.expressions = (ss = new Expression[count]);
// for (int i = 0; i < count; i++) {
// ss[i] = new Expression(ScriptEvaluator.DEFAULT_METHOD_NAME.replace("*", Integer.toString(i)));
// }
// } else {
// if (count != ss.length) {
// throw new IllegalArgumentException(
// "Inconsistent script count; previously " + ss.length + ", now " + count
// );
// }
// }
// }
// private Expression
// getExpression(int index) {
// if (this.expressions != null) return this.expressions[index];
// throw new IllegalStateException("\"getScript()\" invoked befor \"setScriptCount()\"");
// }
@Override public Method
getMethod() { return this.se.getMethod(); }
@Override public Method
getMethod(int idx) { return this.se.getMethod(idx); }
/**
* @return The generated method
* @throws IllegalStateException The {@link ScriptEvaluator} has not yet be cooked
*/
@Override public Method[]
getResult() { return this.se.getResult(); }
@Override public void
cook(@Nullable String fileName, Reader reader) throws CompileException, IOException {
this.cook(new Scanner(fileName, reader));
}
@Override public void
cook(String[] fileNames, Reader[] readers) throws CompileException, IOException {
final int count = fileNames.length;
Scanner[] scanners = new Scanner[count];
for (int i = 0; i < count; i++) scanners[i] = new Scanner(fileNames[i], readers[i]);
this.cook(scanners);
}
public final void
cook(Scanner scanner) throws CompileException, IOException { this.cook(new Scanner[] { scanner }); }
/**
* Like {@link #cook(Scanner)}, but cooks a set of scripts into one class. Notice that if any of
* the scripts causes trouble, the entire compilation will fail. If you need to report which of the
* scripts causes the exception, you may want to use the {@code fileName} argument of {@link
* Scanner#Scanner(String, Reader)} to distinguish between the individual token sources.
*
* On a 2 GHz Intel Pentium Core Duo under Windows XP with an IBM 1.4.2 JDK, compiling 10000 expressions "a + b"
* (integer) takes about 4 seconds and 56 MB of main memory. The generated class file is 639203 bytes large.
*
*
* The number and the complexity of the scripts is restricted by the Limitations of the Java
* Virtual Machine, where the most limiting factor is the 64K entries limit of the constant pool. Since every
* method with a distinct name requires one entry there, you can define at best 32K (very simple) scripts.
*
*
* If and only if the number of scanners is one, then that single script may contain leading IMPORT directives.
*
*
* @throws IllegalStateException Any of the preceding {@code set...()} had an array size different from that of
* {@code scanners}
*/
public final void
cook(Scanner... scanners) throws CompileException, IOException {
Parser[] parsers = new Parser[scanners.length];
for (int i = 0; i < scanners.length; ++i) {
parsers[i] = new Parser(scanners[i]);
parsers[i].setSourceVersion(this.sourceVersion);
parsers[i].setWarningHandler(this.warningHandler);
}
this.cook(parsers);
}
/**
* @see #cook(Scanner[])
*/
public final void
cook(Parser... parsers) throws CompileException, IOException {
int count = parsers.length;
this.se.setScriptCount(count);
String fileName = parsers.length >= 1 ? parsers[0].getScanner().getFileName() : null;
// Parse import declarations.
final Java.AbstractCompilationUnit.ImportDeclaration[]
importDeclarations = this.se.parseImports(parsers.length == 1 ? parsers[0] : null);
Java.BlockStatement[][] statementss = new Java.BlockStatement[count][];
Java.MethodDeclarator[][] localMethodss = new Java.MethodDeclarator[count][];
// Create methods with one block each.
for (int i = 0; i < parsers.length; ++i) {
Class> et = this.se.getReturnType(i);
Parser parser = parsers[i];
// Parse the expression.
Java.Rvalue value = parser.parseExpression();
Java.BlockStatement statement;
if (et == void.class) {
statement = new Java.ExpressionStatement(value);
} else {
statement = new Java.ReturnStatement(parser.location(), value);
}
if (!parser.peek(TokenType.END_OF_INPUT)) {
throw new CompileException("Unexpected token \"" + parser.peek() + "\"", parser.location());
}
statementss[i] = new Java.BlockStatement[] { statement };
localMethodss[i] = new Java.MethodDeclarator[0];
}
this.se.cook(fileName, importDeclarations, statementss, localMethodss);
}
/**
* Converts an array of {@link Class}es into an array of{@link Java.Type}s.
*/
protected Java.Type[]
classesToTypes(Location location, @Nullable Class>... classes) {
if (classes == null) return new Java.Type[0];
Java.Type[] types = new Java.Type[classes.length];
for (int i = 0; i < classes.length; ++i) {
types[i] = this.classToType(location, classes[i]);
}
return types;
}
/**
* Wraps a reflection {@link Class} in a {@link Java.Type} object.
*/
@Nullable protected Java.Type
optionalClassToType(final Location location, @Nullable final Class> clazz) {
return this.se.optionalClassToType(location, clazz);
}
/**
* Wraps a reflection {@link Class} in a {@link Java.Type} object.
*/
protected Java.Type
classToType(final Location location, final Class> clazz) { return this.se.classToType(location, clazz); }
@Override @Nullable public Object
evaluate(@Nullable Object... arguments) throws InvocationTargetException { return this.evaluate(0, arguments); }
@Override @Nullable public Object
evaluate(int idx, @Nullable Object... arguments) throws InvocationTargetException {
Method method = this.getMethod(idx);
try {
return method.invoke(null, arguments);
} catch (IllegalAccessException ex) {
throw new InternalCompilerException(ex.toString(), ex);
}
}
@Override public Class>
getClazz() { return this.se.getClazz(); }
@Override public Map
getBytecodes() { return this.se.getBytecodes(); }
/**
* @deprecated Use {@link #createFastEvaluator(String, Class, String[])} instead:
*/
@Deprecated public static Object
createFastExpressionEvaluator(
String expression,
Class> interfaceToImplement,
String[] parameterNames,
@Nullable ClassLoader parentClassLoader
) throws CompileException {
try {
return ExpressionEvaluator.createFastExpressionEvaluator(
new Scanner(null, new StringReader(expression)), // scanner
IExpressionEvaluator.DEFAULT_CLASS_NAME, // className
null, // extendedType
interfaceToImplement, // interfaceToImplement
parameterNames, // parameterNames
null // parentClassLoader
);
} catch (IOException ioe) {
final AssertionError ae = new AssertionError("IOException despite StringReader");
ae.initCause(ioe);
throw ae;
}
}
/**
* @deprecated Use {@link #createFastEvaluator(Reader, Class, String[])} instead
*/
@Deprecated public static Object
createFastExpressionEvaluator(
Scanner scanner,
String className,
@Nullable Class> extendedType,
Class> interfaceToImplement,
String[] parameterNames,
@Nullable ClassLoader parentClassLoader
) throws CompileException, IOException {
return ExpressionEvaluator.createFastExpressionEvaluator(
scanner, // scanner
new String[0], // defaultImports
className, // className
extendedType, // extendedType
interfaceToImplement, // interfaceToImplement
parameterNames, // parameterNames
parentClassLoader // parentClassLoader
);
}
/**
* @deprecated Use {@link #createFastEvaluator(Reader, Class, String[])} instead
*/
@Deprecated public static Object
createFastExpressionEvaluator(
Scanner scanner,
String[] defaultImports,
String className,
@Nullable Class> extendedType,
Class> interfaceToImplement,
String[] parameterNames,
@Nullable ClassLoader parentClassLoader
) throws CompileException, IOException {
ExpressionEvaluator ee = new ExpressionEvaluator();
ee.setClassName(className);
ee.setExtendedClass(extendedType);
ee.setDefaultImports(defaultImports);
ee.setParentClassLoader(parentClassLoader);
return ee.createFastEvaluator(scanner, interfaceToImplement, parameterNames);
}
@Override public T
createFastEvaluator(Reader reader, Class extends T> interfaceToImplement, String... parameterNames)
throws CompileException, IOException {
return this.createFastEvaluator(new Scanner(null, reader), interfaceToImplement, parameterNames);
}
@Override public T
createFastEvaluator(String script, Class extends T> interfaceToImplement, String... parameterNames)
throws CompileException {
try {
return this.createFastEvaluator(
new StringReader(script),
interfaceToImplement,
parameterNames
);
} catch (IOException ex) {
throw new InternalCompilerException("IOException despite StringReader", ex);
}
}
/**
* Notice: This method is not declared in {@link IScriptEvaluator}, and is hence only available in this
* implementation of {@code com.hazelcast.org.codehaus.commons.compiler}. To be independent from this particular
* implementation, try to switch to {@link #createFastEvaluator(Reader, Class, String[])}.
*
* @param scanner Source of tokens to read
* @see #createFastEvaluator(Reader, Class, String[])
*/
public T
createFastEvaluator(Scanner scanner, Class interfaceToImplement, String... parameterNames)
throws CompileException, IOException {
if (!interfaceToImplement.isInterface()) {
throw new InternalCompilerException("\"" + interfaceToImplement + "\" is not an interface");
}
Method methodToImplement;
{
Method[] methods = interfaceToImplement.getDeclaredMethods();
if (methods.length != 1) {
throw new InternalCompilerException(
"Interface \""
+ interfaceToImplement
+ "\" must declare exactly one method"
);
}
methodToImplement = methods[0];
}
this.setImplementedInterfaces(new Class[] { interfaceToImplement });
this.setOverrideMethod(true);
this.setStaticMethod(false);
if (this instanceof IExpressionEvaluator) {
// Must not call "IExpressionEvaluator.setReturnType()".
((IExpressionEvaluator) this).setExpressionType(methodToImplement.getReturnType());
} else {
this.setExpressionType(methodToImplement.getReturnType());
}
this.setMethodName(methodToImplement.getName());
this.setParameters(parameterNames, methodToImplement.getParameterTypes());
this.setThrownExceptions(methodToImplement.getExceptionTypes());
this.cook(scanner);
@SuppressWarnings("unchecked") Class extends T>
actualClass = (Class extends T>) this.getMethod().getDeclaringClass();
try {
return actualClass.newInstance();
} catch (InstantiationException e) {
// SNO - Declared class is always non-abstract.
throw new InternalCompilerException(e.toString(), e);
} catch (IllegalAccessException e) {
// SNO - interface methods are always PUBLIC.
throw new InternalCompilerException(e.toString(), e);
}
}
/**
* Guess the names of the parameters used in the given expression. The strategy is to look
* at all "ambiguous names" in the expression (e.g. in "a.b.c.d()", the ambiguous name
* is "a.b.c"), and then at the first components of the ambiguous name.
*
* - If any component starts with an upper-case letter, then ambiguous name is assumed to be a type name.
* - Otherwise, it is assumed to be a parameter name.
*
*
* @see Scanner#Scanner(String, Reader)
*/
public static String[]
guessParameterNames(Scanner scanner) throws CompileException, IOException {
Parser parser = new Parser(scanner);
// Impossible, because this method is STATIC:
//parser.setSourceVersion(this.sourceVersion);
//parser.setWarningHandler(this.warningHandler);
// Eat optional leading import declarations.
while (parser.peek("import")) parser.parseImportDeclaration();
// Parse the expression.
Java.Rvalue rvalue = parser.parseExpression();
if (!parser.peek(TokenType.END_OF_INPUT)) {
throw new CompileException("Unexpected token \"" + parser.peek() + "\"", scanner.location());
}
// Traverse the expression for ambiguous names and guess which of them are parameter names.
final Set parameterNames = new HashSet();
new AbstractTraverser() {
@Override public void
traverseAmbiguousName(Java.AmbiguousName an) {
// If any of the components starts with an upper-case letter, then the ambiguous
// name is most probably a type name, e.g. "System.out" or "java.lang.System.out".
for (String identifier : an.identifiers) {
if (Character.isUpperCase(identifier.charAt(0))) return;
}
// It's most probably a parameter name (although it could be a field name as well).
parameterNames.add(an.identifiers[0]);
}
}.visitAtom(rvalue);
return (String[]) parameterNames.toArray(new String[parameterNames.size()]);
}
}