Package com.mysema.query.apt

Source Code of com.mysema.query.apt.AbstractQuerydslProcessor

/*
* Copyright 2011, Mysema Ltd
*
* 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 com.mysema.query.apt;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.*;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.NoType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
import javax.tools.Diagnostic.Kind;
import javax.tools.JavaFileObject;
import java.io.IOException;
import java.io.Writer;
import java.lang.annotation.Annotation;
import java.util.*;

import com.mysema.codegen.JavaWriter;
import com.mysema.codegen.model.Parameter;
import com.mysema.codegen.model.Type;
import com.mysema.codegen.model.TypeCategory;
import com.mysema.query.annotations.QueryDelegate;
import com.mysema.query.annotations.QueryExclude;
import com.mysema.query.annotations.QueryProjection;
import com.mysema.query.codegen.*;

/**
* Base class for Querydsl annotation processors, contains the main processing logic. The subclasses
* just provide the configuration.
*
* @author tiwe
*
*/
public abstract class AbstractQuerydslProcessor extends AbstractProcessor {

    // TODO replace with proper injections in Querydsl 4.0.0
    public static Types TYPES;

    public static Elements ELEMENTS;

    public static final Boolean ALLOW_OTHER_PROCESSORS_TO_CLAIM_ANNOTATIONS = Boolean.FALSE;

    private final TypeExtractor typeExtractor = new TypeExtractor(true);

    private Configuration conf;

    private RoundEnvironment roundEnv;

    private ExtendedTypeFactory typeFactory;

    private TypeElementHandler elementHandler;

    private Context context;

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        TYPES = processingEnv.getTypeUtils();
        ELEMENTS = processingEnv.getElementUtils();

        processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Running " + getClass().getSimpleName());

        if (roundEnv.processingOver() || annotations.size() == 0) {
            return ALLOW_OTHER_PROCESSORS_TO_CLAIM_ANNOTATIONS;
        }

        if (roundEnv.getRootElements() == null || roundEnv.getRootElements().isEmpty()) {
            processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "No sources to process");
            return ALLOW_OTHER_PROCESSORS_TO_CLAIM_ANNOTATIONS;
        }

        conf = createConfiguration(roundEnv);
        context = new Context();
        Set<Class<? extends Annotation>> entityAnnotations = conf.getEntityAnnotations();
        TypeMappings typeMappings = conf.getTypeMappings();
        QueryTypeFactory queryTypeFactory = conf.getQueryTypeFactory();
        this.typeFactory = new ExtendedTypeFactory(processingEnv, conf, entityAnnotations, typeMappings, queryTypeFactory);
        elementHandler = new TypeElementHandler(conf, typeFactory, typeMappings, queryTypeFactory);
        this.roundEnv = roundEnv;

        // process annotations
        processAnnotations();

        validateMetaTypes();

        // serialize created types
        serializeMetaTypes();

        return ALLOW_OTHER_PROCESSORS_TO_CLAIM_ANNOTATIONS;
    }

    private void processAnnotations() {
        processExclusions();

        Set<TypeElement> elements = collectElements();

        // create meta models
        for (Element element : elements) {
            typeFactory.getEntityType(element.asType(), false);
        }
        for (Element element : elements) {
            typeFactory.getEntityType(element.asType(), true);
        }

        // add properties
        boolean embeddableAnn = conf.getEmbeddableAnnotation() != null;
        boolean altEntityAnn = conf.getAlternativeEntityAnnotation() != null;
        boolean superAnn = conf.getSuperTypeAnnotation() != null;
        for (TypeElement element : elements) {
            EntityType entityType = elementHandler.handleEntityType(element);
            registerTypeElement(entityType.getFullName(), element);
            if (element.getAnnotation(conf.getEntityAnnotation()) != null) {
                context.entityTypes.put(entityType.getFullName(), entityType);
            } else if (altEntityAnn && element.getAnnotation(conf.getAlternativeEntityAnnotation()) != null) {
                context.entityTypes.put(entityType.getFullName(), entityType);
            } else if (embeddableAnn && element.getAnnotation(conf.getEmbeddableAnnotation()) != null ) {
                context.embeddableTypes.put(entityType.getFullName(), entityType);
            } else if (superAnn && element.getAnnotation(conf.getSuperTypeAnnotation()) != null) {
                context.supertypes.put(entityType.getFullName(), entityType);
            } else if (!entityType.getDelegates().isEmpty()) {
                context.extensionTypes.put(entityType.getFullName(), entityType);
            } else {
                context.embeddableTypes.put(entityType.getFullName(), entityType);
            }
            context.allTypes.put(entityType.getFullName(), entityType);
        }

        // track also methods from external entity types
        for (EntityType entityType : new ArrayList<EntityType>(typeFactory.getEntityTypes())) {
            String fullName = entityType.getFullName();
            if (!context.allTypes.keySet().contains(fullName)) {
                //System.err.println(fullName);
                TypeElement element = processingEnv.getElementUtils().getTypeElement(fullName);
                if (element != null) {
                    elementHandler.handleEntityType(element);
                }
            }
        }

        // add external parents
        for (Element element : elements) {
            EntityType entityType = typeFactory.getEntityType(element.asType(), false);
            addExternalParents(entityType);
        }

        // add properties from parents
        Set<EntityType> handled = new HashSet<EntityType>();
        for (EntityType entityType : context.allTypes.values()) {
            addSupertypeFields(entityType, handled);
        }

        processProjectionTypes(elements);

        // extend entity types
        typeFactory.extendTypes();

        context.clean();

    }

    private void addExternalParents(EntityType entityType) {
        Deque<Type> superTypes = new ArrayDeque<Type>();
        if (entityType.getSuperType() != null) {
            superTypes.push(entityType.getSuperType().getType());
        }

        while (!superTypes.isEmpty()) {
            Type superType = superTypes.pop();
            if (!context.allTypes.containsKey(superType.getFullName())) {
                TypeElement typeElement = processingEnv.getElementUtils().getTypeElement(superType.getFullName());
                if (conf.isStrictMode() && !TypeUtils.hasAnnotationOfType(typeElement, conf.getEntityAnnotations())) {
                    continue;
                }
                if (typeElement == null) {
                    throw new IllegalStateException("Found no type for " + superType.getFullName());
                }
                EntityType superEntityType = elementHandler.handleEntityType(typeElement);
                if (superEntityType.getSuperType() != null) {
                    superTypes.push(superEntityType.getSuperType().getType());
                }
                context.allTypes.put(superType.getFullName(), superEntityType);
            }
        }

    }

    protected Set<TypeElement> collectElements() {
        Set<TypeElement> elements = new HashSet<TypeElement>();

        // from delegate methods
        elements.addAll(processDelegateMethods());

        // from class annotations
        for (Class<? extends Annotation> annotation : conf.getEntityAnnotations()) {
            for (Element element : getElements(annotation)) {
                if (element instanceof TypeElement) {
                    elements.add((TypeElement) element);
                }
            }
        }

        // from package annotations
        if (conf.getEntitiesAnnotation() != null) {
            for (Element element : getElements(conf.getEntitiesAnnotation())) {
                AnnotationMirror mirror = TypeUtils.getAnnotationMirrorOfType(element, conf.getEntitiesAnnotation());
                elements.addAll(TypeUtils.getAnnotationValuesAsElements(mirror, "value"));
            }
        }

        // from embedded annotations
        if (conf.getEmbeddedAnnotation() != null) {
            elements.addAll(getEmbeddedTypes());
        }

        // from embedded
        if (conf.isUnknownAsEmbedded()) {
            elements.addAll(getTypeFromProperties(elements));
        }

        // from annotation less supertypes
        if (!conf.isStrictMode()) {
            elements.addAll(getAnnotationlessSupertypes(elements));
        }

        // register possible embedded types of non-tracked supertypes
        if (conf.getEmbeddedAnnotation() != null) {
            Class<? extends Annotation> embedded = conf.getEmbeddedAnnotation();
            Set<TypeElement> embeddedElements = new HashSet<TypeElement>();
            for (TypeElement element : elements) {
                TypeMirror superTypeMirror = element.getSuperclass();
                while (superTypeMirror != null) {
                    TypeElement superTypeElement = (TypeElement) processingEnv.getTypeUtils().asElement(superTypeMirror);
                    if (superTypeElement != null) {
                        List<? extends Element> enclosed = superTypeElement.getEnclosedElements();
                        for (Element child : enclosed) {
                            if (child.getAnnotation(embedded) != null) {
                                handleEmbeddedType(child, embeddedElements);
                            }
                        }
                        superTypeMirror = superTypeElement.getSuperclass();
                        if (superTypeMirror instanceof NoType) {
                            superTypeMirror = null;
                        }
                    } else {
                        superTypeMirror = null;
                    }
                }
            }

            // register found elements
            for (TypeElement element : embeddedElements) {
                if (!elements.contains(element)) {
                    elementHandler.handleEntityType(element);
                }
            }
        }


        return elements;
    }

    private Set<TypeElement> getAnnotationlessSupertypes(Set<TypeElement> elements) {
        Set<TypeElement> rv = new HashSet<TypeElement>();
        for (TypeElement element : elements) {
            TypeMirror superTypeMirror = element.getSuperclass();
            while (superTypeMirror != null) {
                TypeElement superTypeElement = (TypeElement) processingEnv.getTypeUtils().asElement(superTypeMirror);
                if (superTypeElement != null
                        && !superTypeElement.toString().startsWith("java.lang.")
                        && !TypeUtils.hasAnnotationOfType(superTypeElement, conf.getEntityAnnotations())) {
                    rv.add(superTypeElement);
                    superTypeMirror = superTypeElement.getSuperclass();
                    if (superTypeMirror instanceof NoType) {
                        superTypeMirror = null;
                    }
                } else {
                    superTypeMirror = null;
                }
            }
        }
        return rv;
    }

    private void registerTypeElement(String entityName, TypeElement element) {
        Set<TypeElement> elements = context.typeElements.get(entityName);
        if (elements == null) {
            elements = new HashSet<TypeElement>();
            context.typeElements.put(entityName, elements);
        }
        elements.add(element);
    }

    private void processProjectionTypes(Set<TypeElement> elements) {
        Set<Element> visited = new HashSet<Element>();
        for (Element element : getElements(QueryProjection.class)) {
            Element parent = element.getEnclosingElement();
            if (!elements.contains(parent) && !visited.contains(parent)) {
                EntityType model = elementHandler.handleProjectionType((TypeElement)parent);
                registerTypeElement(model.getFullName(), (TypeElement)parent);
                context.projectionTypes.put(model.getFullName(), model);
                visited.add(parent);
            }
        }
    }

    private Set<TypeElement> getEmbeddedTypes() {
        Set<TypeElement> elements = new HashSet<TypeElement>();
        // only creation
        for (Element element : getElements(conf.getEmbeddedAnnotation())) {
            handleEmbeddedType(element, elements);
        }
        return elements;
    }

    private void handleEmbeddedType(Element element, Set<TypeElement> elements) {
        TypeMirror type = element.asType();
        if (element.getKind() == ElementKind.METHOD) {
            type = ((ExecutableElement)element).getReturnType();
        }
        String typeName = type.toString();

        if (typeName.startsWith(Collection.class.getName())
         || typeName.startsWith(List.class.getName())
         || typeName.startsWith(Set.class.getName())) {
            type = ((DeclaredType)type).getTypeArguments().get(0);

        } else if (typeName.startsWith(Map.class.getName())) {
            type = ((DeclaredType)type).getTypeArguments().get(1);
        }

        TypeElement typeElement = typeExtractor.visit(type);

        if (typeElement != null && !TypeUtils.hasAnnotationOfType(typeElement, conf.getEntityAnnotations())) {
            if (!typeElement.getQualifiedName().toString().startsWith("java.")) {
                elements.add(typeElement);
            }
        }
    }

    private Set<TypeElement> getTypeFromProperties(Set<TypeElement> parents) {
        Set<TypeElement> elements = new HashSet<TypeElement>();
        for (Element element : parents) {
            if (element instanceof TypeElement) {
                processFromProperties((TypeElement)element, elements);
            }
        }

        Iterator<TypeElement> iterator = elements.iterator();
        while (iterator.hasNext()) {
            TypeElement element = iterator.next();
            String name = element.getQualifiedName().toString();
            if (name.startsWith("java.") || name.startsWith("org.joda.time.")) {
                iterator.remove();
            } else {
                boolean annotated = false;
                for (Class<? extends Annotation> annotation : conf.getEntityAnnotations()) {
                    annotated |= element.getAnnotation(annotation) != null;
                }
                if (annotated) {
                    iterator.remove();
                }
            }
        }

        return elements;
    }

    private void processFromProperties(TypeElement type, Set<TypeElement> types) {
        List<? extends Element> children = type.getEnclosedElements();
        VisitorConfig config = conf.getConfig(type, children);

        // fields
        if (config.visitFieldProperties()) {
            for (VariableElement field : ElementFilter.fieldsIn(children)) {
                TypeElement typeElement = typeExtractor.visit(field.asType());
                if (typeElement != null) {
                    types.add(typeElement);
                }
            }
        }

        // getters
        if (config.visitMethodProperties()) {
            for (ExecutableElement method : ElementFilter.methodsIn(children)) {
                String name = method.getSimpleName().toString();
                if ((name.startsWith("get") || name.startsWith("is")) && method.getParameters().isEmpty()) {
                    TypeElement typeElement = typeExtractor.visit(method.getReturnType());
                    if (typeElement != null) {
                        types.add(typeElement);
                    }
                }
            }
        }
    }

    private void addSupertypeFields(EntityType model, Set<EntityType> handled) {
        if (handled.add(model)) {
            for (Supertype supertype : model.getSuperTypes()) {
                EntityType entityType = context.allTypes.get(supertype.getType().getFullName());
                if (entityType != null) {
                    addSupertypeFields(entityType, handled);
                    supertype.setEntityType(entityType);
                    model.include(supertype);
                }
            }
        }
    }

    private void processExclusions() {
        for (Element element : getElements(QueryExclude.class)) {
            if (element instanceof PackageElement) {
                conf.addExcludedPackage(((PackageElement)element).getQualifiedName().toString());
            } else if (element instanceof TypeElement) {
                conf.addExcludedClass(((TypeElement)element).getQualifiedName().toString());
            } else {
                throw new IllegalArgumentException(element.toString());
            }
        }
    }

    private Set<TypeElement> processDelegateMethods() {
        Set<Element> delegateMethods = (Set)getElements(QueryDelegate.class);
        Set<TypeElement> typeElements = new HashSet<TypeElement>();

        for (Element delegateMethod : delegateMethods) {
            ExecutableElement method = (ExecutableElement)delegateMethod;
            Element element = delegateMethod.getEnclosingElement();
            String name = method.getSimpleName().toString();
            Type delegateType = typeFactory.getType(element.asType(), true);
            Type returnType = typeFactory.getType(method.getReturnType(), true);
            List<Parameter> parameters = elementHandler.transformParams(method.getParameters());
            // remove first element
            parameters = parameters.subList(1, parameters.size());

            EntityType entityType = null;
            for (AnnotationMirror annotation : delegateMethod.getAnnotationMirrors()) {
                if (TypeUtils.isAnnotationMirrorOfType(annotation, QueryDelegate.class)) {
                    TypeMirror type = TypeUtils.getAnnotationValueAsTypeMirror(annotation, "value");
                    if (type != null) {
                        entityType = typeFactory.getEntityType(type, true);
                    }
                }
            }

            if (entityType != null) {
                registerTypeElement(entityType.getFullName(), (TypeElement)element);
                entityType.addDelegate(new Delegate(entityType, delegateType, name, parameters, returnType));
                TypeElement typeElement = processingEnv.getElementUtils().getTypeElement(entityType.getFullName());
                boolean isAnnotated = false;
                for (Class<? extends Annotation> ann : conf.getEntityAnnotations()) {
                    if (typeElement.getAnnotation(ann) != null) {
                        isAnnotated = true;
                    }
                }
                if (isAnnotated) {
                    // handle also properties of entity type
                    typeElements.add(processingEnv.getElementUtils().getTypeElement(entityType.getFullName()));
                } else {
                    // skip handling properties
                    context.extensionTypes.put(entityType.getFullName(), entityType);
                    context.allTypes.put(entityType.getFullName(), entityType);
                }
            }
        }

        return typeElements;
    }

    private void validateMetaTypes() {
        for (Collection<EntityType> entityTypes : Arrays.asList(
                context.supertypes.values(),
                context.entityTypes.values(),
                context.extensionTypes.values(),
                context.embeddableTypes.values(),
                context.projectionTypes.values())) {
            for (EntityType entityType : entityTypes) {
                for (Property property : entityType.getProperties()) {
                    if (property.getInits() != null && property.getInits().size() > 0) {
                        validateInits(entityType, property);
                    }
                }
            }
        }
    }

    protected void validateInits(EntityType entityType, Property property) {
        for (String init : property.getInits()) {
            if (!init.startsWith("*") && property.getType() instanceof EntityType) {
                String initProperty = init.contains(".") ? init.substring(0, init.indexOf('.')) : init;
                if (!((EntityType)property.getType()).getPropertyNames().contains(initProperty)) {
                    processingEnv.getMessager().printMessage(Kind.ERROR,
                        "Illegal inits of " + entityType.getFullName()+ "." + property.getName() + ": " +
                        initProperty + " not found");
                }
            }
        }
    }

    private void serializeMetaTypes() {
        if (!context.supertypes.isEmpty()) {
            processingEnv.getMessager().printMessage(Kind.NOTE, "Serializing Supertypes");
            serialize(conf.getSupertypeSerializer(), context.supertypes.values());
        }

        if (!context.entityTypes.isEmpty()) {
            processingEnv.getMessager().printMessage(Kind.NOTE, "Serializing Entity types");
            serialize(conf.getEntitySerializer(), context.entityTypes.values());
        }

        if (!context.extensionTypes.isEmpty()) {
            processingEnv.getMessager().printMessage(Kind.NOTE, "Serializing Extension types");
            serialize(conf.getEmbeddableSerializer(), context.extensionTypes.values());
        }

        if (!context.embeddableTypes.isEmpty()) {
            processingEnv.getMessager().printMessage(Kind.NOTE, "Serializing Embeddable types");
            serialize(conf.getEmbeddableSerializer(), context.embeddableTypes.values());
        }

        if (!context.projectionTypes.isEmpty()) {
            processingEnv.getMessager().printMessage(Kind.NOTE, "Serializing Projection types");
            serialize(conf.getDTOSerializer(), context.projectionTypes.values());
        }

    }

    private Set<? extends Element> getElements(Class<? extends Annotation> a) {
        return roundEnv.getElementsAnnotatedWith(a);
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    private void serialize(Serializer serializer, Collection<EntityType> models) {
        for (EntityType model : models) {
            try {
                Type type = conf.getTypeMappings().getPathType(model, model, true);
                String packageName = type.getPackageName();
                String className = !packageName.isEmpty() ? (packageName + "." + type.getSimpleName()) : type.getSimpleName();

                // skip if type is excluded class or in excluded package
                if (conf.isExcludedPackage(model.getPackageName()) || conf.isExcludedClass(model.getFullName())) {
                    continue;
                }

                Set<TypeElement> elements = context.typeElements.get(model.getFullName());

                if (elements == null) {
                    elements = new HashSet<TypeElement>();
                }
                for (Property property : model.getProperties()) {
                    if (property.getType().getCategory() == TypeCategory.CUSTOM) {
                        Set<TypeElement> customElements = context.typeElements.get(property.getType().getFullName());
                        if (customElements != null) {
                            elements.addAll(customElements);
                        }
                    }
                }

                processingEnv.getMessager().printMessage(Kind.NOTE, "Generating " + className + " for " + elements);
                JavaFileObject fileObject = processingEnv.getFiler().createSourceFile(className,
                        elements.toArray(new Element[elements.size()]));
                Writer writer = fileObject.openWriter();
                try {
                    SerializerConfig serializerConfig = conf.getSerializerConfig(model);
                    serializer.serialize(model, serializerConfig, new JavaWriter(writer));
                } finally {
                    if (writer != null) {
                        writer.close();
                    }
                }

            } catch (IOException e) {
                System.err.println(e.getMessage());
                processingEnv.getMessager().printMessage(Kind.ERROR, e.getMessage());
            }
        }
    }


    protected abstract Configuration createConfiguration(RoundEnvironment roundEnv);

}
TOP

Related Classes of com.mysema.query.apt.AbstractQuerydslProcessor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.