/** * Copyright 2004-2016 Riccardo Solmi. All rights reserved. * This file is part of the Whole Platform. * * The Whole Platform is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The Whole Platform is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the Whole Platform. If not, see <http://www.gnu.org/licenses/>. */ package org.whole.lang.java.codebase; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.GenericArrayType; 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.lang.reflect.WildcardType; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; import org.whole.lang.builders.IBuilderOperation; import org.whole.lang.commons.builders.ICommonsBuilder; import org.whole.lang.commons.reflect.CommonsLanguageKit; import org.whole.lang.java.builders.IJavaBuilder; import org.whole.lang.java.model.CompilationUnit; import org.whole.lang.java.model.ModifierEnum; import org.whole.lang.java.reflect.JavaLanguageKit; import org.whole.lang.reflect.ReflectionFactory; import org.whole.lang.templates.AbstractTemplateFactory; import org.whole.lang.util.UniqueIdGenerator; /** * @author Enrico Persiani */ public class JavaClassTemplateFactory extends AbstractTemplateFactory<CompilationUnit> { protected Class<?> clazz; protected IJavaBuilder jb; protected ICommonsBuilder cb; protected boolean useCanonicalNames; public JavaClassTemplateFactory() { this((Class<?>)null); } public JavaClassTemplateFactory(String className) throws ClassNotFoundException { this(Class.forName(className, false, ReflectionFactory.getPlatformClassLoader())); } public JavaClassTemplateFactory(Class<?> clazz) { this.clazz = clazz; this.useCanonicalNames = true; } public JavaClassTemplateFactory useCanonicalNames(boolean useCanonicalNames) { this.useCanonicalNames = useCanonicalNames; return this; } public void apply(IBuilderOperation op) { cb = (ICommonsBuilder) op.wGetBuilder(CommonsLanguageKit.URI); jb = (IJavaBuilder) op.wGetBuilder(JavaLanguageKit.URI); buildCompilationUnit(); } protected void buildCompilationUnit() { jb.CompilationUnit_(); buildPackageDeclaration(clazz); buildImportDeclarations(clazz); buildTypeDeclarations(clazz); jb._CompilationUnit(); } protected void buildPackageDeclaration(Class<?> clazz) { jb.PackageDeclaration_(); cb.Resolver(); // JavaDoc cb.Resolver(); // Annotations jb.QualifiedName(clazz.getPackage().getName()); jb._PackageDeclaration(); } protected void buildImportDeclarations(Class<?> clazz) { // uses qualified names in place jb.ImportDeclarations(); } protected void buildTypeDeclarations(Class<?> clazz) { jb.TypeDeclarations_(); buildTypeDeclaration(clazz); jb._TypeDeclarations(); } @SuppressWarnings("unchecked") private void buildTypeDeclaration(Class<?> clazz) { if (clazz.isAnonymousClass()) return; if (clazz.isAnnotation()) buildAnnotationDeclaration((Class<Annotation>) clazz); else if (clazz.isInterface()) buildInterfaceDeclaration(clazz); else if (clazz.isEnum()) buildEnumDeclaration((Class<Enum<?>>) clazz); else buildClassDeclaration(clazz); } protected void buildAnnotationDeclaration(Class<? extends Annotation> clazz) { jb.AnnotationTypeDeclaration_(); cb.Resolver(); // JavaDoc // class modifiers buildExtendedModifiers(clazz.getDeclaredAnnotations(), clazz.getModifiers()); // annotation name buildSimpleName(clazz.getSimpleName()); // annotation type member declarations ... jb.BodyDeclarations_(); for (Method method : asSortedList(clazz.getDeclaredMethods())) buildAnnotationMember(method); for (Class<?> innerClazz : asSortedList(clazz.getDeclaredClasses())) buildTypeDeclaration(innerClazz); jb._BodyDeclarations(); jb._AnnotationTypeDeclaration(); } protected List<Method> asSortedList(Method... methods) { List<Method> methodList = Arrays.asList(methods); Collections.sort(methodList, new Comparator<Method>() { public int compare(Method m1, Method m2) { int result = m1.getName().compareTo(m2.getName()); if (result != 0) return result; Class<?>[] pts1 = m1.getParameterTypes(); Class<?>[] pts2 = m2.getParameterTypes(); result = pts1.length - pts2.length; if (result != 0) return result; for (int i=0; i<pts1.length; i++) { result = pts1[i].getName().compareTo(pts2[i].getName()); if (result != 0) return result; } return 0; // unreachable } }); return methodList; } protected List<Constructor<?>> asSortedList(Constructor<?>... constructors) { List<Constructor<?>> constructorList = Arrays.asList(constructors); Collections.sort(constructorList, new Comparator<Constructor<?>>() { public int compare(Constructor<?> c1, Constructor<?> c2) { Class<?>[] pts1 = c1.getParameterTypes(); Class<?>[] pts2 = c2.getParameterTypes(); int result = pts1.length - pts2.length; if (result != 0) return result; for (int i=0; i<pts1.length; i++) { result = pts1[i].getName().compareTo(pts2[i].getName()); if (result != 0) return result; } return 0; // unreachable } }); return constructorList; } protected List<Field> asSortedList(Field... fields) { List<Field> filedList = Arrays.asList(fields); Collections.sort(filedList, new Comparator<Field>() { public int compare(Field f1, Field f2) { return f1.getName().compareTo(f2.getName()); } }); return filedList; } protected List<Class<?>> asSortedList(Class<?>... classes) { List<Class<?>> classList = Arrays.asList(classes); Collections.sort(classList, new Comparator<Class<?>>() { public int compare(Class<?> c1, Class<?> c2) { return c1.getName().compareTo(c2.getName()); } }); return classList; } protected List<Annotation> asSortedList(Annotation... annotations) { List<Annotation> annotationList = Arrays.asList(annotations); Collections.sort(annotationList, new Comparator<Annotation>() { public int compare(Annotation a1, Annotation a2) { return a1.annotationType().getName().compareTo(a2.annotationType().getName()); } }); return annotationList; } protected List<Enum<?>> asSortedList(Enum<?>... enums) { List<Enum<?>> enumList = Arrays.asList(enums); Collections.sort(enumList, new Comparator<Enum<?>>() { public int compare(Enum<?> e1, Enum<?> e2) { return e1.name().compareTo(e2.name()); } }); return enumList; } protected void buildInterfaceDeclaration(Class<?> clazz) { jb.InterfaceDeclaration_(); cb.Resolver(); // JavaDoc // interface modifiers buildExtendedModifiers(clazz.getDeclaredAnnotations(), clazz.getModifiers()); // interface name buildSimpleName(clazz.getSimpleName()); // generic type parameters buildTypeParameters(clazz.getTypeParameters()); // extends buildTypes(clazz.getGenericInterfaces()); // fields, methods, annotations, inner classes ... jb.BodyDeclarations_(); for (Field filed : asSortedList(clazz.getDeclaredFields())) buildField(filed); for (Method method : asSortedList(clazz.getDeclaredMethods())) buildMethod(method); for (Class<?> innerClazz : asSortedList(clazz.getDeclaredClasses())) buildTypeDeclaration(innerClazz); jb._BodyDeclarations(); jb._InterfaceDeclaration(); } protected void buildEnumDeclaration(Class<Enum<?>> clazz) { jb.EnumDeclaration_(); cb.Resolver(); // JavaDoc // enumeration modifiers buildExtendedModifiers(clazz.getDeclaredAnnotations(), clazz.getModifiers(), Modifier.TRANSIENT | Modifier.VOLATILE | Modifier.FINAL); // enumeration name buildSimpleName(clazz.getSimpleName()); // extends buildTypes(clazz.getGenericInterfaces()); // enumeration constants buildEnumConstants(clazz.getEnumConstants()); // fields, methods, annotations, inner classes ... jb.BodyDeclarations_(); for (Field field : asSortedList(clazz.getDeclaredFields())) if (!field.isEnumConstant()) buildField(field); for (Constructor<?> constructor : asSortedList(clazz.getDeclaredConstructors())) buildConstructor(constructor); for (Method method : asSortedList(clazz.getDeclaredMethods())) { String methodName = method.getName(); Class<?> methodReturnType = method.getReturnType(); Class<?>[] methodParameterTypes = method.getParameterTypes(); if ("valueOf".equals(methodName) && clazz.equals(methodReturnType) && methodParameterTypes.length == 1 && String.class.equals(methodParameterTypes[0])) continue; else if ("values".equals(methodName) && methodReturnType.isArray() && clazz.equals(methodReturnType.getComponentType()) && methodParameterTypes.length == 0) continue; else buildMethod(method); } for (Class<?> innerClazz : asSortedList(clazz.getDeclaredClasses())) buildTypeDeclaration(innerClazz); jb._BodyDeclarations(); jb._EnumDeclaration(); } protected void buildClassDeclaration(Class<?> clazz) { jb.ClassDeclaration_(); cb.Resolver(); // JavaDoc // class modifiers buildExtendedModifiers(clazz.getDeclaredAnnotations(), clazz.getModifiers()); // class name buildSimpleName(clazz.getSimpleName()); // generic type parameters buildTypeParameters(clazz.getTypeParameters()); // extends Class<?> superclass = clazz.getSuperclass(); if (Object.class.equals(superclass)) cb.Resolver(); else buildType(superclass); // implements buildTypes(clazz.getGenericInterfaces()); // fields, methods, annotations, inner classes ... jb.BodyDeclarations_(); for (Field filed : asSortedList(clazz.getDeclaredFields())) buildField(filed); for (Constructor<?> constructor : asSortedList(clazz.getDeclaredConstructors())) buildConstructor(constructor); for (Method method : asSortedList(clazz.getDeclaredMethods())) buildMethod(method); for (Class<?> innerClazz : asSortedList(clazz.getDeclaredClasses())) buildTypeDeclaration(innerClazz); jb._BodyDeclarations(); jb._ClassDeclaration(); } protected void buildField(Field field) { if (field.isSynthetic()) return; jb.FieldDeclaration_(); cb.Resolver(); // JavaDoc // field modifiers buildExtendedModifiers(field.getAnnotations(), field.getModifiers(), 0); // filed type buildType(field.getGenericType()); // field declaration jb.VariableDeclarationFragments_(); jb.VariableDeclarationFragment_(); buildSimpleName(field.getName()); cb.Resolver(); // extra dimensions cb.Resolver(); // initializer jb._VariableDeclarationFragment(); jb._VariableDeclarationFragments(); jb._FieldDeclaration(); } protected void buildConstructor(Constructor<?> constructor) { if (constructor.isSynthetic()) return; jb.ConstructorDeclaration_(); cb.Resolver(); // JavaDoc // constructor modifiers buildExtendedModifiers(constructor.getAnnotations(), constructor.getModifiers()); // generic type parameters buildTypeParameters(constructor.getTypeParameters()); // constructor name String name = constructor.getName().replaceAll(".*\\$", ""); buildSimpleName(name); // constructor parameters buildParameters(constructor.getGenericParameterTypes(), constructor.isVarArgs()); cb.Resolver(); // extra dimensions // exceptions buildExceptions(constructor.getGenericExceptionTypes()); jb.Block(); // empty body jb._ConstructorDeclaration(); } protected void buildAnnotationMember(Method method) { jb.AnnotationTypeMemberDeclaration_(); cb.Resolver(); // JavaDoc // member modifiers int modifiers = method.getModifiers(); buildExtendedModifiers(method.getAnnotations(), modifiers, Modifier.TRANSIENT | Modifier.VOLATILE | Modifier.ABSTRACT | Modifier.PUBLIC); // member name buildSimpleName(method.getName()); // member type buildType(method.getGenericReturnType()); cb.Resolver(); // default jb._AnnotationTypeMemberDeclaration(); } protected void buildMethod(Method method) { if (method.isSynthetic()) return; jb.MethodDeclaration_(); cb.Resolver(); // JavaDoc // method modifiers int modifiers = method.getModifiers(); buildExtendedModifiers(method.getAnnotations(), modifiers); // generic type parameters buildTypeParameters(method.getTypeParameters()); // return type buildType(method.getGenericReturnType()); // method name buildSimpleName(method.getName()); // method parameters buildParameters(method.getGenericParameterTypes(), method.isVarArgs()); cb.Resolver(); // extra dimensions // exceptions buildExceptions(method.getGenericExceptionTypes()); if (method.getDeclaringClass().isInterface() || Modifier.isAbstract(modifiers)) // method body cb.Resolver(); else jb.Block(); jb._MethodDeclaration(); } protected void buildEnumConstants(Enum<?>[] enumConstants){ if (enumConstants.length == 0) return; jb.EnumConstants_(); for (Enum<?> enumConstant : asSortedList(enumConstants)) { jb.EnumConstantDeclaration_(); cb.Resolver(); // JavaDoc cb.Resolver(); // modifiers buildSimpleName(enumConstant.name()); cb.Resolver(); // arguments cb.Resolver(); // anonymous class declaration jb._EnumConstantDeclaration(); } jb._EnumConstants(); } protected void buildParameters(Type[] parameterTypes, boolean hasVarArgs) { jb.Parameters_(); UniqueIdGenerator idgen = UniqueIdGenerator.newUniqueIdGenerator("arg", 0); for (int i=0; i<parameterTypes.length; i++) { Type parameterType = parameterTypes[i]; jb.SingleVariableDeclaration_(); cb.Resolver(); // modifiers if (hasVarArgs && i == parameterTypes.length -1) { if (parameterType instanceof GenericArrayType) buildType(((GenericArrayType) parameterType).getGenericComponentType()); else buildType(((Class<?>) parameterType).getComponentType()); jb.Varargs(true); } else { buildType(parameterType); cb.Resolver(); } buildSimpleName(idgen.next()); cb.Resolver(); // extra dimensions cb.Resolver(); // expression jb._SingleVariableDeclaration(); } jb._Parameters(); } protected void buildExtendedModifiers(Annotation[] annotations, int modifiers) { buildExtendedModifiers(annotations, modifiers, Modifier.TRANSIENT | Modifier.VOLATILE); } protected void buildExtendedModifiers(Annotation[] annotations, int modifiers, int ignoreModifiers) { jb.ExtendedModifiers_(); buildModifiers(modifiers, ignoreModifiers); for (Annotation annotation : asSortedList(clazz.getDeclaredAnnotations())) buildAnnotation(annotation.annotationType()); jb._ExtendedModifiers(); } private void buildAnnotation(Class<? extends Annotation> clazz) { //FIXME there's no support for finding annotations } protected void buildModifiers(int modifier, int ignoreModifiers) { if(!Modifier.isPrivate(ignoreModifiers) && Modifier.isPrivate(modifier)) jb.Modifier(ModifierEnum._private); if(!Modifier.isProtected(ignoreModifiers) && Modifier.isProtected(modifier)) jb.Modifier(ModifierEnum._protected); if(!Modifier.isPublic(ignoreModifiers) && Modifier.isPublic(modifier)) jb.Modifier(ModifierEnum._public); if(!Modifier.isAbstract(ignoreModifiers) && Modifier.isAbstract(modifier)) jb.Modifier(ModifierEnum._abstract); if(!Modifier.isStatic(ignoreModifiers) && Modifier.isStatic(modifier)) jb.Modifier(ModifierEnum._static); if(!Modifier.isFinal(ignoreModifiers) && Modifier.isFinal(modifier)) jb.Modifier(ModifierEnum._final); if(!Modifier.isNative(ignoreModifiers) && Modifier.isNative(modifier)) jb.Modifier(ModifierEnum._native); if(!Modifier.isSynchronized(ignoreModifiers) && Modifier.isSynchronized(modifier)) jb.Modifier(ModifierEnum._synchronized); if(!Modifier.isTransient(ignoreModifiers) && Modifier.isTransient(modifier)) jb.Modifier(ModifierEnum._transient); if(!Modifier.isVolatile(ignoreModifiers) && Modifier.isVolatile(modifier)) jb.Modifier(ModifierEnum._volatile); if(!Modifier.isStrict(ignoreModifiers) && Modifier.isStrict(modifier)) jb.Modifier(ModifierEnum.strictftp); } protected void buildExceptions(Type[] exceptionTypes) { if (exceptionTypes.length == 0) cb.Resolver(); else { jb.Types_(); for (Type exceptionType : exceptionTypes) buildType(exceptionType); jb._Types(); } } protected void buildTypes(Type[] types) { if (types.length == 0) cb.Resolver(); else { jb.Types_(); for (Type interfaceType : types) buildType(interfaceType); jb._Types(); } } protected void buildTypeParameters(TypeVariable<?>[] typeParameters) { jb.TypeParameters_(); for (TypeVariable<?> typeParameter : typeParameters) { jb.TypeParameter_(); buildSimpleName(typeParameter.getName()); buildTypes(typeParameter.getBounds()); jb._TypeParameter(); } jb._TypeParameters(); } protected void buildType(Type type) { if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) type; Type[] typeArguments = parameterizedType.getActualTypeArguments(); Type rawType = parameterizedType.getRawType(); jb.ParameterizedType_(); buildType(rawType); buildTypes(typeArguments); jb._ParameterizedType(); } else if (type instanceof GenericArrayType) { jb.ArrayType_(); buildType(((GenericArrayType) type).getGenericComponentType()); jb._ArrayType(); } else if (type instanceof TypeVariable) { TypeVariable<?> typeVariable = (TypeVariable<?>) type; jb.SimpleType(typeVariable.getName()); } else if (type instanceof WildcardType) { jb.WildcardType_(); WildcardType wildcardType = (WildcardType) type; if (wildcardType.getLowerBounds().length == 0) { Type upperBound = wildcardType.getUpperBounds()[0]; if (Object.class.equals(upperBound)) { cb.Resolver(); jb.UpperBound(); } else { buildType(upperBound); jb.UpperBound(true); } } else { buildType(wildcardType.getLowerBounds()[0]); jb.UpperBound(false); } jb._WildcardType(); } else { // non generic types Class<?> clazz = (Class<?>) type; if (clazz.isArray()) { jb.ArrayType_(); buildType(clazz.getComponentType()); jb._ArrayType(); } else if (clazz.isPrimitive()) { jb.PrimitiveType(clazz.getName()); } else { String canonicalName = clazz.getCanonicalName(); if (canonicalName != null) jb.QualifiedType(useCanonicalNames ? canonicalName : clazz.getName()); else jb.SimpleType(clazz.getSimpleName()); } } } protected void buildSimpleName(String name) { jb.SimpleName(name); } protected void buildQualifiedName(String name) { jb.QualifiedName(name); } }