org.lirazs.gbackbone.gen.reflection.ReflectAllInOneCreator.java Source code

Java tutorial

Introduction

Here is the source code for org.lirazs.gbackbone.gen.reflection.ReflectAllInOneCreator.java

Source

/*******************************************************************************
 *  Copyright 2001, 2007 JamesLuo(JamesLuo.au@gmail.com)
 *  
 *  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.
 * 
 *  Contributors:
 *******************************************************************************/

package org.lirazs.gbackbone.gen.reflection;

import java.io.PrintWriter;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.typeinfo.*;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
import org.lirazs.gbackbone.common.client.CheckedExceptionWrapper;
import org.lirazs.gbackbone.gen.AnnotationsHelper;
import org.lirazs.gbackbone.gen.GenExclusion;
import org.lirazs.gbackbone.gen.GenUtils;
import org.lirazs.gbackbone.gen.LogableSourceCreator;
import org.lirazs.gbackbone.reflection.client.HasReflect;
import org.lirazs.gbackbone.reflection.client.Reflectable;
import org.lirazs.gbackbone.reflection.client.ReflectionTarget;
import org.lirazs.gbackbone.reflection.client.ReflectionUtils;
import org.lirazs.gbackbone.reflection.client.Type;
import org.lirazs.gbackbone.reflection.client.impl.TypeOracleImpl;

public class ReflectAllInOneCreator extends LogableSourceCreator {

    private List<String> allGeneratedClassNames = new ArrayList<String>();
    private Set<JClassType> relationClassesProcessed = new HashSet<JClassType>();

    JClassType javascriptClassType;

    public ReflectAllInOneCreator(TreeLogger logger, GeneratorContext context, String typeName) {
        super(logger, context, typeName);

        //      try {
        //         System.out.println(context.getPropertyOracle().getPropertyValue(logger, "locale______"));
        //      } catch (BadPropertyValueException e) {
        //         //nothing, there is no exclusion setting
        //      }
    }

    protected GenExclusion getGenExclusion() {
        return GenExclusionCompositeReflection.INSTANCE;
    }

    @Override
    protected String getSUFFIX() {
        return GenUtils.getReflection_SUFFIX();
    }

    private List<JClassType> candidateList = new ArrayList<JClassType>();
    private Map<JClassType, Reflectable> candidates = new HashMap<JClassType, Reflectable>();

    @Override
    public void createSource(SourceWriter source, JClassType classType) {
        //ClassType -->> the interface name created automatically
        Map<JClassType, String> typeNameMap = new HashMap<JClassType, String>();

        genAllClasses(source, typeNameMap);

        //      source.println("public " + getSimpleUnitName(classType) + "(){");
        //      source.indent();
        //      
        //      for (String classname : allGeneratedClassNames){
        //         source.println("new " + classname + "();");
        //      }
        //      source.outdent();
        //      source.println("}");

        source.println("public org.lirazs.gbackbone.reflection.client.Type doGetType(String name) {");
        source.indent();
        //source.println("org.lirazs.gbackbone.reflection.client.Type resultType = super.doGetType(name);");
        //source.println("if (resultType != null) {return resultType;}");

        for (JClassType type : typeNameMap.keySet()) {
            source.println("if (name.equals( \"" + type.getQualifiedSourceName() + "\")){return GWT.create("
                    + typeNameMap.get(type) + ".class);}");
        }
        source.println();
        source.println("return null;");

        source.outdent();
        source.print("}");

    }

    private void genAllClasses(SourceWriter sourceWriter, Map<JClassType, String> typeNameMap) {
        for (JClassType type : candidateList) {
            String className = type.getPackage().getName().replace('.', '_') + '_'
                    + getSimpleUnitNameWithOutSuffix(type) + "_GWTENTAUTO_ClassType"; //type.getPackage().getName().replace('.', '_') + '_' + type.getSimpleSourceName().replace('.', '_'); //getSimpleUnitName(type);

            sourceWriter.println("@ReflectionTarget(value=\"" + type.getQualifiedSourceName() + "\")");
            sourceWriter.println("public static interface " + className
                    + " extends org.lirazs.gbackbone.reflection.client.ClassType {}");

            typeNameMap.put(type, className);
        }
    }

    //TODO refactor by source visitor pattern

    private void getAllReflectionClasses() throws NotFoundException {

        //System annotations
        addClassIfNotExists(typeOracle.getType(Retention.class.getCanonicalName()),
                ReflectableHelper.getDefaultSettings(typeOracle));
        addClassIfNotExists(typeOracle.getType(Documented.class.getCanonicalName()),
                ReflectableHelper.getDefaultSettings(typeOracle));
        addClassIfNotExists(typeOracle.getType(Inherited.class.getCanonicalName()),
                ReflectableHelper.getDefaultSettings(typeOracle));
        addClassIfNotExists(typeOracle.getType(Target.class.getCanonicalName()),
                ReflectableHelper.getDefaultSettings(typeOracle));
        addClassIfNotExists(typeOracle.getType(Deprecated.class.getCanonicalName()),
                ReflectableHelper.getDefaultSettings(typeOracle));
        //typeOracle.getType("org.lirazs.gbackbone.client.test.reflection.TestReflectionGenerics.TestReflection1");

        //=====GWT0.7
        for (JClassType classType : typeOracle.getTypes()) {
            Reflectable reflectable = GenUtils.getClassTypeAnnotationWithMataAnnotation(classType,
                    Reflectable.class);
            if (reflectable != null) {
                processClass(classType, reflectable);

                if (reflectable.assignableClasses()) {
                    for (JClassType type : classType.getSubtypes()) {
                        processClass(type, reflectable);
                    }
                }
            }
        }
        //======end of gwt0.7
    }

    private void processClass(Class<?> clazz, Reflectable reflectable) {
        processClass(typeOracle.findType(ReflectionUtils.getQualifiedSourceName(clazz)), reflectable);
    }

    private void processClass(JClassType classType, Reflectable reflectable) {
        if (!genExclusion(classType)) {
            if (addClassIfNotExists(classType, reflectable)) {
                processRelationClasses(classType, reflectable);
                processAnnotationClasses(classType, reflectable);
            }
        }
    }

    private Reflectable getNearestSetting(Class<?> clazz, Reflectable defaultSetting) {
        return getNearestSetting(typeOracle.findType(ReflectionUtils.getQualifiedSourceName(clazz)),
                defaultSetting);
    }

    /**
     * Get nearest Reflectable, if not found, using defaultSetting
     * @param classType
     * @param defaultSetting
     * @return
     */
    private Reflectable getNearestSetting(JClassType classType, Reflectable defaultSetting) {
        Reflectable result = GenUtils.getClassTypeAnnotationWithMataAnnotation(classType, Reflectable.class);
        if (result != null)
            return result;
        else
            return defaultSetting;
    }

    private void processRelationClass(JClassType classType, Reflectable reflectable) {
        Reflectable nearest = getNearestSetting(classType, reflectable);
        processRelationClasses(classType, nearest);
        processAnnotationClasses(classType, nearest);
        addClassIfNotExists(classType, nearest);
    }

    private boolean hasReflection(HasAnnotations type) {
        return type.getAnnotation(HasReflect.class) != null;
    }

    private boolean hasReflectionAnnotation(HasAnnotations type) {
        return (type.getAnnotation(HasReflect.class) != null) && type.getAnnotation(HasReflect.class).annotation();
    }

    private void processRelationClasses(JClassType classType, Reflectable reflectable) {
        if (classType == null)
            return;

        if (classType.isParameterized() != null)
            classType = classType.isParameterized().getBaseType();

        if (classType.isRawType() != null || classType.isWildcard() != null || classType.isTypeParameter() != null)
            classType = classType.getErasedType();

        if (relationClassesProcessed.contains(classType))
            return;

        processAnnotationClasses(classType, reflectable);

        if (reflectable.superClasses()) {
            if (classType.getSuperclass() != null) {
                processRelationClass(classType.getSuperclass(), reflectable);
            }
        }

        if (reflectable.relationTypes()) {
            for (JClassType type : classType.getImplementedInterfaces()) {
                processRelationClass(type, reflectable);
            }
        }

        relationClassesProcessed.add(classType);

        processFields(classType, reflectable);

        processMethods(classType, reflectable);
    }

    private void processFields(JClassType classType, Reflectable reflectable) {
        boolean need = reflectable.relationTypes();

        for (JField field : classType.getFields()) {
            Annotation[] annotations = AnnotationsHelper.getAnnotations(field);
            if ((reflectable.fieldAnnotations() && annotations.length > 0) || (hasReflectionAnnotation(field))) {
                processAnnotationClasses(field, reflectable);

                JClassType type = field.getType().isClassOrInterface();
                if (type != null)
                    if (need || (hasReflection(field) && field.getAnnotation(HasReflect.class).fieldType()))
                        if (!type.isAssignableTo(classType)) //some times, it's itself of devided class
                            processRelationClasses(type, reflectable);

                addClassIfNotExists(type, reflectable);
            }

        }
    }

    private void processMethods(JClassType classType, Reflectable reflectable) {
        boolean need = reflectable.relationTypes();
        for (JMethod method : classType.getMethods()) {
            if (reflectable.fieldAnnotations() || (hasReflectionAnnotation(method))) {
                processAnnotationClasses(method, reflectable);

                HasReflect hasReflect = method.getAnnotation(HasReflect.class);
                JClassType type = null;

                if (need || (hasReflect != null && hasReflect.resultType())) {
                    if (method.getReturnType() != null && method.getReturnType().isClassOrInterface() != null) {
                        type = method.getReturnType().isClassOrInterface();

                        if (!type.isAssignableTo(classType))
                            processRelationClasses(type, reflectable);

                        addClassIfNotExists(type, reflectable);
                    }
                }

                if (need || (hasReflect != null && hasReflect.parameterTypes())) {
                    for (JParameter parameter : method.getParameters()) {
                        if (parameter.getType() != null && parameter.getType().isClassOrInterface() != null) {
                            type = parameter.getType().isClassOrInterface();

                            if (!type.isAssignableTo(classType))
                                processRelationClasses(type, reflectable);

                            addClassIfNotExists(type, reflectable);
                        }
                    }
                }
            }
        }
    }

    private void processAnnotationClasses(HasAnnotations annotations, Reflectable reflectable) {
        if (!reflectable.classAnnotations())
            return;

        Annotation[] annos = AnnotationsHelper.getAnnotations(annotations);
        if (annos == null)
            return;

        for (Annotation annotation : annos) {
            processAnnotation(annotation);
        }
    }

    private Reflectable getFullSettings() {
        return ReflectableHelper.getFullSettings(typeOracle);
    }

    private void processClassFromAnnotationValue(Object value) {
        if (value != null && value instanceof Class && (!((Class) value).getName().equals("void"))) {
            processClass((Class) value, getNearestSetting((Class) value, getFullSettings()));
        }
    }

    private void processAnnotation(Annotation annotation) {
        if (annotation.annotationType().getName().startsWith("java.lang.annotation")) {
            return; //Document's parent is itself, must check here
        } else {
            JClassType classType = this.typeOracle
                    .findType(ReflectionUtils.getQualifiedSourceName(annotation.annotationType()));

            if (classType == null)
                return; //

            addClassIfNotExists(classType, getNearestSetting(classType, getFullSettings()));

            //Go through all annotation methods, if has class, add that class to reflection as well
            JAnnotationType annoType = classType.isAnnotation();

            // JAnnotationMethod[] methods = annoType.getMethods();
            JAnnotationMethod[] methods = (JAnnotationMethod[]) annoType.getMethods();
            for (JAnnotationMethod method : methods) {
                Object value = null;
                try {
                    value = annotation.annotationType().getMethod(method.getName(), new Class[] {})
                            .invoke(annotation, null);
                    //System.out.println(value);
                    //System.out.println(value.getClass());
                    if (value instanceof Class) {
                        processClassFromAnnotationValue(value);
                    } else if (value.getClass().isArray()) {
                        for (int i = 0; i < Array.getLength(value); i++) {
                            if (Array.get(value, i) instanceof Class)
                                processClassFromAnnotationValue(Array.get(value, i));
                        }
                    } else if (value instanceof Annotation) {
                        processAnnotation((Annotation) value);
                    }
                } catch (Exception e) {
                    throw new CheckedExceptionWrapper(e);
                }

                if (method.getReturnType() != null) {
                    JType type = method.getReturnType();
                    processJType(type);

                }
            }

            Class<? extends Annotation> annotationType = annotation.annotationType();
            Annotation[] metaAnnotations = annotationType.getAnnotations();
            for (Annotation metaAnnotation : metaAnnotations) {
                processAnnotation(metaAnnotation);
            }
        }
    }

    private void processJType(JType type) {
        JClassType classType = null;
        if (type.isClassOrInterface() != null) {
            classType = type.isClassOrInterface();
        } else if (type.isArray() != null) {
            processJType(type.isArray().getComponentType());
        } else if (type.isAnnotation() != null) {
            classType = type.isAnnotation();
        }

        if (classType != null)
            processClass(classType, getNearestSetting(classType, getFullSettings()));
    }

    private boolean addClassIfNotExists(JClassType classType, Reflectable setting) {
        //Add next line we can make sure we just append normal class type, always get from TypeOracle
        //not JParameterizedType or JTypeParameter etc...
        //RC2 we support ParameterizedType now.

        if (classType != null && classType.isParameterized() == null) {
            //         System.out.println("addClassIfNotExists: " + classType.getQualifiedSourceName());
            classType = this.typeOracle.findType(classType.getQualifiedSourceName());
        }

        //we just process public classes
        if ((classType == null) || (classType.isPrivate()) || (classType.isProtected())
                || (GeneratorHelper.isSystemClass(classType) && !classType.isPublic()))
            return false;

        String qualifiedSourceName = classType.getQualifiedSourceName();

        //no need java.lang.class
        if (qualifiedSourceName.equals("java.lang.Class"))
            return false;

        //no need for system or gwt core classes
        if (qualifiedSourceName.contains("java.lang") || qualifiedSourceName.contains("java.util")
                || qualifiedSourceName.contains("java.io") || qualifiedSourceName.contains("com.google.gwt"))
            return false;

        // if class is extending javascript object we cannot create reflection for it.
        if ((javascriptClassType != null && classType.isAssignableTo(javascriptClassType)))
            return false;

        if (candidateList.indexOf(classType.getErasedType()) < 0) {
            candidateList.add(classType.getErasedType());
            candidates.put(classType.getErasedType(), setting);
            return true;
        }

        return false;
    }

    protected SourceWriter doGetSourceWriter(JClassType classType) throws NotFoundException {
        try {
            javascriptClassType = typeOracle.getType(JavaScriptObject.class.getName());
        } catch (NotFoundException e) {
            e.printStackTrace();
        }

        if (candidates.size() <= 0) {
            getAllReflectionClasses();
        }

        String packageName = classType.getPackage().getName();
        String simpleName = getSimpleUnitName(classType);
        ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory(packageName, simpleName);
        composer.setSuperclass(TypeOracleImpl.class.getCanonicalName());

        composer.addImport("org.lirazs.gbackbone.reflection.client.*");
        composer.addImport(classType.getPackage().getName() + ".*");

        composer.addImport("org.lirazs.gbackbone.reflection.client.impl.*");
        composer.addImport("com.google.gwt.core.client.*");
        composer.addImport("java.util.*");

        //James remove the following, some times client package have the same 
        //class name which is used by system(ie: Map), if using both package
        //Compiler will raise error.
        //      Set<String> imports = new HashSet<String>();
        //      for (JClassType aClassType : allReflectionClasses){
        //         String str = aClassType.getPackage().getName() + ".*";
        //         if (! imports.contains(str)){
        //            imports.add(str);
        //            composer.addImport(str);
        //         }
        //      }

        PrintWriter printWriter = context.tryCreate(logger, packageName, simpleName);
        if (printWriter == null) {
            return null;
        } else {
            SourceWriter sw = composer.createSourceWriter(context, printWriter);
            return sw;
        }
    }

}