001: /*
002: * Janino - An embedded Java[TM] compiler
003: *
004: * Copyright (c) 2006, Arno Unkrig
005: * All rights reserved.
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions
009: * are met:
010: *
011: * 1. Redistributions of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: * 2. Redistributions in binary form must reproduce the above
014: * copyright notice, this list of conditions and the following
015: * disclaimer in the documentation and/or other materials
016: * provided with the distribution.
017: * 3. The name of the author may not be used to endorse or promote
018: * products derived from this software without specific prior
019: * written permission.
020: *
021: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
022: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
023: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
024: * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
025: * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
026: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
027: * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
028: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
029: * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
030: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
031: * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
032: */
033:
034: package org.codehaus.janino;
035:
036: import java.io.*;
037: import java.lang.reflect.*;
038:
039: /**
040: * A script evaluator that executes a script in Java<sup>TM</sup> bytecode.
041: * <p>
042: * The syntax of the script to compile is a sequence of import declarations followed by a
043: * sequence of statements, as defined in the
044: * <a href="http://java.sun.com/docs/books/jls/second_edition">Java Language Specification, 2nd
045: * edition</a>, sections
046: * <a href="http://java.sun.com/docs/books/jls/second_edition/html/packages.doc.html#70209">7.5</a>
047: * and
048: * <a href="http://java.sun.com/docs/books/jls/second_edition/html/statements.doc.html#101241">14</a>.
049: * <p>
050: * Example:
051: * <pre>
052: * import java.text.*;
053: *
054: * System.out.println("HELLO");
055: * System.out.println(new DecimalFormat("####,###.##").format(a));
056: * </pre>
057: * (Notice that this expression refers to a parameter "a", as explained below.)
058: * <p>
059: * The script may complete abnormally, e.g. through a RETURN statement:
060: * <pre>
061: * if (a == null) {
062: * System.out.println("Oops!");
063: * return;
064: * }
065: * </pre>
066: * Optionally, the script may be declared with a non-void return type. In this case, the last
067: * statement of the script must be a RETURN statement (or a THROW statement), and all RETURN
068: * statements in the script must return a value with the given type.
069: * <p>
070: * The script evaluator is implemented by creating and compiling a temporary compilation unit
071: * defining one class with one method the body of which consists of the statements of the
072: * script.
073: * <p>
074: * To set up a {@link ScriptEvaluator} object, proceed as follows:
075: * <ol>
076: * <li>
077: * Create the {@link ScriptEvaluator} using {@link #ScriptEvaluator()}
078: * <li>
079: * Configure the {@link ScriptEvaluator} by calling any of the following methods:
080: * <ul>
081: * <li>{@link #setReturnType(Class)}
082: * <li>{@link #setParameters(String[], Class[])}
083: * <li>{@link #setThrownExceptions(Class[])}
084: * <li>{@link org.codehaus.janino.SimpleCompiler#setParentClassLoader(ClassLoader)}
085: * <li>{@link org.codehaus.janino.ClassBodyEvaluator#setDefaultImports(String[])}
086: * </ul>
087: * <li>
088: * Call any of the {@link org.codehaus.janino.Cookable#cook(Scanner)} methods to scan,
089: * parse, compile and load the script into the JVM.
090: * </ol>
091: * Alternatively, a number of "convenience constructors" exist that execute the steps described
092: * above instantly.
093: * <p>
094: * After the {@link ScriptEvaluator} object is created, the script can be executed as often with
095: * different parameter values (see {@link #evaluate(Object[])}). This execution is very fast,
096: * compared to the compilation.
097: * <p>
098: * The more elaborate constructors of {@link ScriptEvaluator} also allow for the specification
099: * of the name of the generated class, the class it extends, the interfaces it implements, the
100: * name of the method that executes the script, the exceptions that this method is allowed
101: * to throw, and the {@link ClassLoader} that is used to define the generated
102: * class and to load classes referenced by the expression. This degree of flexibility is usually
103: * not required; the most commonly used constructor is
104: * {@link #ScriptEvaluator(String, Class, String[], Class[])}.
105: */
106: public class ScriptEvaluator extends ClassBodyEvaluator {
107: public static final String DEFAULT_METHOD_NAME = "eval";
108: public static final String[] ZERO_STRINGS = new String[0];
109:
110: private boolean staticMethod = true;
111: private Class returnType = void.class;
112: private String methodName = ScriptEvaluator.DEFAULT_METHOD_NAME;
113: private String[] parameterNames = ScriptEvaluator.ZERO_STRINGS;
114: private Class[] parameterTypes = ClassBodyEvaluator.ZERO_CLASSES;
115: private Class[] thrownExceptions = ClassBodyEvaluator.ZERO_CLASSES;
116:
117: private Method method = null; // null=uncooked
118:
119: /**
120: * Equivalent to<pre>
121: * ScriptEvaluator se = new ScriptEvaluator();
122: * se.cook(script);</pre>
123: *
124: * @see #ScriptEvaluator()
125: * @see Cookable#cook(String)
126: */
127: public ScriptEvaluator(String script) throws CompileException,
128: Parser.ParseException, Scanner.ScanException {
129: this .cook(script);
130: }
131:
132: /**
133: * Equivalent to<pre>
134: * ScriptEvaluator se = new ScriptEvaluator();
135: * se.setReturnType(returnType);
136: * se.cook(script);</pre>
137: *
138: * @see #ScriptEvaluator()
139: * @see #setReturnType(Class)
140: * @see Cookable#cook(String)
141: */
142: public ScriptEvaluator(String script, Class returnType)
143: throws CompileException, Parser.ParseException,
144: Scanner.ScanException {
145: this .setReturnType(returnType);
146: this .cook(script);
147: }
148:
149: /**
150: * Equivalent to<pre>
151: * ScriptEvaluator se = new ScriptEvaluator();
152: * se.setReturnType(returnType);
153: * se.setParameters(parameterNames, parameterTypes);
154: * se.cook(script);</pre>
155: *
156: * @see #ScriptEvaluator()
157: * @see #setReturnType(Class)
158: * @see #setParameters(String[], Class[])
159: * @see Cookable#cook(String)
160: */
161: public ScriptEvaluator(String script, Class returnType,
162: String[] parameterNames, Class[] parameterTypes)
163: throws CompileException, Parser.ParseException,
164: Scanner.ScanException {
165: this .setReturnType(returnType);
166: this .setParameters(parameterNames, parameterTypes);
167: this .cook(script);
168: }
169:
170: /**
171: * Equivalent to<pre>
172: * ScriptEvaluator se = new ScriptEvaluator();
173: * se.setReturnType(returnType);
174: * se.setParameters(parameterNames, parameterTypes);
175: * se.setThrownExceptions(thrownExceptions);
176: * se.cook(script);</pre>
177: *
178: * @see #ScriptEvaluator()
179: * @see #setReturnType(Class)
180: * @see #setParameters(String[], Class[])
181: * @see #setThrownExceptions(Class[])
182: * @see Cookable#cook(String)
183: */
184: public ScriptEvaluator(String script, Class returnType,
185: String[] parameterNames, Class[] parameterTypes,
186: Class[] thrownExceptions) throws CompileException,
187: Parser.ParseException, Scanner.ScanException {
188: this .setReturnType(returnType);
189: this .setParameters(parameterNames, parameterTypes);
190: this .setThrownExceptions(thrownExceptions);
191: this .cook(script);
192: }
193:
194: /**
195: * Equivalent to<pre>
196: * ScriptEvaluator se = new ScriptEvaluator();
197: * se.setReturnType(returnType);
198: * se.setParameters(parameterNames, parameterTypes);
199: * se.setThrownExceptions(thrownExceptions);
200: * se.setParentClassLoader(optionalParentClassLoader);
201: * se.cook(optionalFileName, is);</pre>
202: *
203: * @see #ScriptEvaluator()
204: * @see #setReturnType(Class)
205: * @see #setParameters(String[], Class[])
206: * @see #setThrownExceptions(Class[])
207: * @see SimpleCompiler#setParentClassLoader(ClassLoader)
208: * @see Cookable#cook(String, InputStream)
209: */
210: public ScriptEvaluator(String optionalFileName, InputStream is,
211: Class returnType, String[] parameterNames,
212: Class[] parameterTypes, Class[] thrownExceptions,
213: ClassLoader optionalParentClassLoader // null = use current thread's context class loader
214: ) throws CompileException, Parser.ParseException,
215: Scanner.ScanException, IOException {
216: this .setReturnType(returnType);
217: this .setParameters(parameterNames, parameterTypes);
218: this .setThrownExceptions(thrownExceptions);
219: this .setParentClassLoader(optionalParentClassLoader);
220: this .cook(optionalFileName, is);
221: }
222:
223: /**
224: * Equivalent to<pre>
225: * ScriptEvaluator se = new ScriptEvaluator();
226: * se.setReturnType(returnType);
227: * se.setParameters(parameterNames, parameterTypes);
228: * se.setThrownExceptions(thrownExceptions);
229: * se.setParentClassLoader(optionalParentClassLoader);
230: * se.cook(reader);</pre>
231: *
232: * @see #ScriptEvaluator()
233: * @see #setReturnType(Class)
234: * @see #setParameters(String[], Class[])
235: * @see #setThrownExceptions(Class[])
236: * @see SimpleCompiler#setParentClassLoader(ClassLoader)
237: * @see Cookable#cook(String, Reader)
238: */
239: public ScriptEvaluator(String optionalFileName, Reader reader,
240: Class returnType, String[] parameterNames,
241: Class[] parameterTypes, Class[] thrownExceptions,
242: ClassLoader optionalParentClassLoader // null = use current thread's context class loader
243: ) throws CompileException, Parser.ParseException,
244: Scanner.ScanException, IOException {
245: this .setReturnType(returnType);
246: this .setParameters(parameterNames, parameterTypes);
247: this .setThrownExceptions(thrownExceptions);
248: this .setParentClassLoader(optionalParentClassLoader);
249: this .cook(optionalFileName, reader);
250: }
251:
252: /**
253: * Equivalent to<pre>
254: * ScriptEvaluator se = new ScriptEvaluator();
255: * se.setReturnType(returnType);
256: * se.setParameters(parameterNames, parameterTypes);
257: * se.setThrownExceptions(thrownExceptions);
258: * se.setParentClassLoader(optionalParentClassLoader);
259: * se.cook(scanner);</pre>
260: *
261: * @see #ScriptEvaluator()
262: * @see #setReturnType(Class)
263: * @see #setParameters(String[], Class[])
264: * @see #setThrownExceptions(Class[])
265: * @see SimpleCompiler#setParentClassLoader(ClassLoader)
266: * @see Cookable#cook(Scanner)
267: */
268: public ScriptEvaluator(Scanner scanner, Class returnType,
269: String[] parameterNames, Class[] parameterTypes,
270: Class[] thrownExceptions,
271: ClassLoader optionalParentClassLoader // null = use current thread's context class loader
272: ) throws CompileException, Parser.ParseException,
273: Scanner.ScanException, IOException {
274: this .setReturnType(returnType);
275: this .setParameters(parameterNames, parameterTypes);
276: this .setThrownExceptions(thrownExceptions);
277: this .setParentClassLoader(optionalParentClassLoader);
278: this .cook(scanner);
279: }
280:
281: /**
282: * Equivalent to<pre>
283: * ScriptEvaluator se = new ScriptEvaluator();
284: * se.setExtendedType(optionalExtendedType);
285: * se.setImplementedTypes(implementedTypes);
286: * se.setReturnType(returnType);
287: * se.setParameters(parameterNames, parameterTypes);
288: * se.setThrownExceptions(thrownExceptions);
289: * se.setParentClassLoader(optionalParentClassLoader);
290: * se.cook(scanner);</pre>
291: *
292: * @see #ScriptEvaluator()
293: * @see ClassBodyEvaluator#setExtendedType(Class)
294: * @see ClassBodyEvaluator#setImplementedTypes(Class[])
295: * @see #setReturnType(Class)
296: * @see #setParameters(String[], Class[])
297: * @see #setThrownExceptions(Class[])
298: * @see SimpleCompiler#setParentClassLoader(ClassLoader)
299: * @see Cookable#cook(Scanner)
300: */
301: public ScriptEvaluator(Scanner scanner, Class optionalExtendedType,
302: Class[] implementedTypes, Class returnType,
303: String[] parameterNames, Class[] parameterTypes,
304: Class[] thrownExceptions,
305: ClassLoader optionalParentClassLoader // null = use current thread's context class loader
306: ) throws CompileException, Parser.ParseException,
307: Scanner.ScanException, IOException {
308: this .setExtendedType(optionalExtendedType);
309: this .setImplementedTypes(implementedTypes);
310: this .setReturnType(returnType);
311: this .setParameters(parameterNames, parameterTypes);
312: this .setThrownExceptions(thrownExceptions);
313: this .setParentClassLoader(optionalParentClassLoader);
314: this .cook(scanner);
315: }
316:
317: /**
318: * Equivalent to<pre>
319: * ScriptEvaluator se = new ScriptEvaluator();
320: * se.setClassName(className);
321: * se.setExtendedType(optionalExtendedType);
322: * se.setImplementedTypes(implementedTypes);
323: * se.setStaticMethod(staticMethod);
324: * se.setReturnType(returnType);
325: * se.setMethodName(methodName);
326: * se.setParameters(parameterNames, parameterTypes);
327: * se.setThrownExceptions(thrownExceptions);
328: * se.setParentClassLoader(optionalParentClassLoader);
329: * se.cook(scanner);</pre>
330: *
331: * @see #ScriptEvaluator()
332: * @see ClassBodyEvaluator#setClassName(String)
333: * @see ClassBodyEvaluator#setExtendedType(Class)
334: * @see ClassBodyEvaluator#setImplementedTypes(Class[])
335: * @see #setStaticMethod(boolean)
336: * @see #setReturnType(Class)
337: * @see #setMethodName(String)
338: * @see #setParameters(String[], Class[])
339: * @see #setThrownExceptions(Class[])
340: * @see SimpleCompiler#setParentClassLoader(ClassLoader)
341: * @see Cookable#cook(Scanner)
342: */
343: public ScriptEvaluator(Scanner scanner, String className,
344: Class optionalExtendedType, Class[] implementedTypes,
345: boolean staticMethod, Class returnType, String methodName,
346: String[] parameterNames, Class[] parameterTypes,
347: Class[] thrownExceptions,
348: ClassLoader optionalParentClassLoader // null = use current thread's context class loader
349: ) throws Scanner.ScanException, Parser.ParseException,
350: CompileException, IOException {
351: this .setClassName(className);
352: this .setExtendedType(optionalExtendedType);
353: this .setImplementedTypes(implementedTypes);
354: this .setStaticMethod(staticMethod);
355: this .setReturnType(returnType);
356: this .setMethodName(methodName);
357: this .setParameters(parameterNames, parameterTypes);
358: this .setThrownExceptions(thrownExceptions);
359: this .setParentClassLoader(optionalParentClassLoader);
360: this .cook(scanner);
361: }
362:
363: public ScriptEvaluator() {
364: }
365:
366: /**
367: * Define whether the generated method should be STATIC or not. Defaults to <code>true</code>.
368: */
369: public void setStaticMethod(boolean staticMethod) {
370: this .staticMethod = staticMethod;
371: }
372:
373: /**
374: * Define the return type of the generated method. Defaults to <code>void.class</code>.
375: */
376: public void setReturnType(Class returnType) {
377: this .returnType = returnType;
378: }
379:
380: /**
381: * Define the name of the generated method. Defaults to {@link #DEFAULT_METHOD_NAME}.
382: */
383: public void setMethodName(String methodName) {
384: this .methodName = methodName;
385: }
386:
387: /**
388: * Define the names and types of the parameters of the generated method.
389: */
390: public void setParameters(String[] parameterNames,
391: Class[] parameterTypes) {
392: if (parameterNames.length != parameterTypes.length)
393: throw new IllegalArgumentException(
394: "Parameter names and types counts do not match");
395: this .parameterNames = parameterNames;
396: this .parameterTypes = parameterTypes;
397: }
398:
399: /**
400: * Define the exceptions that the generated method may throw.
401: */
402: public void setThrownExceptions(Class[] thrownExceptions) {
403: if (thrownExceptions == null)
404: throw new NullPointerException(
405: "Zero thrown exceptions must be specified as \"new Class[0]\", not \"null\"");
406: this .thrownExceptions = thrownExceptions;
407: }
408:
409: protected void internalCook(Scanner scanner)
410: throws CompileException, Parser.ParseException,
411: Scanner.ScanException, IOException {
412: Java.CompilationUnit compilationUnit = this
413: .makeCompilationUnit(scanner);
414:
415: // Create class, method and block.
416: Java.Block block = this .addClassMethodBlockDeclaration(scanner
417: .location(), // location
418: compilationUnit, // compilationUnit
419: this .returnType // returnType
420: );
421:
422: // Parse block statements.
423: Parser parser = new Parser(scanner);
424: while (!scanner.peek().isEOF()) {
425: block.addStatement(parser.parseBlockStatement());
426: }
427:
428: this .compileToMethod(compilationUnit);
429: }
430:
431: protected void compileToMethod(Java.CompilationUnit compilationUnit)
432: throws CompileException {
433:
434: // Compile and load the compilation unit.
435: Class c = this .compileToClass(compilationUnit, // compilationUnit
436: DebuggingInformation.DEFAULT_DEBUGGING_INFORMATION, // debuggingInformation
437: this .className // className
438: );
439:
440: // Find the script method by name.
441: try {
442: this .method = c.getMethod(this .methodName,
443: this .parameterTypes);
444: } catch (NoSuchMethodException ex) {
445: throw new RuntimeException(
446: "SNO: Loaded class does not declare method \""
447: + this .methodName + "\"");
448: }
449: }
450:
451: /**
452: * To the given {@link Java.CompilationUnit}, add
453: * <ul>
454: * <li>A package member class declaration with the given name, superclass and interfaces
455: * <li>A public method declaration with the given return type, name, parameter
456: * names and values and thrown exceptions
457: * <li>A block
458: * </ul>
459: * @param location
460: * @param compilationUnit
461: * @param className
462: * @param optionalExtendedType (null == {@link Object})
463: * @param implementedTypes
464: * @param staticMethod Whether the method should be declared "static"
465: * @param returnType Return type of the declared method
466: * @param methodName
467: * @param parameterNames
468: * @param parameterTypes
469: * @param thrownExceptions
470: * @return The created {@link Java.Block} object
471: * @throws Parser.ParseException
472: */
473: protected Java.Block addClassMethodBlockDeclaration(
474: Location location, Java.CompilationUnit compilationUnit,
475: Class returnType) throws Parser.ParseException {
476: if (this .parameterNames.length != this .parameterTypes.length)
477: throw new RuntimeException(
478: "Lengths of \"parameterNames\" and \"parameterTypes\" do not match");
479:
480: // Add class declaration.
481: Java.ClassDeclaration cd = this
482: .addPackageMemberClassDeclaration(location,
483: compilationUnit);
484:
485: // Add method declaration.
486: Java.Block b = new Java.Block(location);
487: Java.FunctionDeclarator.FormalParameter[] fps = new Java.FunctionDeclarator.FormalParameter[this .parameterNames.length];
488: for (int i = 0; i < fps.length; ++i) {
489: fps[i] = new Java.FunctionDeclarator.FormalParameter(
490: location, // location
491: true, // finaL
492: this .classToType(location, this .parameterTypes[i]), // type
493: this .parameterNames[i] // name
494: );
495: }
496: Java.MethodDeclarator md = new Java.MethodDeclarator(location, // location
497: null, // optionalDocComment
498: ( // modifiers
499: this .staticMethod ? (short) (Mod.PUBLIC | Mod.STATIC)
500: : (short) Mod.PUBLIC), this .classToType(
501: location, returnType), // type
502: this .methodName, // name
503: fps, // formalParameters
504: this .classesToTypes(location, this .thrownExceptions), // thrownExceptions
505: b // optionalBody
506: );
507: cd.addDeclaredMethod(md);
508:
509: return b;
510: }
511:
512: /**
513: * Simplified version of
514: * {@link #createFastScriptEvaluator(Scanner, Class, String[], ClassLoader)}.
515: *
516: * @param script Contains the sequence of script tokens
517: * @param interfaceToImplement Must declare exactly the one method that defines the expression's signature
518: * @param parameterNames The expression references the parameters through these names
519: * @return an object that implements the given interface and extends the <code>optionalExtendedType</code>
520: */
521: public static Object createFastScriptEvaluator(String script,
522: Class interfaceToImplement, String[] parameterNames)
523: throws CompileException, Parser.ParseException,
524: Scanner.ScanException {
525: ScriptEvaluator se = new ScriptEvaluator();
526: return ScriptEvaluator.createFastEvaluator(se, script,
527: parameterNames, interfaceToImplement);
528: }
529:
530: /**
531: * If the parameter and return types of the expression are known at compile time,
532: * then a "fast" script evaluator can be instantiated through this method.
533: * <p>
534: * Script evaluation is faster than through {@link #evaluate(Object[])}, because
535: * it is not done through reflection but through direct method invocation.
536: * <p>
537: * Example:
538: * <pre>
539: * public interface Foo {
540: * int bar(int a, int b);
541: * }
542: * ...
543: * Foo f = (Foo) ScriptEvaluator.createFastScriptEvaluator(
544: * new Scanner(null, new StringReader("return a + b;")),
545: * Foo.class,
546: * new String[] { "a", "b" },
547: * (ClassLoader) null // Use current thread's context class loader
548: * );
549: * System.out.println("1 + 2 = " + f.bar(1, 2));
550: * </pre>
551: * Notice: The <code>interfaceToImplement</code> must either be declared <code>public</code>,
552: * or with package scope in the root package (i.e. "no" package).
553: *
554: * @param scanner Source of script tokens
555: * @param interfaceToImplement Must declare exactly one method
556: * @param parameterNames
557: * @param optionalParentClassLoader
558: * @return an object that implements the given interface
559: */
560: public static Object createFastScriptEvaluator(Scanner scanner,
561: Class interfaceToImplement, String[] parameterNames,
562: ClassLoader optionalParentClassLoader)
563: throws CompileException, Parser.ParseException,
564: Scanner.ScanException, IOException {
565: ScriptEvaluator se = new ScriptEvaluator();
566: se.setParentClassLoader(optionalParentClassLoader);
567: return ScriptEvaluator.createFastEvaluator(se, scanner,
568: parameterNames, interfaceToImplement);
569: }
570:
571: /**
572: * Like {@link #createFastScriptEvaluator(Scanner, Class, String[], ClassLoader)},
573: * but gives you more control over the generated class (rarely needed in practice).
574: * <p>
575: * Notice: The <code>interfaceToImplement</code> must either be declared <code>public</code>,
576: * or with package scope in the same package as <code>className</code>.
577: *
578: * @param scanner Source of script tokens
579: * @param className Name of generated class
580: * @param optionalExtendedType Class to extend
581: * @param interfaceToImplement Must declare exactly the one method that defines the expression's signature
582: * @param parameterNames The expression references the parameters through these names
583: * @param optionalParentClassLoader Used to load referenced classes, defaults to the current thread's "context class loader"
584: * @return an object that implements the given interface and extends the <code>optionalExtendedType</code>
585: */
586: public static Object createFastScriptEvaluator(Scanner scanner,
587: String className, Class optionalExtendedType,
588: Class interfaceToImplement, String[] parameterNames,
589: ClassLoader optionalParentClassLoader)
590: throws CompileException, Parser.ParseException,
591: Scanner.ScanException, IOException {
592: ScriptEvaluator se = new ScriptEvaluator();
593: se.setClassName(className);
594: se.setExtendedType(optionalExtendedType);
595: se.setParentClassLoader(optionalParentClassLoader);
596: return ScriptEvaluator.createFastEvaluator(se, scanner,
597: parameterNames, interfaceToImplement);
598: }
599:
600: public static Object createFastScriptEvaluator(Scanner scanner,
601: String[] optionalDefaultImports, String className,
602: Class optionalExtendedType, Class interfaceToImplement,
603: String[] parameterNames,
604: ClassLoader optionalParentClassLoader)
605: throws CompileException, Parser.ParseException,
606: Scanner.ScanException, IOException {
607: ScriptEvaluator se = new ScriptEvaluator();
608: se.setClassName(className);
609: se.setExtendedType(optionalExtendedType);
610: se.setDefaultImports(optionalDefaultImports);
611: se.setParentClassLoader(optionalParentClassLoader);
612: return ScriptEvaluator.createFastEvaluator(se, scanner,
613: parameterNames, interfaceToImplement);
614: }
615:
616: public static Object createFastEvaluator(ScriptEvaluator se,
617: String s, String[] parameterNames,
618: Class interfaceToImplement) throws CompileException,
619: Parser.ParseException, Scanner.ScanException {
620: try {
621: return ScriptEvaluator.createFastEvaluator(se, new Scanner(
622: null, new StringReader(s)), parameterNames,
623: interfaceToImplement);
624: } catch (IOException ex) {
625: throw new RuntimeException(
626: "IOException despite StringReader");
627: }
628: }
629:
630: public static Object createFastEvaluator(ScriptEvaluator se,
631: Scanner scanner, String[] parameterNames,
632: Class interfaceToImplement) throws CompileException,
633: Parser.ParseException, Scanner.ScanException, IOException {
634: if (!interfaceToImplement.isInterface())
635: throw new RuntimeException("\"" + interfaceToImplement
636: + "\" is not an interface");
637:
638: Method[] methods = interfaceToImplement.getDeclaredMethods();
639: if (methods.length != 1)
640: throw new RuntimeException("Interface \""
641: + interfaceToImplement
642: + "\" must declare exactly one method");
643: Method methodToImplement = methods[0];
644:
645: se.setImplementedTypes(new Class[] { interfaceToImplement });
646: se.setStaticMethod(false);
647: se.setReturnType(methodToImplement.getReturnType());
648: se.setMethodName(methodToImplement.getName());
649: se.setParameters(parameterNames, methodToImplement
650: .getParameterTypes());
651: se.setThrownExceptions(methodToImplement.getExceptionTypes());
652: se.cook(scanner);
653: Class c = se.getMethod().getDeclaringClass();
654: try {
655: return c.newInstance();
656: } catch (InstantiationException e) {
657: // SNO - Declared class is always non-abstract.
658: throw new RuntimeException(e.toString());
659: } catch (IllegalAccessException e) {
660: // SNO - interface methods are always PUBLIC.
661: throw new RuntimeException(e.toString());
662: }
663: }
664:
665: /**
666: * Calls the generated method with concrete parameter values.
667: * <p>
668: * Each parameter value must have the same type as specified through
669: * the "parameterTypes" parameter of
670: * {@link #setParameters(String[], Class[])}.
671: * <p>
672: * Parameters of primitive type must passed with their wrapper class
673: * objects.
674: * <p>
675: * The object returned has the class as specified through
676: * {@link #setReturnType(Class)}.
677: * <p>
678: * This method is thread-safe.
679: *
680: * @param parameterValues The concrete parameter values.
681: */
682: public Object evaluate(Object[] parameterValues)
683: throws InvocationTargetException {
684: if (this .method == null)
685: throw new IllegalStateException(
686: "Must only be called after \"cook()\"");
687: try {
688: return this .method.invoke(null, parameterValues);
689: } catch (IllegalAccessException ex) {
690: throw new RuntimeException(ex.toString());
691: }
692: }
693:
694: /**
695: * Returns the loaded {@link java.lang.reflect.Method}.
696: * <p>
697: * This method must only be called after {@link #cook(Scanner)}.
698: * <p>
699: * This method must not be called for instances of derived classes.
700: */
701: public Method getMethod() {
702: if (this .method == null)
703: throw new IllegalStateException(
704: "Must only be called after \"cook()\"");
705: return this.method;
706: }
707: }
|