Package org.codehaus.commons.compiler.jdk

Source Code of org.codehaus.commons.compiler.jdk.ClassBodyEvaluator

/*
* 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. The name of the author may not be used to endorse or promote products derived from this software without
*       specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 org.codehaus.commons.compiler.jdk;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.nio.CharBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.codehaus.commons.compiler.CompileException;
import org.codehaus.commons.compiler.IClassBodyEvaluator;
import org.codehaus.commons.io.MultiReader;

/**
* To set up a {@link ClassBodyEvaluator} object, proceed as described for {@link
* IClassBodyEvaluator}. Alternatively, a number of "convenience constructors" exist that execute
* the described steps instantly.
* <p>
* <b>Notice that this implementation of {@link IClassBodyEvaluator} is prone to "Java
* injection", i.e. an application could get more than one class body compiled by passing a
* bogus input document.</b>
* <p>
* <b>Also notice that the parsing of leading IMPORT declarations is heuristic and has certain
* limitations; see {@link #parseImportDeclarations(Reader)}.</b>
*
* @see IClassBodyEvaluator
*/
public class ClassBodyEvaluator extends SimpleCompiler implements IClassBodyEvaluator {

    private String[]   optionalDefaultImports = null;
    private String     className = IClassBodyEvaluator.DEFAULT_CLASS_NAME;
    private Class<?>   optionalExtendedType = null;
    private Class<?>[] implementedTypes = new Class[0];;

    private Class<?> result = null;

    @Override
    public void setClassName(String className) {
        assertNotCooked();
        this.className = className;
    }

    @Override
    public void setDefaultImports(String[] optionalDefaultImports) {
        assertNotCooked();
        this.optionalDefaultImports = optionalDefaultImports;
    }

    @Override
    public void setExtendedClass(@SuppressWarnings("rawtypes") Class optionalExtendedType) {
        assertNotCooked();
        this.optionalExtendedType = optionalExtendedType;
    }

    /** @deprecated */
    @Override
    public void setExtendedType(@SuppressWarnings("rawtypes") Class optionalExtendedClass) {
        this.setExtendedClass(optionalExtendedClass);
    }

    @Override
    public void setImplementedInterfaces(@SuppressWarnings("rawtypes") Class[] implementedTypes) {
        assertNotCooked();
        this.implementedTypes = implementedTypes;
    }

    /** @deprecated */
    @Override
    public void setImplementedTypes(@SuppressWarnings("rawtypes") Class[] implementedInterfaces) {
        this.setImplementedInterfaces(implementedInterfaces);
    }

    public void cook(
        String optionalFileName,
        Reader r
    ) throws CompileException, IOException {
        if (!r.markSupported()) r = new BufferedReader(r);
        this.cook(optionalFileName, ClassBodyEvaluator.parseImportDeclarations(r), r);
    }

    /**
     * @param imports E.g. "java.io.*" or "static java.util.Arrays.asList"
     * @param r The class body to cook, without leading IMPORT declarations
     */
    protected void cook(
        String   optionalFileName,
        String[] imports,
        Reader   r
    ) throws CompileException, IOException {

        // Wrap the class body in a compilation unit.
        {
            StringWriter sw1 = new StringWriter();
            {
                PrintWriter pw = new PrintWriter(sw1);

                // Break the class name up into package name and simple class name.
                String packageName; // null means default package.
                String simpleClassName;
                {
                    int idx = this.className.lastIndexOf('.');
                    if (idx == -1) {
                        packageName = "";
                        simpleClassName = this.className;
                    } else
                    {
                        packageName = this.className.substring(0, idx);
                        simpleClassName = this.className.substring(idx + 1);
                    }
                }

                // Print PACKAGE directive.
                if (!packageName.isEmpty()) {
                    pw.print("package ");
                    pw.print(packageName);
                    pw.println(";");
                }

                // Print default imports.
                if (this.optionalDefaultImports != null) {
                    for (String defaultImport : this.optionalDefaultImports) {
                        pw.print("import ");
                        pw.print(defaultImport);
                        pw.println(";");
                    }
                }

                // Print imports as declared in the document.
                if (!r.markSupported()) r = new BufferedReader(r);
                for (String imporT : imports) {
                    pw.print("import ");
                    pw.print(imporT);
                    pw.println(";");
                }

                // Print the class declaration.
                pw.print("public class ");
                pw.print(simpleClassName);
                if (this.optionalExtendedType != null) {
                    pw.print(" extends ");
                    pw.print(this.optionalExtendedType.getCanonicalName());
                }
                if (this.implementedTypes.length > 0) {
                    pw.print(" implements ");
                    pw.print(this.implementedTypes[0].getName());
                    for (int i = 1; i < this.implementedTypes.length; ++i) {
                        pw.print(", ");
                        pw.print(this.implementedTypes[i].getName());
                    }
                }
                pw.println(" {");
                pw.close();
            }

            StringWriter sw2 = new StringWriter();
            {
                PrintWriter pw = new PrintWriter(sw2);
                pw.println("}");
                pw.close();
            }

            r = new MultiReader(new Reader[] {
                new StringReader(sw1.toString()),
                r,
                new StringReader(sw2.toString()),
            });
        }

        /**
         * Compile the generated compilation unit.
         */
        super.cook(optionalFileName, r);

        try {

            // Load the "main" class through the ClassLoader that was created by
            // "SimpleCompiler.cook()". More classes (e.g. member types will be loaded
            // automatically by the JVM.
            this.result = this.getClassLoader().loadClass(this.className);
        } catch (ClassNotFoundException cnfe) {
            throw new IOException(cnfe);
        }
    }

    /**
     * @return The {@link Class} created by the preceding call to {@link #cook(Reader)}
     */
    public Class<?> getClazz() {
        return this.result;
    }

    /**
     * Heuristically parse IMPORT declarations at the beginning of the character stream produced
     * by the given {@link Reader}. After this method returns, all characters up to and including
     * that last IMPORT declaration have been read from the {@link Reader}.
     * <p>
     * This method does not handle comments and string literals correctly, i.e. if a pattern that
     * looks like an IMPORT declaration appears within a comment or a string literal, it will be
     * taken as an IMPORT declaration.
     *
     * @param r A {@link Reader} that supports MARK, e.g. a {@link BufferedReader}
     * @return  The parsed imports, e.g. {@code { "java.util.*", "static java.util.Map.Entry" }}
     */
    protected static String[] parseImportDeclarations(Reader r) throws IOException {
        final CharBuffer cb = CharBuffer.allocate(10000);
        r.mark(cb.limit());
        r.read(cb);
        cb.rewind();

        List<String> imports = new ArrayList<String>();
        int afterLastImport = 0;
        for (Matcher matcher = IMPORT_STATEMENT_PATTERN.matcher(cb); matcher.find();) {
            imports.add(matcher.group(1));
            afterLastImport = matcher.end();
        }
        r.reset();
        r.skip(afterLastImport);
        return imports.toArray(new String[imports.size()]);
    }
    private static final Pattern IMPORT_STATEMENT_PATTERN = Pattern.compile(
        "\\bimport\\s+"
        + "("
        + "(?:static\\s+)?"
        + "[\\p{javaLowerCase}\\p{javaUpperCase}_\\$][\\p{javaLowerCase}\\p{javaUpperCase}\\d_\\$]*"
        + "(?:\\.[\\p{javaLowerCase}\\p{javaUpperCase}_\\$][\\p{javaLowerCase}\\p{javaUpperCase}\\d_\\$]*)*"
        + "(?:\\.\\*)?"
        + ");"
    );

    @Override
    public Object createInstance(Reader reader) throws CompileException, IOException {
        this.cook(reader);
        try {
            return this.getClazz().newInstance();
        } catch (InstantiationException ie) {
            CompileException ce = new CompileException((
                "Class is abstract, an interface, an array class, a primitive type, or void; "
                + "or has no zero-parameter constructor"
            ), null);
            ce.initCause(ie);
            throw ce;
        } catch (IllegalAccessException iae) {
            CompileException ce = new CompileException(
                "The class or its zero-parameter constructor is not accessible",
                null
            );
            ce.initCause(iae);
            throw ce;
        }
    }
}
TOP

Related Classes of org.codehaus.commons.compiler.jdk.ClassBodyEvaluator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.