blob: 86e743e8870c22495e0bc84a54de6e3d7e26a1ae [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.drill.exec.compile;
/*
* 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.
*/
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import org.codehaus.commons.compiler.CompileException;
import org.codehaus.commons.compiler.IClassBodyEvaluator;
public class ClassBodyBuilder {
private String[] optionalDefaultImports = null;
private String className = IClassBodyEvaluator.DEFAULT_CLASS_NAME;
private Class<?> optionalExtendedType = null;
private Class<?>[] implementedTypes = new Class[0];
private String[] imports = {};
private String body;
private boolean used = false;
public static ClassBodyBuilder newBuilder(){
return new ClassBodyBuilder();
}
private ClassBodyBuilder(){
}
public ClassBodyBuilder setClassName(String className) {
assertNotCooked();
this.className = className;
return this;
}
public ClassBodyBuilder setDefaultImports(String... optionalDefaultImports) {
assertNotCooked();
this.optionalDefaultImports = optionalDefaultImports;
return this;
}
public ClassBodyBuilder setExtendedClass(Class<?> optionalExtendedType) {
assertNotCooked();
this.optionalExtendedType = optionalExtendedType;
return this;
}
public ClassBodyBuilder setImplementedInterfaces(Class<?>... implementedTypes) {
assertNotCooked();
this.implementedTypes = implementedTypes;
return this;
}
private void assertNotCooked() {
assert !used;
}
public ClassBodyBuilder setImports(String[] imports) {
assertNotCooked();
this.imports = imports;
return this;
}
public ClassBodyBuilder setBody(String body) {
assertNotCooked();
this.body = body;
return this;
}
public String build() throws CompileException, IOException {
used = true;
// 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.
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();
}
return sw1.toString() + body + sw2.toString();
}
}
// /**
// * 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;
// }
// }
}