blob: 146544ceb4eb89b61fbe4056da631bf66cae4f23 [file] [log] [blame]
package org.codehaus.groovy.runtime;
import groovy.lang.MetaMethod;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import org.codehaus.groovy.runtimemetaclassactionssupport.DefaultGroovyInstanceMethodGenerator;
import org.codehaus.groovy.runtimemetaclassactionssupport.DefaultGroovyStaticMethodGenerator;
import org.codehaus.groovy.runtimemetaclassactionssupport.InstanceMethodGenerator;
import org.codehaus.groovy.runtimemetaclassactionssupport.MethodGenerator;
import org.codehaus.groovy.runtimemetaclassactionssupport.ReflectionMethodGenerator;
import org.codehaus.groovy.runtimemetaclassactionssupport.StaticMethodGenerator;
/*
* Copyright 2005 John G. Wilson
*
* 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.
*
*/
/**
* @author John Wilson
*
*/
public class MetaClassActionsGenerator {
private static final Map actionsInfoMap = new WeakHashMap();
static {
actionsInfoMap.put(Object.class, new ActionsInfo(Object.class));
}
public static MetaClassActions getActions(final Class claz) {
return getActionsInfo(claz).getActions();
}
protected static ActionsInfo getActionsInfo(final Class claz) {
ActionsInfo classInfo = (ActionsInfo)actionsInfoMap.get(claz);
if (classInfo == null) {
final Class superClass = claz.getSuperclass();
if (superClass != null) {
getActionsInfo(superClass); // ensure that the superclass information has been created
}
classInfo = new ActionsInfo(claz);
actionsInfoMap.put(claz, classInfo);
}
return classInfo;
}
}
class ActionsInfo {
private static Map defaultGroovyMethodsMap = new HashMap();
static {
try {
final Method[] defaultGroovyMethods = Class.forName("org.codehaus.groovy.runtime.DefaultGroovyMethods").getDeclaredMethods();
for (int i = 0; i != defaultGroovyMethods.length; i++) {
final Method method = defaultGroovyMethods[i];
if ((method.getModifiers() & (Modifier.PUBLIC | Modifier.STATIC)) == (Modifier.PUBLIC | Modifier.STATIC)) {
final Class[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length != 0) {
List methodList = (List)defaultGroovyMethodsMap.get(parameterTypes[0]);
if (methodList == null) {
methodList = new LinkedList();
defaultGroovyMethodsMap.put(parameterTypes[0], methodList);
}
methodList.add(new DefaultGroovyInstanceMethodGenerator(method));
}
}
}
} catch (SecurityException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private static Map defaultGroovyStaticMethodsMap = new HashMap();
static {
try {
final Method[] defaultGroovyStaticMethods = Class.forName("org.codehaus.groovy.runtime.DefaultGroovyStaticMethods").getDeclaredMethods();
for (int i = 0; i != defaultGroovyStaticMethods.length; i++) {
final Method method = defaultGroovyStaticMethods[i];
if ((method.getModifiers() & (Modifier.PUBLIC | Modifier.STATIC)) == (Modifier.PUBLIC | Modifier.STATIC)) {
final Class[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length != 0) {
List methodList = (List)defaultGroovyStaticMethodsMap.get(parameterTypes[0]);
if (methodList == null) {
methodList = new LinkedList();
defaultGroovyStaticMethodsMap.put(parameterTypes[0], methodList);
}
methodList.add(new DefaultGroovyStaticMethodGenerator(method));
}
}
}
} catch (SecurityException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private MetaClassActions actions = null;
private final List generators = new LinkedList();
public ActionsInfo(final Class claz) {
final Package pack = claz.getPackage();
final String packageName = "groovy.runtime.metaclassactions." + ((pack == null) ? "" : pack.getName() + ".");
setUpGenerators(claz); // Need to do this even if the class is already generated
try {
final Class actionsClass = Class.forName(packageName + claz.getSimpleName() + "MetaClassActions");
this.actions = (MetaClassActions)actionsClass.newInstance();
} catch (final Exception e1) {
final File generatedFile = new File("generated/" + packageName.replace('.', '/'));
generatedFile.mkdirs();
try {
final Writer out = new FileWriter(new File(generatedFile, claz.getSimpleName() + "MetaClassActions.java"));
try {
out.write(generateJavaFile(claz));
} finally {
out.close();
}
} catch (final IOException e) {
e.printStackTrace();
}
}
}
public MetaClassActions getActions() {
return this.actions;
}
private void setUpGenerators(final Class claz) {
final Method[] methods = claz.getDeclaredMethods();
final List defaultMethods = (List)defaultGroovyMethodsMap.get(claz);
final List defaultStaticMethods = (List)defaultGroovyStaticMethodsMap.get(claz);
if (defaultMethods != null) {
this.generators.addAll(defaultMethods);
}
if (defaultStaticMethods != null) {
this.generators.addAll(defaultStaticMethods);
}
for (int i = 0; i != methods.length; i++) {
final Method method = methods[i];
final int methodModifiers = method.getModifiers();
if ((methodModifiers & Modifier.PUBLIC) != 0) {
if ((methodModifiers & Modifier.STATIC) != 0) {
this.generators.add(new StaticMethodGenerator(method));
} else {
this.generators.add(new InstanceMethodGenerator(method));
}
} else if ((methodModifiers & Modifier.PROTECTED) != 0) {
this.generators.add(new ReflectionMethodGenerator(method));
}
}
if (claz != Object.class) {
final Class superClass = claz.getSuperclass();
if (superClass != null) {
this.generators.addAll(MetaClassActionsGenerator.getActionsInfo(superClass).generators);
}
final Class[] interfaces = claz.getInterfaces();
for (int i = 0; i != interfaces.length; i++) {
this.generators.addAll(MetaClassActionsGenerator.getActionsInfo(interfaces[i]).generators);
}
}
Collections.sort(this.generators, new Comparator() {
public int compare(final Object lhs, final Object rhs) {
final int lhsNumberOfParameters = ((MethodGenerator)lhs).getNumberOfParameters();
final int rhsNumberOfParameters = ((MethodGenerator)rhs).getNumberOfParameters();
if (lhsNumberOfParameters == rhsNumberOfParameters) {
final String lhsName = ((MethodGenerator)lhs).getName();
final String rhsName = ((MethodGenerator)rhs).getName();
return lhsName.compareTo(rhsName);
} else if (lhsNumberOfParameters < rhsNumberOfParameters) {
return -1;
} else {
return 1;
}
}
});
if (this.generators.size() > 1) {
MethodGenerator g1 = (MethodGenerator)this.generators.get(this.generators.size() - 1);
for (int i = this.generators.size() - 2; i > -1 ; i--) {
final MethodGenerator g2 = (MethodGenerator)this.generators.get(i);
if (g1.isOverloaded(g2)) {
this.generators.remove(i + 1);
g1 = g1.processOverloaded(claz, g2);
this.generators.set(i, g1);
} else {
g1 = g2;
}
}
}
}
private String generateJavaFile(final Class claz) {
final StringBuffer code = new StringBuffer();
generateStartOfFile(claz, code);
final Iterator iter = this.generators.iterator();
int methodIndex = 0;
while (iter.hasNext()) {
final MethodGenerator gen = (MethodGenerator)iter.next();
code.append(gen.generateDeclaration(methodIndex++));
}
generateInvokemethod(claz, code);
generateEndOfFile(claz, code);
return code.toString();
}
private void addMethod(final Set methodSet, final Map methodMap, final List methods) {
final Iterator methodIterator = methods.iterator();
while (methodIterator.hasNext()) {
final MetaMethod method = (MetaMethod)methodIterator.next();
final String name = method.getName();
methodSet.add(name);
List methodList = (List)methodMap.get(name);
if (methodList == null) {
methodList = new LinkedList();
methodMap.put(name, methodList);
}
methodList.add(method);
}
}
private void generateStartOfFile(final Class claz, final StringBuffer code) {
final Package pack = claz.getPackage();
final String packageName = (pack == null) ? "" : "." + pack.getName();
code.append("package groovy.runtime.metaclassactions").append(packageName).append(";\n");
code.append("import java.lang.reflect.Method;\n");
code.append("import java.security.AccessController;\n");
code.append("import java.security.PrivilegedAction;\n");
code.append("public class ").append(claz.getSimpleName()).append("MetaClassActions extends org.codehaus.groovy.runtime.MetaClassActions {\n");
}
private void generateInvokemethod(final Class claz, final StringBuffer code) {
code.append("public Object doInvokeMethod(final Object target, final String name, final Object[] args) ");
code.append("throws Exception {\n");
if (this.generators.size() != 0) {
final List generatorSubset = new LinkedList();
MethodGenerator firstGenerator = (MethodGenerator)this.generators.get(0);
int currentArgsLength = firstGenerator.getNumberOfParameters();
char firstChar = firstGenerator.getName().charAt(0);
final Iterator iter = this.generators.iterator();
while (iter.hasNext()) {
final MethodGenerator generator = (MethodGenerator)iter.next();
if (currentArgsLength == generator.getNumberOfParameters()) {
generatorSubset.add(generator);
} else {
generateCalls(claz, code, currentArgsLength, generatorSubset);
currentArgsLength = generator.getNumberOfParameters();
}
}
generateCalls(claz, code, currentArgsLength, generatorSubset);
code.setLength(code.length() - 6); // chop the trailing " else "
}
code.append("\nreturn groovy.lang.MetaClass.NO_METHOD_FOUND;");
code.append("\n}\n");
}
private void generateCalls(final Class claz, final StringBuffer code, final int currentArgsLength, final List generatorSubset) {
if (generatorSubset.size() != 0) {
code.append("if (args.length == ").append(currentArgsLength).append(") {\n");
code.append("switch(name.charAt(0)) {");
boolean firstCase = true;
final Iterator iter1 = generatorSubset.iterator();
char fc = 0;
while (iter1.hasNext()) {
final MethodGenerator generator1 = (MethodGenerator)iter1.next();
final String name = generator1.getName();
if (fc != name.charAt(0)) {
if (firstCase) {
firstCase = false;
} else {
code.append(" else {\n");
code.append("return groovy.lang.MetaClass.NO_METHOD_FOUND;\n");
code.append("}");
}
fc = name.charAt(0);
code.append("\ncase '").append(fc).append("' :\n");
code.append("if (\"").append(generator1.getName()).append("\".equals(name)) {\n");
code.append(generator1.generateCall(claz));
code.append("}");
} else {
code.append(" else if (\"").append(generator1.getName()).append("\".equals(name)) {\n");
code.append(generator1.generateCall(claz));
code.append("}");
}
}
code.append(" else {\n");
code.append("return groovy.lang.MetaClass.NO_METHOD_FOUND;\n");
code.append("}\n");
code.append("default:\n");
code.append("return groovy.lang.MetaClass.NO_METHOD_FOUND;\n");
code.append("}\n");
code.append("} else ");
generatorSubset.clear();
}
}
private void generateEndOfFile(final Class claz, final StringBuffer code) {
code.append("}\n");
}
}