package com.francetelecom.rd.stubs.engine;
/*
* #%L
* Matos
* $Id:$
* $HeadURL:$
* %%
* Copyright (C) 2008 - 2015 Orange SA
* %%
* 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.
* #L%
*/
import java.io.IOException;
import java.io.PrintStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
* @author piac6784
*
*/
/**
* @author piac6784
*
*/
public class AnnotatingClassDumper extends ClassDumper {
private static final String TOKEN_CLASS_KEY = "tokenClass";
/**
* The key to get the name of the annotation package in properties.
*/
public static final String ANNOTATION_PACKAGE_KEY = "annotationPackage";
private static final String GENERATOR_CLASS_KEY = "generatorClass";
final static String[] primitiveTypes = { "boolean", "byte", "char",
"short", "int", "long", "float", "double" };
/**
* The name of the class used as an internal token for all generated
* constructors.
*/
private final String tokenClass;
/**
* The name of the class used as an internal token for all generated
* constructors.
*/
private final String generatorClass;
/**
* A complete hierarchy to know what can be dumped as default value.
*/
private final Hierarchy hierarchy;
/**
* Counter to generate unique variable names.
*/
private int varCounter = 0;
/**
* All the already defined real class implementations.
*/
private HashSet <String> realClasses = new HashSet<String>();
/**
* Handle to the name cleaner
*/
private ReflexUtil rf;
private Object context;
/**
* Constructor in the most usual case: does not show private elements.
*
* @param outDir
* the name of the directory where the new source tree is created
* @param prefix
* the relocation prefix added for the classloader.
*/
public AnnotatingClassDumper(String outDir, ReflexUtil rf,
Properties properties, Hierarchy hierarchy) {
super(outDir, rf, true, null);
this.hierarchy = hierarchy;
this.tokenClass = properties.getProperty(TOKEN_CLASS_KEY);
this.generatorClass = properties.getProperty(GENERATOR_CLASS_KEY);
this.rf = rf;
}
@Override
public void dumpClass(Class<?> c) throws IOException {
super.dumpClass(c);
Annotation annot = rf.findAnnotation(EngineConstant.REAL_ANNOT, c
.getAnnotations());
if (annot != null) {
String name = ReflexUtil.getStringValue(annot);
String superName = (String) ReflexUtil.getAnnotationField(annot,
EngineConstant.SUPER_FIELD);
if (realClasses.contains(name)) {
System.err.println("You have reused the class name: " + name + " for real classes.");
} else {
realClasses.add(name);
dumpRealClass(c, name, superName);
}
}
}
@Override
public boolean dumpClass(PrintStream out, Class<?> c) {
context = c;
if (c.isAnonymousClass())
return false;
Map<TypeVariable<?>, Type> typeEnv = dumpClassHeader(out, c);
out.println("{");
beginIndent();
if (!c.isInterface()) dumpAddedFields(out, c);
if (!c.isInterface() && !c.isEnum())
dumpAddedConstructor(out, c);
dumpClassBody(out, c, typeEnv);
endIndent();
indent(out);
out.println("}");
return true;
}
private <T> T[] sort(T [] array) {
Comparator <Object> generic = new Comparator () {
@Override
public int compare(Object o1, Object o2) { return o1.toString().compareTo(o2.toString()); }
};
Arrays.sort(array, generic);
return array;
}
/**
* Dumps the fields added for the annotations.
*
* @param out
* @param c
*/
private void dumpAddedFields(PrintStream out, Class<?> c) {
Annotation [] annotArray = c.getAnnotations();
Annotation accuAnnotation = rf.findAnnotation(EngineConstant.ACCUMULATOR_ANNOT, annotArray);
if (accuAnnotation != null && !c.isInterface()) {
String name = ReflexUtil.getStringValue(accuAnnotation);
indent(out);
out.print(ClassDumper.modifier(Modifier.STATIC | Modifier.PUBLIC));
out.print(rf.restoreString(c.getSimpleName()));
out.print(" ");
out.print(name);
out.println(";");
}
List<Annotation> newFieldList = rf.findAnnotations(
EngineConstant.FIELD_ANNOT, EngineConstant.FIELD_ARRAY_ANNOT, annotArray);
Set<String> fields = new HashSet<String>();
// Do not add fields already declared in the class.
for (Field f: c.getFields()) { fields.add(f.getName()); }
for (Annotation newField : newFieldList) {
String name = ReflexUtil.getStringValue(newField);
fields.add(name);
String type = rf.restoreString((String) ReflexUtil
.getAnnotationField(newField, EngineConstant.TYPE_FIELD));
int mod = (Integer) ReflexUtil.getAnnotationField(newField,
EngineConstant.MODIFIER_FIELD);
indent(out);
out.print(ClassDumper.modifier(mod));
out.print(type);
out.print(" ");
out.print(name);
out.println(";");
}
for (Method m : sort(c.getDeclaredMethods())) {
Annotation[][] paramAnnotsArray = m.getParameterAnnotations();
Type[] paramTypes = m.getGenericParameterTypes();
dumpAddedFields(out, fields, paramTypes, paramAnnotsArray, (m.getModifiers() & Modifier.STATIC) != 0);
}
for (Constructor<?> co : sort(c.getDeclaredConstructors())) {
Annotation[][] paramAnnotsArray = co.getParameterAnnotations();
Type[] paramTypes = co.getGenericParameterTypes();
dumpAddedFields(out, fields, paramTypes, paramAnnotsArray, false);
}
}
/**
* This auxiliary function looks for field setters in methods or
* constructors and dump a field declaration if not already done and the
* field is local.
*
* @param out
* output stream
* @param fields
* fields already declared
* @param paramTypes
* the types of parameters of the method/constructor
* @param paramAnnotsArray
* the annotations for each parameter of method/constructor.
* @param b tell if the method added is static
*/
private void dumpAddedFields(PrintStream out, Set<String> fields,
Type[] paramTypes, Annotation[][] paramAnnotsArray, boolean isStatic) {
for (int i = 0; i < paramAnnotsArray.length; i++) {
Annotation[] paramAnnots = paramAnnotsArray[i];
Annotation annot = rf.findAnnotation(
EngineConstant.FIELD_SET_ANNOT, paramAnnots);
if (annot != null) {
String name = ReflexUtil.getStringValue(annot);
if (!fields.contains(name) && name.indexOf('.') < 0) {
fields.add(name);
indent(out);
out.print(isStatic ? "private static " : "protected ");
type(out, paramTypes[i]);
out.print(" ");
out.print(name);
out.println(";");
}
}
}
}
/**
* Dumps a constructor added to be sure that we can generate any object.
* This constructor takes a "private" parameter used as an isolation token.
* If the class has no constructor, then we must generate an explicit empty
* constructor.
*
* @param out
* @param c
*/
private void dumpAddedConstructor(PrintStream out, Class<?> c) {
String classname = shorten(c.getName());
indent(out);
out.print("public ");
out.print(classname.substring(classname.lastIndexOf('.') + 1));
out.print("(" + tokenClass + " arg0) {");
Class<?> superClass = c.getSuperclass();
if (rf.handledClass(superClass)) {
beginIndent();
indent(out);
out.print("super(arg0);");
endIndent();
}
out.println("// Defaults");
for(Field field : c.getDeclaredFields()) {
Annotation [] annots = field.getAnnotations();
Annotation noValueAnnot = rf.findAnnotation(EngineConstant.FIELD_NO_VALUE_ANNOT, annots);
if(noValueAnnot != null) {
out.print(field.getName());
dumpFieldValue(out,field);
out.println(";");
}
}
dumpAccumulator(out,c);
indent(out);
out.println("}");
}
@Override
protected void dumpAnnotation(PrintStream out, Annotation annot) {
if (!rf.isStubAnnotation(annot))
super.dumpAnnotation(out, annot);
}
/**
* Gets the list of registered callbacks in the list of annotations or null
*
* @param annotArray
* @return
*/
private String[] getRegisteredCallbacks(Annotation[] annotArray) {
Annotation registerAnnot = rf.findAnnotation(
EngineConstant.CALLBACK_REGISTER_ANNOT, annotArray);
if (registerAnnot == null)
return null;
return (String[]) ReflexUtil.getAnnotationField(registerAnnot,
EngineConstant.VALUE_FIELD);
}
/**
* @param out
* @param eltAnnot
* @param baseClass
* @param argsAnnot
* @param argsTypes
* @param typeEnv
*/
private void dumpCallbackCalls(PrintStream out, Annotation[] eltAnnot,
Class<?> baseClass, Annotation[][] argsAnnot, Class<?>[] argsTypes,
Map<TypeVariable<?>, Type> typeEnv) {
String[] baseCallbacks = getRegisteredCallbacks(eltAnnot);
if (baseCallbacks != null) {
generateCallbackCalls(out, "this", baseClass, baseCallbacks,
typeEnv);
}
int argCount = 0;
for (Annotation[] paramAnnots : argsAnnot) {
String[] argCallbacks = getRegisteredCallbacks(paramAnnots);
if (argCallbacks != null) {
Class<?> argClass = argsTypes[argCount];
generateCallbackCalls(out, "arg" + (argCount + 1), argClass,
argCallbacks, typeEnv);
}
argCount++;
}
}
@Override
protected void dumpConstructorBody(PrintStream out,
Constructor<?> constructor, Map<TypeVariable<?>, Type> typeEnv) {
context = constructor;
Annotation[] constructorAnnotations = constructor.getAnnotations();
Annotation[][] parametersAnnotations = constructor
.getParameterAnnotations();
Annotation codeAnnotation = rf.findAnnotation(
EngineConstant.CODE_ANNOT, constructorAnnotations);
if (codeAnnotation != null) {
dumpCode(out, codeAnnotation);
} else {
super.dumpConstructorBody(out, constructor, typeEnv);
dumpAccumulator(out,constructor.getDeclaringClass());
dumpFieldAssigns(out, constructorAnnotations, parametersAnnotations);
try {
dumpCallbackCalls(out, constructorAnnotations, constructor
.getDeclaringClass(), parametersAnnotations,
constructor.getParameterTypes(), typeEnv);
} catch (RuntimeException e) {
System.err.println(rf.restoreString(constructor.toString()));
}
}
}
private void dumpAccumulator(PrintStream out, Class<?> c) {
Annotation [] annotArray = c.getAnnotations();
Annotation accuAnnotation = rf.findAnnotation(EngineConstant.ACCUMULATOR_ANNOT, annotArray);
if (accuAnnotation != null) {
String name = ReflexUtil.getStringValue(accuAnnotation);
indent(out);
out.print(name);
out.println(" = this;");
}
Class <?> [] itfs = c.getInterfaces();
for(Class<?> itf : itfs) {
annotArray = itf.getAnnotations();
accuAnnotation = rf.findAnnotation(EngineConstant.ACCUMULATOR_ANNOT, annotArray);
if (accuAnnotation != null) {
String itfName = rf.restoreString(itf.getName());
String name = ReflexUtil.getStringValue(accuAnnotation);
indent(out);
out.print(itfName);
out.print(".");
out.print(name);
out.println(" = this;");
}
}
}
private void dumpCode(PrintStream out, Annotation codeAnnotation) {
String[] code = ReflexUtil.getStringValues(codeAnnotation);
for (String codeLine : code) {
indent(out);
out.println(rf.restoreString(codeLine));
}
}
private void dumpFieldAssigns(PrintStream out, Annotation[] baseAnnotation,
Annotation[][] parameterAnnotations) {
Annotation thisFieldSet = rf.findAnnotation(
EngineConstant.FIELD_SET_ANNOT, baseAnnotation);
if (thisFieldSet != null) {
String name = ReflexUtil.getStringValue(thisFieldSet);
indent(out);
out.println(name + " = this;");
}
for (int i = 0; i < parameterAnnotations.length; i++) {
Annotation fieldSet = rf.findAnnotation(
EngineConstant.FIELD_SET_ANNOT, parameterAnnotations[i]);
if (fieldSet != null) {
String name = ReflexUtil.getStringValue(fieldSet);
indent(out);
out.println(name + " = arg" + (i + 1) + ";");
}
}
}
@Override
protected void dumpMethodBody(PrintStream out, Method method,
Map<TypeVariable<?>, Type> typeEnv) {
Annotation[] methodAnnotations = method.getAnnotations();
Annotation codeAnnotation = rf.findAnnotation(
EngineConstant.CODE_ANNOT, methodAnnotations);
if (codeAnnotation != null) {
dumpCode(out, codeAnnotation);
} else {
Annotation[][] parameterAnnotations = method
.getParameterAnnotations();
dumpFieldAssigns(out, methodAnnotations, parameterAnnotations);
try {
dumpCallbackCalls(out, methodAnnotations, method
.getDeclaringClass(), parameterAnnotations, method
.getParameterTypes(), typeEnv);
} catch (RuntimeException e) {
System.err.println(rf.restoreString(method.toString()));
}
dumpMethodResult(out, method, typeEnv);
}
}
@Override
protected void dumpMethod(PrintStream out, Method meth, boolean isItf,
boolean isNotAbstract, Map<TypeVariable<?>, Type> typeEnv) {
context = meth;
Annotation annot = rf.findAnnotation(EngineConstant.SUPER_ANNOT, meth
.getAnnotations());
if (annot == null)
super.dumpMethod(out, meth, isItf, isNotAbstract, typeEnv);
else {
// TODO Check that this is correct to use USE_SUPER
}
}
/**
* Output the result of a method so that it is an actual value.
*
* @param out
* @param method
* @param typeEnv
*/
private void dumpMethodResult(PrintStream out, Method method,
Map<TypeVariable<?>, Type> typeEnv) {
Class<?> result = method.getReturnType();
if (result.getName().equals("void"))
return;
Annotation[] annots = method.getAnnotations();
Annotation specificResultAnnot = rf.findAnnotation(
EngineConstant.MAY_BE_ANNOT, annots);
Annotation fieldResultAnnot = rf.findAnnotation(
EngineConstant.FIELD_GET_ANNOT, annots);
if (fieldResultAnnot != null) {
indent(out);
String fieldName = ReflexUtil.getStringValue(fieldResultAnnot);
out.append("return ").append(fieldName).append(";\n");
} else if (specificResultAnnot != null) {
String[] mayBeClasses = ReflexUtil
.getStringValues(specificResultAnnot);
int count = 0;
indent(out);
out.println("switch(" + generatorClass + ".intValue()){");
for (String maybe : mayBeClasses) {
indent(out);
out.println("case " + count++ + ":");
beginIndent();
indent(out);
out.append("return new ").append(maybe).append("(").append(
generatorClass + ".token").append(");\n");
endIndent();
}
indent(out);
out.println("default:");
beginIndent();
indent(out);
out.println("return null;");
endIndent();
indent(out);
out.println("}");
} else {
String resultContent = buildDefaultValue(out, result, typeEnv);
indent(out);
// I do not understand why I need this explicit coercion.
if (method.getGenericReturnType() instanceof TypeVariable<?>) {
out.append("return (");
type(out, method.getGenericReturnType(), true, typeEnv );
out.append(") ").append(resultContent).append(";\n");
} else {
out.append("return ").append(resultContent).append(";\n");
}
}
}
/**
* @param out
* the output stream
* @param targetObj
* the name of the element containing the callbacks
* @param clazz
* its class.
* @param callbackIds
* a list of callback id
* @param typeEnv
* a type environment to solve type variables
*/
private void generateCallbackCalls(PrintStream out, String targetObj,
Class<?> clazz, String[] callbackIds,
Map<TypeVariable<?>, Type> typeEnv) {
for (String callbackId : callbackIds) {
int count = generateCallbackCalls(out, targetObj, clazz,
callbackId, typeEnv);
if (count == 0) {
System.err.println("Problem with callbacks in "
+ rf.restoreString(clazz.getName()) + " for "
+ callbackId);
throw new RuntimeException("count");
}
}
}
/**
* Generate the callback for one id. This version recursively explore the
* ancestors (interfaces and superclass).
*
* @param out
* @param targetObj
* @param clazz
* @param callbackId
* @param typeEnv
* @return
*/
private int generateCallbackCalls(PrintStream out, String targetObj,
Class<?> clazz, String callbackId,
Map<TypeVariable<?>, Type> typeEnv) {
int count = generateCallbackCallsLocal(out, targetObj, clazz,
callbackId, typeEnv);
Class<?> superClass = clazz.getSuperclass();
if (superClass != null)
count += generateCallbackCalls(out, targetObj, superClass,
callbackId, typeEnv);
for (Class<?> itfs : clazz.getInterfaces())
count += generateCallbackCalls(out, targetObj, itfs, callbackId,
typeEnv);
return count;
}
/**
* Generate the callbacks for one id and one class.
*
* @param out
* the output stream
* @param targetObj
* the name of the element containing the callbacks
* @param clazz
* its class.
* @param callbackId
* a given callback id
* @param typeEnv
* a type environment to solve type variables
*/
private int generateCallbackCallsLocal(PrintStream out, String targetObj,
Class<?> clazz, String callbackId,
Map<TypeVariable<?>, Type> typeEnv) {
int count = 0;
int lastDot = callbackId.lastIndexOf('.');
if (lastDot > 0) {
// String prefix = callbackId.substring(0, lastDot);
// String clazzName =
// restoreString(clazz.getName()).replace('$','.');
// System.err.println(clazzName + " " + prefix);
// if (!clazzName.equals(prefix)) return 0;
callbackId = callbackId.substring(lastDot + 1);
// System.err.println(callbackId);
}
for (Method method : clazz.getDeclaredMethods()) {
Annotation callbackAnnot = rf.findAnnotation(
EngineConstant.CALLBACK_ANNOT, method.getAnnotations());
if (callbackAnnot != null) {
String[] ids = ReflexUtil.getStringValues(callbackAnnot);
if (ids == null)
continue;
if (contains(ids, callbackId)) {
Class<?>[] argTypesArray = method.getParameterTypes();
String[] defaultValuesArray = new String[argTypesArray.length];
for (int i = 0; i < argTypesArray.length; i++) {
defaultValuesArray[i] = buildDefaultValue(out,
argTypesArray[i], typeEnv);
}
boolean raiseExc = method.getExceptionTypes().length > 0;
if (raiseExc) {
indent(out);
out.println("try {");
beginIndent();
}
indent(out);
out.print(targetObj);
out.print(".");
out.print(method.getName());
out.print("(");
boolean first = true;
for (int i = 0; i < defaultValuesArray.length; i++) {
if (first)
first = false;
else
out.print(", ");
out.print(defaultValuesArray[i]);
}
out.println(");");
if (raiseExc) {
endIndent();
indent(out);
out.println("} catch (Exception exc) { }");
}
count++;
}
}
}
return count;
}
/**
* This methods gives back a string that represent a default value of class
* clazz. But building this value may require several statements that will
* be dumped on the output stream during the method execution.
*
* @param out
* @param type
* @return
*/
private String buildDefaultValue(PrintStream out, Type type,
Map<TypeVariable<?>, Type> typeEnv) {
String typeName = type.toString();
if (contains(primitiveTypes, typeName)) {
return generatorClass + "." + typeName + "Value()";
} else {
if (type instanceof TypeVariable<?>) {
if (typeEnv == null)
return "null";
else {
Type actual = typeEnv.get((TypeVariable<?>) type);
if (actual == null)
return "null";
else
return buildDefaultValue(out, actual, null);
}
} else if (type instanceof Class<?>) {
Class<?> clazz = (Class<?>) type;
if (clazz.isArray()) {
Class<?> elt = clazz.getComponentType();
String eltValue = buildDefaultValue(out, elt, typeEnv);
return "new " + shorten(ReflexUtil.readableName(elt))
+ "[]{" + eltValue + "}";
} else if (clazz.isEnum()) {
return buildDefaultEnumValue(out, clazz, typeEnv);
} else {
return buildDefaultClassValue(out, clazz, typeEnv);
}
} else {
return "*** BOGUS " + type + " ***";
}
}
}
@Override
protected void dumpField(PrintStream out, Field field, boolean hasValue) {
Annotation [] annots = field.getAnnotations();
Annotation implemAnnot = rf.findAnnotation(EngineConstant.FIELD_VALUE_IMPL_ANNOT, annots);
if (implemAnnot != null) {
String name = ReflexUtil.getStringValue(implemAnnot);
String superName;
Class <?> type = field.getType();
Annotation [] annotType = type.getAnnotations();
Annotation realAnnot = rf.findAnnotation(EngineConstant.REAL_ANNOT, annotType);
if (realAnnot != null) {
superName = ReflexUtil.getStringValue(realAnnot);
} else {
superName = shorten(type.getName());
}
indent(out);
out.println("static class " + name + " extends " + superName + " {");
beginIndent();
indent(out);
out.println("public " + name + "(){ super(" + generatorClass + ".token); }");
endIndent();
indent(out);
out.println("}");
}
super.dumpField(out,field,rf.findAnnotation(EngineConstant.FIELD_NO_VALUE_ANNOT, annots) == null );
}
private String buildDefaultClassValue(PrintStream out, Class<?> clazz, Map<TypeVariable<?>, Type> typeEnv) {
int mod = clazz.getModifiers();
if (! rf.handledClass(clazz)) {
if (clazz.isInterface() || Modifier.isAbstract(mod)) return "null";
if (clazz.equals(Class.class)) return "Object.class";
if (clazz.equals(String.class)) {
return "\"[stubs:" + rf.restoreString(context.toString()) + "]\"";
}
return "new " + clazz.getName() + "()";
}
List <Class <?>> subclasses = hierarchy.getSubclasses(clazz);
if (clazz.isInterface()) {
if (subclasses == null) subclasses = hierarchy.getImplementors(clazz);
else subclasses.addAll(hierarchy.getImplementors(clazz));
}
String thisDefault;
// TODO : Here we get rid of interfaces, abstract class but also embedded classes.
// The last point deserves a second thought.
Annotation [] annots = clazz.getAnnotations();
Annotation accumulatorAnnot = rf.findAnnotation(EngineConstant.ACCUMULATOR_ANNOT, annots);
if (accumulatorAnnot != null) {
String name = ReflexUtil.getStringValue(accumulatorAnnot);
// We stop here.
return rf.restoreString(clazz.getName())+ "." + name;
}
if (clazz.isInterface() || Modifier.isAbstract(mod) || (clazz.getEnclosingClass() != null && (!Modifier.isStatic(mod)) || (!Modifier.isPublic(mod)))) {
thisDefault = interfaceWitness(annots);
} else {
thisDefault =
"new " + shorten(clazz.getName()) + "(" +
(rf.handledClass(clazz) ? generatorClass + ".token)" : ")");
}
if (hierarchy.count(clazz) > 30 || subclasses == null) {
if (thisDefault.equals("null") && (clazz.isInterface() || Modifier.isAbstract(mod) || clazz.getEnclosingClass() == null)) {
System.err.println("Warning no default for " + rf.restoreString(clazz.toString())
+ " in context " + rf.restoreString(context.toString()));
count++;
}
return thisDefault;
} else {
int count = 0;
String var = "v" + varCounter ++;
indent(out);
out.println(shorten(clazz.getName()) + " " + var + ";");
indent(out);
out.println("switch(" + generatorClass + ".intValue()){");
for(Class <?> subC : subclasses) {
indent(out);
out.println("case " + count++ + ":");
beginIndent();
String defSubC = buildDefaultValue(out, subC, typeEnv);
indent(out);
out.println(var + " = " + defSubC + ";");
indent(out);
out.println("break;");
endIndent();
}
indent(out);
out.println("default:");
beginIndent();
indent(out);
out.println(var + " = " + thisDefault + ";");
endIndent();
indent(out);
out.println("}");
return var;
}
}
/**
* Goes through the different enum constants as they are more or less
* independent objects. It is not clear it is of any good.
*
* @param out
* @param enumClazz
* @param typeEnv
* @return
*/
private String buildDefaultEnumValue(PrintStream out, Class<?> enumClazz,
Map<TypeVariable<?>, Type> typeEnv) {
int count = 0;
String var = "v" + varCounter++;
String name = shorten(enumClazz.getName());
indent(out);
out.println(name + " " + var + ";");
indent(out);
out.println("switch(" + generatorClass + ".intValue()){");
for (Field field : enumClazz.getDeclaredFields()) {
if (field.isEnumConstant()) {
indent(out);
out.println("case " + count++ + ":");
beginIndent();
indent(out);
out.println(var + " = " + name + "." + field.getName() + ";");
indent(out);
out.println("break;");
endIndent();
}
}
indent(out);
out.println("default:");
beginIndent();
indent(out);
out.println(var + " = null;");
endIndent();
indent(out);
out.println("}");
return var;
}
/**
* Finds the class if it exists that is the witness for a given class and
* gives back the code that provide that witness.
*
* @param clazz
* the interface.
* @return a new expression if the implementor exists and null otherwise.
*/
private String interfaceWitness(Annotation [] annots) {
Annotation implementorAnnot = rf.findAnnotation(
EngineConstant.REAL_ANNOT, annots);
if (implementorAnnot == null) {
return "null";
} else {
String implemClazz = ReflexUtil.getStringValue(implementorAnnot);
return "new " + implemClazz + "(" + generatorClass + ".token)";
}
}
/**
* Utility method that search a string in an array of strings.
*
* @param array
* the array where the search is done
* @param searched
* the string searched.
* @return
*/
private boolean contains(String[] array, String searched) {
if (array == null)
return false;
for (String id : array) {
if (searched.equals(id))
return true;
}
return false;
}
private void buildRecursiveEnvType(Class <?> c, Map<TypeVariable<?>, Type> typeEnv) {
if (c == null || c.equals(Object.class)) return;
Type superClass = c.getGenericSuperclass();
if (superClass instanceof ParameterizedType) {
buildEnvType(typeEnv, (ParameterizedType) superClass);
}
buildRecursiveEnvType(c.getSuperclass(), typeEnv);
for(Type itfs: c.getGenericInterfaces()) {
if (itfs instanceof ParameterizedType) {
buildEnvType(typeEnv, (ParameterizedType) itfs);
}
}
for(Class <?> itf : c.getInterfaces()) buildRecursiveEnvType(itf, typeEnv);
}
/**
* Dump the real class implementing an interface to a file to a file
*
* @param out
* @param c
*/
public void dumpRealClass(Class<?> c, String classname, String superclass)
throws IOException {
PrintStream out = buildOutStream(classname);
if (out == null)
return;
try {
Map<TypeVariable<?>, Type> typeEnv = new HashMap<TypeVariable<?>, Type>();
buildRecursiveEnvType(c,typeEnv);
setPackage(out, classname);
indent(out);
String shortname = classname
.substring(classname.lastIndexOf('.') + 1);
out.print("public /* */ class ");
out.print(shortname);
typeParameters(out, c.getTypeParameters());
if (c.isInterface()) {
if (superclass != null && !superclass.equals("") && !superclass.equals("-")) {
out.print(" extends " + superclass);
}
out.print(" implements ");
type(out,c);
} else {
out.print(" extends ");
type(out,c);
}
Type [] args = c.getTypeParameters();
if (args.length > 0) {
out.print("<");
dumpListType(out,args, false);
out.print(">");
}
out.println("{");
beginIndent();
// Implemented fields
dumpAddedFields(out,c);
// Constructor
indent(out);
out.print("public ");
out.print(shortname);
out.print("(" + tokenClass + " arg0){");
beginIndent();
if ((superclass != null && !superclass.equals("") && !superclass.equals("-")) || !c.isInterface()) {
indent(out);
out.print("super(arg0);");
}
endIndent();
indent(out);
out.println("}");
// Dump constructors as calls to super if there are super constructor (abstract class)
for(Constructor<?> co : c.getDeclaredConstructors()) {
int mod = co.getModifiers();
if (!Modifier.isProtected(mod) && ! Modifier.isPublic(mod)) continue;
indent(out);
out.print("public ");
out.print(shortname);
out.print("(");
Type [] params = co.getGenericParameterTypes();
int count = 0;
for(Type param : params) {
if(count > 0) out.print(", ");
type(out, param,true, typeEnv);
out.print(" arg");
out.print(++count);
}
out.print(") ");
Type [] excs = co.getGenericExceptionTypes();
if (excs.length > 0) {
out.print(" throws ");
dumpListType(out, excs, true);
}
out.println("{");
beginIndent();
indent(out);
out.print("super(");
for(int c1=0; c1 < params.length; c1++) {
if(c1 > 0) out.print(", ");
out.print(" arg");
out.print(c1+1);
}
out.println(");");
endIndent();
indent(out);
out.println("}");
}
// Get all the public methods.
HashSet <Method> seen = new HashSet<Method>();
for (Method meth : c.getMethods()) {
if (Modifier.isAbstract(meth.getModifiers()) && !meth.isSynthetic() && isNewMethod(meth,seen)) {
dumpMethod(out, meth, false, true, typeEnv);
seen.add(meth);
}
}
// Go up the hierarchy
Class <?> parent = c;
while(parent != null && parent != java.lang.Object.class) {
for (Method meth : parent.getDeclaredMethods()) {
int mod = meth.getModifiers();
if (Modifier.isAbstract(mod) && Modifier.isProtected(mod) && isNewMethod(meth,seen))
dumpMethod(out, meth, false, true, typeEnv);
}
for(Method m: parent.getDeclaredMethods()) { seen.add(m); }
parent = parent.getSuperclass();
}
endIndent();
indent(out);
out.println("}");
} catch (Throwable e) {
e.printStackTrace();
out.println("}");
throw new IOException(e.getMessage());
}
if (!debug) {
out.close();
}
}
private boolean isNewMethod(Method meth, HashSet<Method> seen) {
String name = meth.getName();
Class<?> [] types = meth.getParameterTypes();
for(Method match: seen) {
if(match.getName().equals(name) && Arrays.equals(match.getParameterTypes(), types)) return false;
}
return true;
}
@Override
protected void dumpValueAnyClass(PrintStream out, Type typ) {
if (typ instanceof Class <?>) {
Class<?> clazz = (Class <?>) typ;
if (clazz.isArray()) {
Class<?> elt = clazz.getComponentType();
out.print("new " + shorten(ReflexUtil.readableName(elt)) + "[]{");
dumpDefaultValue(out,elt,null);
out.print("}");
} else {
if (!Modifier.isAbstract(clazz.getModifiers()) && !clazz.isEnum() && rf.handledClass(clazz)) {
out.print("new " + shorten(clazz.getName()) + "(" + generatorClass + ".token)" );
} else {
Annotation [] annots = clazz.getAnnotations();
Annotation realAnnot = rf.findAnnotation(EngineConstant.REAL_ANNOT, annots);
if (realAnnot != null) {
String name = ReflexUtil.getStringValue(realAnnot);
out.print("new " + name + "(" + generatorClass + ".token)" );
} else { out.print("null"); }
}
}
} else out.print("null");
}
@Override
protected void dumpFieldValue(PrintStream out, Field field) {
int mod = field.getModifiers();
out.print(" = ");
Annotation [] annots = field.getAnnotations();
Annotation implemAnnot = rf.findAnnotation(EngineConstant.FIELD_VALUE_IMPL_ANNOT, annots);
if (implemAnnot != null) {
out.print("new ");
out.print(ReflexUtil.getStringValue(implemAnnot));
out.print("()");
} else if (Modifier.isFinal(mod) && Modifier.isStatic(mod)) dumpValue(out,field);
else dumpFieldValue(out,field, field.getType());
}
private void dumpFieldValue(PrintStream out, Field field, Class<?> type) {
if (type.equals(java.lang.String.class)) {
out.print("\"[field:");
out.print(rf.restoreString(field.toString()));
out.print("]\"");
} else if (type.isArray()) {
Class<?> elt = type.getComponentType();
out.print("new " + shorten(ReflexUtil.readableName(elt)) + "[]{");
dumpFieldValue(out,field,elt);
out.print("}");
} else if(type.equals(boolean.class)) out.print("false");
else if (type.equals(byte.class)) out.print("(byte) 0");
else if (type.equals(char.class)) out.print("'\\u0000'");
else if (type.equals(short.class)) out.print("(short) 0");
else if (type.equals(int.class)) out.print("0");
else if (type.equals(long.class)) out.print("0l");
else if (type.equals(float.class)) out.print("0.0f");
else if (type.equals(double.class)) out.print("0.0d");
else {
dumpValueAnyClass(out, type);
}
}
@Override
protected void generateImports(PrintStream out, Class<?> c) { }
}