All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hazelcast.shaded.org.codehaus.janino.ExpressionEvaluator Maven / Gradle / Ivy

There is a newer version: 5.5.0
Show newest version

/*
 * 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.shaded.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.shaded.org.codehaus.commons.compiler.CompileException;
import com.hazelcast.shaded.org.codehaus.commons.compiler.Cookable;
import com.hazelcast.shaded.org.codehaus.commons.compiler.ErrorHandler;
import com.hazelcast.shaded.org.codehaus.commons.compiler.IClassBodyEvaluator;
import com.hazelcast.shaded.org.codehaus.commons.compiler.ICookable;
import com.hazelcast.shaded.org.codehaus.commons.compiler.IExpressionEvaluator;
import com.hazelcast.shaded.org.codehaus.commons.compiler.IScriptEvaluator;
import com.hazelcast.shaded.org.codehaus.commons.compiler.ISimpleCompiler;
import com.hazelcast.shaded.org.codehaus.commons.compiler.InternalCompilerException;
import com.hazelcast.shaded.org.codehaus.commons.compiler.Location;
import com.hazelcast.shaded.org.codehaus.commons.compiler.MultiCookable;
import com.hazelcast.shaded.org.codehaus.commons.compiler.WarningHandler;
import com.hazelcast.shaded.org.codehaus.commons.nullanalysis.Nullable;
import com.hazelcast.shaded.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 JVMClient JVM
Normal EE23.7 ns64.0 ns
Fast EE31.2 ns42.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); } @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 interfaceToImplement, String... parameterNames) throws CompileException, IOException { return this.createFastEvaluator(new Scanner(null, reader), interfaceToImplement, parameterNames); } @Override public T createFastEvaluator(String script, Class 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 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 actualClass = (Class) 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()]); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy