001: /*
002: * Janino - An embedded Java[TM] compiler
003: *
004: * Copyright (c) 2001-2007, 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:
038: import org.codehaus.janino.Parser.ParseException;
039: import org.codehaus.janino.Scanner.ScanException;
040: import org.codehaus.janino.util.enumerator.EnumeratorSet;
041:
042: /**
043: * Parses a class body and returns it as a <tt>java.lang.Class</tt> object
044: * ready for use with <tt>java.lang.reflect</tt>.
045: * <p>
046: * Example:
047: * <pre>
048: * import java.util.*;
049: *
050: * static private int a = 1;
051: * private int b = 2;
052: *
053: * public void func(int c, int d) {
054: * return func2(c, d);
055: * }
056: *
057: * private static void func2(int e, int f) {
058: * return e * f;
059: * }
060: * </pre>
061: * <p>
062: * The <code>optionalClassLoader</code> serves two purposes:
063: * <ul>
064: * <li>It is used to look for classes referenced by the class body.
065: * <li>It is used to load the generated Java<sup>TM</sup> class
066: * into the JVM; directly if it is a subclass of {@link
067: * ByteArrayClassLoader}, or by creation of a temporary
068: * {@link ByteArrayClassLoader} if not.
069: * </ul>
070: * To set up a {@link ClassBodyEvaluator} object, proceed as follows:
071: * <ol>
072: * <li>
073: * Create the {@link ClassBodyEvaluator} using {@link #ClassBodyEvaluator()}
074: * <li>
075: * Configure the {@link ClassBodyEvaluator} by calling any of the following methods:
076: * <ul>
077: * <li>{@link org.codehaus.janino.SimpleCompiler#setParentClassLoader(ClassLoader)}
078: * <li>{@link #setDefaultImports(String[])}
079: * </ul>
080: * <li>
081: * Call any of the {@link org.codehaus.janino.Cookable#cook(Scanner)} methods to scan,
082: * parse, compile and load the class body into the JVM.
083: * </ol>
084: * Alternatively, a number of "convenience constructors" exist that execute the steps described
085: * above instantly.
086: * <p>
087: * To compile a class body and immediately instantiate an object, one of the
088: * {@link #createFastClassBodyEvaluator(Scanner, Class, ClassLoader)} methods can be used.
089: * <p>
090: * The generated class may optionally extend/implement a given type; the returned instance can
091: * safely be type-casted to that <code>optionalBaseType</code>.
092: * <p>
093: * Example:
094: * <pre>
095: * public interface Foo {
096: * int bar(int a, int b);
097: * }
098: * ...
099: * Foo f = (Foo) ClassBodyEvaluator.createFastClassBodyEvaluator(
100: * new Scanner(null, new StringReader("public int bar(int a, int b) { return a + b; }")),
101: * Foo.class, // Base type to extend/implement
102: * (ClassLoader) null // Use current thread's context class loader
103: * );
104: * System.out.println("1 + 2 = " + f.bar(1, 2));
105: * </pre>
106: * Notice: The <code>optionalBaseType</code> must be accessible from the generated class,
107: * i.e. it must either be declared <code>public</code>, or with default accessibility in the
108: * same package as the generated class.
109: */
110: public class ClassBodyEvaluator extends SimpleCompiler {
111: public static final String DEFAULT_CLASS_NAME = "SC";
112: protected static final Class[] ZERO_CLASSES = new Class[0];
113:
114: private String[] optionalDefaultImports = null;
115: protected String className = ClassBodyEvaluator.DEFAULT_CLASS_NAME;
116: private Class optionalExtendedType = null;
117: private Class[] implementedTypes = ClassBodyEvaluator.ZERO_CLASSES;
118: private Class result = null; // null=uncooked
119:
120: /**
121: * Equivalent to<pre>
122: * ClassBodyEvaluator cbe = new ClassBodyEvaluator();
123: * cbe.cook(classBody);</pre>
124: *
125: * @see #ClassBodyEvaluator()
126: * @see Cookable#cook(String)
127: */
128: public ClassBodyEvaluator(String classBody)
129: throws CompileException, ParseException, ScanException {
130: this .cook(classBody);
131: }
132:
133: /**
134: * Equivalent to<pre>
135: * ClassBodyEvaluator cbe = new ClassBodyEvaluator();
136: * cbe.cook(optionalFileName, is);</pre>
137: *
138: * @see #ClassBodyEvaluator()
139: * @see Cookable#cook(String, InputStream)
140: */
141: public ClassBodyEvaluator(String optionalFileName, InputStream is)
142: throws CompileException, ParseException, ScanException,
143: IOException {
144: this .cook(optionalFileName, is);
145: }
146:
147: /**
148: * Equivalent to<pre>
149: * ClassBodyEvaluator cbe = new ClassBodyEvaluator();
150: * cbe.cook(optionalFileName, reader);</pre>
151: *
152: * @see #ClassBodyEvaluator()
153: * @see Cookable#cook(String, Reader)
154: */
155: public ClassBodyEvaluator(String optionalFileName, Reader reader)
156: throws CompileException, ParseException, ScanException,
157: IOException {
158: this .cook(optionalFileName, reader);
159: }
160:
161: /**
162: * Equivalent to<pre>
163: * ClassBodyEvaluator cbe = new ClassBodyEvaluator();
164: * cbe.setParentClassLoader(optionalParentClassLoader);
165: * cbe.cook(scanner);</pre>
166: *
167: * @see #ClassBodyEvaluator()
168: * @see SimpleCompiler#setParentClassLoader(ClassLoader)
169: * @see Cookable#cook(Scanner)
170: */
171: public ClassBodyEvaluator(Scanner scanner,
172: ClassLoader optionalParentClassLoader)
173: throws CompileException, ParseException, ScanException,
174: IOException {
175: this .setParentClassLoader(optionalParentClassLoader);
176: this .cook(scanner);
177: }
178:
179: /**
180: * Equivalent to<pre>
181: * ClassBodyEvaluator cbe = new ClassBodyEvaluator();
182: * cbe.setExtendedType(optionalExtendedType);
183: * cbe.setImplementedTypes(implementedTypes);
184: * cbe.setParentClassLoader(optionalParentClassLoader);
185: * cbe.cook(scanner);</pre>
186: *
187: * @see #ClassBodyEvaluator()
188: * @see #setExtendedType(Class)
189: * @see #setImplementedTypes(Class[])
190: * @see SimpleCompiler#setParentClassLoader(ClassLoader)
191: * @see Cookable#cook(Scanner)
192: */
193: public ClassBodyEvaluator(Scanner scanner,
194: Class optionalExtendedType, Class[] implementedTypes,
195: ClassLoader optionalParentClassLoader)
196: throws CompileException, ParseException, ScanException,
197: IOException {
198: this .setExtendedType(optionalExtendedType);
199: this .setImplementedTypes(implementedTypes);
200: this .setParentClassLoader(optionalParentClassLoader);
201: this .cook(scanner);
202: }
203:
204: /**
205: * Equivalent to<pre>
206: * ClassBodyEvaluator cbe = new ClassBodyEvaluator();
207: * cbe.setClassName(className);
208: * cbe.setExtendedType(optionalExtendedType);
209: * cbe.setImplementedTypes(implementedTypes);
210: * cbe.setParentClassLoader(optionalParentClassLoader);
211: * cbe.cook(scanner);</pre>
212: *
213: * @see #ClassBodyEvaluator()
214: * @see #setClassName(String)
215: * @see #setExtendedType(Class)
216: * @see #setImplementedTypes(Class[])
217: * @see SimpleCompiler#setParentClassLoader(ClassLoader)
218: * @see Cookable#cook(Scanner)
219: */
220: public ClassBodyEvaluator(Scanner scanner, String className,
221: Class optionalExtendedType, Class[] implementedTypes,
222: ClassLoader optionalParentClassLoader)
223: throws CompileException, ParseException, ScanException,
224: IOException {
225: this .setClassName(className);
226: this .setExtendedType(optionalExtendedType);
227: this .setImplementedTypes(implementedTypes);
228: this .setParentClassLoader(optionalParentClassLoader);
229: this .cook(scanner);
230: }
231:
232: public ClassBodyEvaluator() {
233: }
234:
235: /**
236: * "Default imports" add to the system import "java.lang", i.e. the evaluator may refer to
237: * classes imported by default imports without having to explicitly declare IMPORT statements.
238: * <p>
239: * Notice that JDK 5 "static imports" are also supported, as shown in the following example.
240: * <p>
241: * Example: <pre>
242: * sc.setDefaultImports(new String[] {
243: * "java.util.Map", // Single type import
244: * "java.io.*", // Type-import-on-demand
245: * "static java.util.Collections.EMPTY_MAP", // Single static import
246: * "static java.util.Collections.*", // Static-import-on-demand
247: * });</pre>
248: */
249: public void setDefaultImports(String[] optionalDefaultImports) {
250: this .optionalDefaultImports = optionalDefaultImports;
251: }
252:
253: /**
254: * Set the name of the generated class. Defaults to {@link #DEFAULT_CLASS_NAME}. In most cases,
255: * there is no need to set this name, because the generated class is loaded into its own
256: * {@link java.lang.ClassLoader} where its name cannot collide with classes generated by
257: * other evaluators.
258: * <p>
259: * One reason to use this function is to have a class name in a non-default package, which
260: * can be relevant when types and members with DEFAULT accessibility are accessed.
261: */
262: public void setClassName(String className) {
263: if (className == null)
264: throw new NullPointerException();
265: this .className = className;
266: }
267:
268: /**
269: * Set a particular superclass that the generated class will extend. If <code>null</code> is
270: * passed, the generated class will extend {@link Object}.
271: * <p>
272: * The common reason to set a base class for an evaluator is that the generated class can
273: * directly access the base superclass's (non-private) members.
274: */
275: public void setExtendedType(Class optionalExtendedType) {
276: this .optionalExtendedType = optionalExtendedType;
277: }
278:
279: /**
280: * Set a particular set of interfaces that the generated class will implement.
281: */
282: public void setImplementedTypes(Class[] implementedTypes) {
283: if (implementedTypes == null)
284: throw new NullPointerException(
285: "Zero implemented types must be specified as \"new Class[0]\", not \"null\"");
286: this .implementedTypes = implementedTypes;
287: }
288:
289: public void cook(Scanner scanner) throws CompileException,
290: ParseException, ScanException, IOException {
291: this .setUpClassLoaders();
292:
293: Java.CompilationUnit compilationUnit = this
294: .makeCompilationUnit(scanner);
295:
296: // Add class declaration.
297: Java.ClassDeclaration cd = this
298: .addPackageMemberClassDeclaration(scanner.location(),
299: compilationUnit);
300:
301: // Parse class body declarations (member declarations) until EOF.
302: Parser parser = new Parser(scanner);
303: while (!scanner.peek().isEOF()) {
304: parser.parseClassBodyDeclaration(cd);
305: }
306:
307: // Compile and load it.
308: this .result = this .compileToClass(compilationUnit, // compilationUnit
309: DebuggingInformation.ALL, // debuggingInformation
310: this .className);
311: }
312:
313: /**
314: * Create a {@link Java.CompilationUnit}, set the default imports, and parse the import
315: * declarations.
316: * <p>
317: * If the <code>optionalScanner</code> is given, a sequence of IMPORT directives is parsed
318: * from it and added to the compilation unit.
319: */
320: protected final Java.CompilationUnit makeCompilationUnit(
321: Scanner optionalScanner) throws ParseException,
322: ScanException, IOException {
323: Java.CompilationUnit cu = new Java.CompilationUnit(
324: optionalScanner == null ? null : optionalScanner
325: .getFileName());
326:
327: // Set default imports.
328: if (this .optionalDefaultImports != null) {
329: for (int i = 0; i < this .optionalDefaultImports.length; ++i) {
330: Scanner s = new Scanner(null, new StringReader(
331: this .optionalDefaultImports[i]));
332: cu.addImportDeclaration(new Parser(s)
333: .parseImportDeclarationBody());
334: if (!s.peek().isEOF())
335: throw new ParseException("Unexpected token \""
336: + s.peek() + "\" in default import", s
337: .location());
338: }
339: }
340:
341: // Parse all available IMPORT declarations.
342: if (optionalScanner != null) {
343: Parser parser = new Parser(optionalScanner);
344: while (optionalScanner.peek().isKeyword("import")) {
345: cu
346: .addImportDeclaration(parser
347: .parseImportDeclaration());
348: }
349: }
350:
351: return cu;
352: }
353:
354: /**
355: * To the given {@link Java.CompilationUnit}, add
356: * <ul>
357: * <li>A class declaration with the configured name, superclass and interfaces
358: * <li>A method declaration with the given return type, name, parameter
359: * names and values and thrown exceptions
360: * </ul>
361: *
362: * @return The created {@link Java.ClassDeclaration} object
363: */
364: protected Java.PackageMemberClassDeclaration addPackageMemberClassDeclaration(
365: Location location, Java.CompilationUnit compilationUnit)
366: throws ParseException {
367: String cn = this .className;
368: int idx = cn.lastIndexOf('.');
369: if (idx != -1) {
370: compilationUnit
371: .setPackageDeclaration(new Java.PackageDeclaration(
372: location, cn.substring(0, idx)));
373: cn = cn.substring(idx + 1);
374: }
375: Java.PackageMemberClassDeclaration tlcd = new Java.PackageMemberClassDeclaration(
376: location, // location
377: null, // optionalDocComment
378: Mod.PUBLIC, // modifiers
379: cn, // name
380: this .classToType(location, this .optionalExtendedType), // optionalExtendedType
381: this .classesToTypes(location, this .implementedTypes) // implementedTypes
382: );
383: compilationUnit.addPackageMemberTypeDeclaration(tlcd);
384: return tlcd;
385: }
386:
387: /**
388: * Compile the given compilation unit, load all generated classes, and
389: * return the class with the given name.
390: * @param compilationUnit
391: * @param debuggingInformation TODO
392: * @param newClassName The fully qualified class name
393: * @return The loaded class
394: */
395: protected final Class compileToClass(
396: Java.CompilationUnit compilationUnit,
397: EnumeratorSet debuggingInformation, String newClassName)
398: throws CompileException {
399:
400: // Compile and load the compilation unit.
401: ClassLoader cl = this .compileToClassLoader(compilationUnit,
402: debuggingInformation);
403:
404: // Find the generated class by name.
405: try {
406: return cl.loadClass(newClassName);
407: } catch (ClassNotFoundException ex) {
408: throw new RuntimeException(
409: "SNO: Generated compilation unit does not declare class \""
410: + newClassName + "\"");
411: }
412: }
413:
414: /**
415: * Returns the loaded {@link Class}.
416: * <p>
417: * This method must only be called after {@link #cook(Scanner)}.
418: * <p>
419: * This method must not be called for instances of derived classes.
420: */
421: public Class getClazz() {
422: if (this .getClass() != ClassBodyEvaluator.class)
423: throw new IllegalStateException(
424: "Must not be called on derived instances");
425: if (this .result == null)
426: throw new IllegalStateException(
427: "Must only be called after \"cook()\"");
428: return this .result;
429: }
430:
431: /**
432: * Scans, parses and compiles a class body from the tokens delivered by the the given
433: * {@link Scanner}.
434: * The generated class has the {@link #DEFAULT_CLASS_NAME} and extends the given
435: * <code>optionalBaseType</code> (if that is a class), and implements the given
436: * <code>optionalBaseType</code> (if that is an interface).
437: * <p>
438: * For an explanation of the "fast class body evaluator" concept, see the class description.
439: *
440: * @param scanner Source of class body tokens
441: * @param optionalBaseType Base type to extend/implement
442: * @param optionalParentClassLoader Used to load referenced classes, defaults to the current thread's "context class loader"
443: * @return an object that extends/implements the given <code>optionalBaseType</code>
444: * @see ClassBodyEvaluator
445: */
446: public static Object createFastClassBodyEvaluator(Scanner scanner,
447: Class optionalBaseType,
448: ClassLoader optionalParentClassLoader)
449: throws CompileException, ParseException, ScanException,
450: IOException {
451: return ClassBodyEvaluator
452: .createFastClassBodyEvaluator(
453: scanner, // scanner
454: ClassBodyEvaluator.DEFAULT_CLASS_NAME, // className
455: ( // optionalExtendedType
456: optionalBaseType != null
457: && !optionalBaseType.isInterface() ? optionalBaseType
458: : null),
459: ( // implementedTypes
460: optionalBaseType != null
461: && optionalBaseType.isInterface() ? new Class[] { optionalBaseType }
462: : new Class[0]),
463: optionalParentClassLoader // optionalParentClassLoader
464: );
465: }
466:
467: /**
468: * Scans, parses and compiles a class body from the tokens delivered by the the given
469: * {@link Scanner} with no default imports.
470: * <p>
471: * For an explanation of the "fast class body evaluator" concept, see the class description.
472: *
473: * @param scanner Source of class body tokens
474: * @param className Name of generated class
475: * @param optionalExtendedType Class to extend
476: * @param implementedTypes Interfaces to implement
477: * @param optionalParentClassLoader Used to load referenced classes, defaults to the current thread's "context class loader"
478: * @return an object that extends the <code>optionalExtendedType</code> and implements the given <code>implementedTypes</code>
479: * @see ClassBodyEvaluator
480: */
481: public static Object createFastClassBodyEvaluator(Scanner scanner,
482: String className, Class optionalExtendedType,
483: Class[] implementedTypes,
484: ClassLoader optionalParentClassLoader)
485: throws CompileException, ParseException, ScanException,
486: IOException {
487: Class c = new ClassBodyEvaluator(scanner, className,
488: optionalExtendedType, implementedTypes,
489: optionalParentClassLoader).getClazz();
490: try {
491: return c.newInstance();
492: } catch (InstantiationException e) {
493: throw new CompileException(
494: "Cannot instantiate abstract class -- one or more method implementations are missing",
495: null);
496: } catch (IllegalAccessException e) {
497: // SNO - type and default constructor of generated class are PUBLIC.
498: throw new RuntimeException(e.toString());
499: }
500: }
501: }
|