org.codehaus.groovy.tools.javac.JavaStubGenerator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of groovy-all Show documentation
Show all versions of groovy-all Show documentation
Groovy: A powerful, dynamic language for the JVM
/*
* Copyright 2003-2007 the original author or authors.
*
* Licensed 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.codehaus.groovy.tools.javac;
import org.codehaus.groovy.ast.*;
import org.codehaus.groovy.ast.expr.ArgumentListExpression;
import org.codehaus.groovy.ast.expr.ConstantExpression;
import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.VariableExpression;
import org.codehaus.groovy.ast.stmt.BlockStatement;
import org.codehaus.groovy.ast.stmt.ExpressionStatement;
import org.codehaus.groovy.ast.stmt.Statement;
import org.codehaus.groovy.classgen.Verifier;
import org.codehaus.groovy.control.ResolveVisitor;
import org.objectweb.asm.Opcodes;
import java.io.*;
import java.util.*;
public class JavaStubGenerator
{
private boolean java5 = false;
private boolean requireSuperResolved = false;
private File outputPath;
private List toCompile = new ArrayList();
private ArrayList propertyMethods = new ArrayList();
public JavaStubGenerator(final File outputPath, final boolean requireSuperResolved, final boolean java5) {
this.outputPath = outputPath;
this.requireSuperResolved = requireSuperResolved;
this.java5 = java5;
}
public JavaStubGenerator(final File outputPath) {
this(outputPath, false, false);
}
private void mkdirs(File parent, String relativeFile) {
int index = relativeFile.lastIndexOf('/');
if (index==-1) return;
File dir = new File(parent,relativeFile.substring(0,index));
dir.mkdirs();
}
public void generateClass(ClassNode classNode) throws FileNotFoundException {
// Only attempt to render our self if our super-class is resolved, else wait for it
if (requireSuperResolved && !classNode.getSuperClass().isResolved()) {
return;
}
String fileName = classNode.getName().replace('.', '/');
mkdirs(outputPath,fileName);
toCompile.add(fileName);
File file = new File(outputPath, fileName + ".java");
FileOutputStream fos = new FileOutputStream(file);
PrintWriter out = new PrintWriter(fos);
Verifier verifier = new Verifier() {
public void addCovariantMethods(ClassNode cn) {}
protected void addTimeStamp(ClassNode node) {}
protected void addInitialization(ClassNode node) {}
protected void addPropertyMethod(MethodNode method) {
propertyMethods.add(method);
}
};
verifier.visitClass(classNode);
try {
String packageName = classNode.getPackageName();
if (packageName != null) {
out.println("package " + packageName + ";\n");
}
genImports(classNode, out);
boolean isInterface = classNode.isInterface();
boolean isEnum = (classNode.getModifiers() & Opcodes.ACC_ENUM) !=0;
printModifiers(out, classNode.getModifiers()
& ~(isInterface ? Opcodes.ACC_ABSTRACT : 0));
if (isInterface) {
out.print ("interface ");
} else if (isEnum) {
out.print ("enum ");
} else {
out.print ("class ");
}
out.println(classNode.getNameWithoutPackage());
writeGenericsBounds(out, classNode, true);
ClassNode superClass = classNode.getUnresolvedSuperClass(false);
if (!isInterface && !isEnum) {
out.print(" extends ");
printType(superClass,out);
}
ClassNode[] interfaces = classNode.getInterfaces();
if (interfaces != null && interfaces.length > 0) {
if (isInterface) {
out.println(" extends");
} else {
out.println(" implements");
}
for (int i = 0; i < interfaces.length - 1; ++i) {
out.print(" ");
printType(interfaces[i], out);
out.print(",");
}
out.print(" ");
printType(interfaces[interfaces.length - 1],out);
}
out.println(" {");
genFields(classNode, out, isEnum);
genMethods(classNode, out, isEnum);
out.println("}");
} finally {
propertyMethods.clear();
try {
out.close();
} catch (Exception e) {
// ignore
}
try {
fos.close();
} catch (IOException e) {
// ignore
}
}
}
private void genMethods(ClassNode classNode, PrintWriter out, boolean isEnum) {
if (!isEnum) getConstructors(classNode, out);
List methods = (List) propertyMethods.clone();
methods.addAll(classNode.getMethods());
if (methods != null)
for (Iterator it = methods.iterator(); it.hasNext();) {
MethodNode methodNode = (MethodNode) it.next();
if(isEnum && methodNode.isSynthetic()) {
// skip values() method and valueOf(String)
String name = methodNode.getName();
Parameter[] params = methodNode.getParameters();
if (name.equals("values") && params.length==0) continue;
if (name.equals("valueOf") &&
params.length==1 &&
params[0].getType().equals(ClassHelper.STRING_TYPE))
{
continue;
}
}
genMethod(classNode, methodNode, out);
}
}
private void getConstructors(ClassNode classNode, PrintWriter out) {
List constrs = classNode.getDeclaredConstructors();
if (constrs != null)
for (Iterator it = constrs.iterator(); it.hasNext();) {
ConstructorNode constrNode = (ConstructorNode) it.next();
genConstructor(classNode, constrNode, out);
}
}
private void genFields(ClassNode classNode, PrintWriter out, boolean isEnum) {
List fields = classNode.getFields();
if (fields == null) return;
ArrayList enumFields = new ArrayList(fields.size());
ArrayList normalFields = new ArrayList(fields.size());
for (Iterator it = fields.iterator(); it.hasNext();) {
FieldNode fieldNode = (FieldNode) it.next();
boolean isEnumField = (fieldNode.getModifiers() & Opcodes.ACC_ENUM) !=0;
boolean isSynthetic = (fieldNode.getModifiers() & Opcodes.ACC_SYNTHETIC) !=0;
if (isEnumField) {
enumFields.add(fieldNode);
} else if (!isSynthetic) {
normalFields.add(fieldNode);
}
}
genEnumFields(enumFields, out);
for (Iterator iterator = normalFields.iterator(); iterator.hasNext();) {
FieldNode fieldNode = (FieldNode) iterator.next();
genField(fieldNode, out);
}
}
private void genEnumFields(List fields, PrintWriter out) {
if (fields.size()==0) return;
boolean first = true;
for (Iterator iterator = fields.iterator(); iterator.hasNext();) {
FieldNode fieldNode = (FieldNode) iterator.next();
if (!first) {
out.print(", ");
} else {
first = false;
}
out.print(fieldNode.getName());
}
out.println(";");
}
private void genField(FieldNode fieldNode, PrintWriter out) {
if ((fieldNode.getModifiers()&Opcodes.ACC_PRIVATE)!=0) return;
printModifiers(out, fieldNode.getModifiers());
printType(fieldNode.getType(), out);
out.print(" ");
out.print(fieldNode.getName());
out.println(";");
}
private ConstructorCallExpression getConstructorCallExpression(
ConstructorNode constructorNode) {
Statement code = constructorNode.getCode();
if (!(code instanceof BlockStatement))
return null;
BlockStatement block = (BlockStatement) code;
List stats = block.getStatements();
if (stats == null || stats.size() == 0)
return null;
Statement stat = (Statement) stats.get(0);
if (!(stat instanceof ExpressionStatement))
return null;
Expression expr = ((ExpressionStatement) stat).getExpression();
if (!(expr instanceof ConstructorCallExpression))
return null;
return (ConstructorCallExpression) expr;
}
private void genConstructor(ClassNode clazz, ConstructorNode constructorNode, PrintWriter out) {
// printModifiers(out, constructorNode.getModifiers());
out.print("public "); // temporary hack
out.print(clazz.getNameWithoutPackage());
printParams(constructorNode, out);
ConstructorCallExpression constrCall = getConstructorCallExpression(constructorNode);
if (constrCall == null || !constrCall.isSpecialCall()) {
out.println(" {}");
} else {
out.println(" {");
genSpecialConstructorArgs(out, constructorNode, constrCall);
out.println("}");
}
}
private Parameter[] selectAccessibleConstructorFromSuper(ConstructorNode node) {
ClassNode type = node.getDeclaringClass();
ClassNode superType = type.getSuperClass();
boolean hadPrivateConstructor = false;
for (Iterator iter = superType.getDeclaredConstructors().iterator(); iter.hasNext();) {
ConstructorNode c = (ConstructorNode)iter.next();
// Only look at things we can actually call
if (c.isPublic() || c.isProtected()) {
return c.getParameters();
}
}
// fall back for parameterless constructor
if (superType.isPrimaryClassNode()) {
return Parameter.EMPTY_ARRAY;
}
return null;
}
private void genSpecialConstructorArgs(PrintWriter out, ConstructorNode node, ConstructorCallExpression constrCall) {
// Select a constructor from our class, or super-class which is legal to call,
// then write out an invoke w/nulls using casts to avoid abigous crapo
Parameter[] params = selectAccessibleConstructorFromSuper(node);
if (params != null) {
out.print("super (");
for (int i=0; i")) return;
if (!clazz.isInterface()) printModifiers(out, methodNode.getModifiers());
writeGenericsBounds(out, methodNode.getGenericsTypes());
out.print(" ");
printType(methodNode.getReturnType(), out);
out.print(" ");
out.print(methodNode.getName());
printParams(methodNode, out);
ClassNode[] exceptions = methodNode.getExceptions();
for (int i=0; i');
}
private void writeGenericsBounds(PrintWriter out, GenericsType genericsType) {
if (genericsType.isPlaceholder()) {
out.print(genericsType.getName());
} else {
printType(genericsType.getType(),out);
}
ClassNode[] upperBounds = genericsType.getUpperBounds();
ClassNode lowerBound = genericsType.getLowerBound();
if (upperBounds != null) {
out.print(" extends ");
for (int i = 0; i < upperBounds.length; i++) {
printType(upperBounds[i], out);
if (i + 1 < upperBounds.length) out.print(" & ");
}
} else if (lowerBound != null) {
out.print(" super ");
printType(lowerBound, out);
}
}
private void printParams(MethodNode methodNode, PrintWriter out) {
out.print("(");
Parameter[] parameters = methodNode.getParameters();
if (parameters != null && parameters.length != 0) {
for (int i = 0; i != parameters.length; ++i) {
printType(parameters[i].getType(), out);
out.print(" ");
out.print(parameters[i].getName());
if (i + 1 < parameters.length) {
out.print(", ");
}
}
}
out.print(")");
}
private void printModifiers(PrintWriter out, int modifiers) {
if ((modifiers & Opcodes.ACC_PUBLIC) != 0)
out.print("public ");
if ((modifiers & Opcodes.ACC_PROTECTED) != 0)
out.print("protected ");
if ((modifiers & Opcodes.ACC_PRIVATE) != 0)
out.print("private ");
if ((modifiers & Opcodes.ACC_STATIC) != 0)
out.print("static ");
if ((modifiers & Opcodes.ACC_SYNCHRONIZED) != 0)
out.print("synchronized ");
if ((modifiers & Opcodes.ACC_ABSTRACT) != 0)
out.print("abstract ");
}
private void genImports(ClassNode classNode, PrintWriter out) {
Set imports = new HashSet();
//
// HACK: Add the default imports... since things like Closure and GroovyObject seem to parse out w/o fully qualified classnames.
//
imports.addAll(Arrays.asList(ResolveVisitor.DEFAULT_IMPORTS));
ModuleNode moduleNode = classNode.getModule();
for (Iterator it = moduleNode.getImportPackages().iterator(); it.hasNext();) {
imports.add(it.next());
}
for (Iterator it = moduleNode.getImports().iterator(); it.hasNext();) {
ImportNode imp = (ImportNode) it.next();
String name = imp.getType().getName();
int lastDot = name.lastIndexOf('.');
if (lastDot != -1)
imports.add(name.substring(0, lastDot + 1));
}
for (Iterator it = imports.iterator(); it.hasNext();) {
String imp = (String) it.next();
out.print("import ");
out.print(imp);
out.println("*;");
}
out.println();
}
public void clean() {
for (Iterator it = toCompile.iterator(); it.hasNext();) {
String path = (String) it.next();
new File(outputPath, path + ".java").delete();
}
}
}