rx.codegen.internal.util.CodegenUtil.java Source code

Java tutorial

Introduction

Here is the source code for rx.codegen.internal.util.CodegenUtil.java

Source

/*
 * Copyright 2015 Matthias Schorsch.
 *
 * 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 rx.codegen.internal.util;

import com.google.common.base.CharMatcher;
import com.google.common.base.Splitter;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;

import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.*;
import javax.lang.model.type.*;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import java.util.*;
import java.util.regex.Pattern;

public class CodegenUtil {

    private static final String JAVA_LANG_PACKAGE_NAME = "java.lang";
    private static final Pattern NEW_LINE_PATTERN = Pattern.compile("\\r?\\n");

    private final ProcessingEnvironment processingEnv;
    private final Types typeUtils;
    private final Elements elementUtils;

    private final PackageElement javaLangPackage;
    private final TypeElement objectTypeElement;
    private final ListMultimap<String, ExecutableElement> objectMethodsByName;

    public CodegenUtil(ProcessingEnvironment processingEnv) {
        this.processingEnv = processingEnv;
        this.typeUtils = processingEnv.getTypeUtils();
        this.elementUtils = processingEnv.getElementUtils();
        this.objectTypeElement = elementUtils.getTypeElement(Object.class.getName());
        this.javaLangPackage = elementUtils.getPackageElement(JAVA_LANG_PACKAGE_NAME);
        this.objectMethodsByName = initObjectMethodsByName(objectTypeElement);
    }

    private static ListMultimap<String, ExecutableElement> initObjectMethodsByName(TypeElement objectTypeElement) {
        final ListMultimap<String, ExecutableElement> ret = ArrayListMultimap.create();
        for (ExecutableElement method : ElementFilter.methodsIn(objectTypeElement.getEnclosedElements())) {
            ret.put(method.getSimpleName().toString(), method);
        }
        return ret;
    }

    public Messager getMessager() {
        return processingEnv.getMessager();
    }

    public Filer getFiler() {
        return processingEnv.getFiler();
    }

    public Locale getLocale() {
        return processingEnv.getLocale();
    }

    public Types getTypeUtils() {
        return typeUtils;
    }

    public Elements getElementUtils() {
        return elementUtils;
    }

    public String generateSimpleClassname(TypeElement element) {
        return element.getSimpleName().toString() + "_";
    }

    public String createJavadocComment(String rawComment) {
        final StringBuilder comment = new StringBuilder();
        comment.append("    /**").append("\n");
        final List<String> lines = Splitter.on(NEW_LINE_PATTERN).trimResults(CharMatcher.anyOf("\r\n"))
                .splitToList(rawComment);
        final int size = lines.size();
        for (int i = 0; i < size; i++) {
            comment.append("     *").append(lines.get(i)).append("\n");
        }
        comment.append("     */");
        return comment.toString();
    }

    public String generateFullQualifiedNameWithGenerics(TypeElement typeElement) {
        final StringBuilder name = new StringBuilder(rawTypeElementToString(typeElement));

        final List<? extends TypeParameterElement> typeParameters = typeElement.getTypeParameters();
        if (!typeParameters.isEmpty()) {
            name.append("<");
            for (int number = 0; number < typeParameters.size(); number++) {
                if (number != 0) {
                    name.append(", ");
                }
                name.append("C").append(number);
            }
            name.append(">");
        }

        return name.toString();
    }

    public boolean overridesMethodOfObject(TypeElement classOfMethod, ExecutableElement method, String methodname) {
        final boolean staticMethod = method.getModifiers().contains(Modifier.STATIC);

        for (ExecutableElement methodOfObject : objectMethodsByName.get(methodname)) {
            if (staticMethod) {
                final int paramteterCount = methodOfObject.getParameters().size();
                if (paramteterCount == 0) {
                    return true;
                }
            } else if (elementUtils.overrides(method, methodOfObject, classOfMethod)
                    || methodOfObject.equals(method)) {
                return true;
            }
        }
        return false;
    }

    public boolean isDeprecatedElement(Element method) {
        return elementUtils.isDeprecated(method);
    }

    public TypeMirror boxTypeIfNeeded(TypeMirror type) {
        return type.getKind().isPrimitive() ? typeUtils.boxedClass((PrimitiveType) type).asType() : type;
    }

    public List<TypeMirror> elementsToTypes(List<? extends Element> elements) {
        final List<TypeMirror> ret = new ArrayList<TypeMirror>();
        for (Element element : elements) {
            ret.add(boxTypeIfNeeded(element.asType()));
        }
        return ret;
    }

    public List<ExecutableElement> findRecursiveAllMethods(TypeElement rootTypeElement) {
        final List<? extends Element> allMembers = elementUtils.getAllMembers(rootTypeElement);
        return ElementFilter.methodsIn(allMembers);
    }

    public Set<TypeVariable> findAllTypeVariables(TypeElement typeElement) {
        return findAllTypeVariables(typeElement.asType());
    }

    public Set<TypeVariable> findAllTypeVariablesFromElements(List<? extends Element> elements) {
        final Set<TypeVariable> ret = new LinkedHashSet<TypeVariable>();
        for (Element element : elements) {
            ret.addAll(findAllTypeVariables(element.asType()));
        }
        return ret;
    }

    public Set<TypeVariable> findAllTypeVariables(List<? extends TypeMirror> types) {
        final Set<TypeVariable> ret = new LinkedHashSet<TypeVariable>();
        for (TypeMirror type : types) {
            ret.addAll(findAllTypeVariables(type));
        }
        return ret;
    }

    public Set<TypeVariable> findAllTypeVariables(TypeMirror rootType) {
        final Set<TypeVariable> ret = new LinkedHashSet<TypeVariable>();

        final Set<TypeMirror> visited = new HashSet<TypeMirror>();
        final LinkedList<TypeMirror> queue = new LinkedList<TypeMirror>();
        queue.add(rootType);

        TypeMirror current;
        while ((current = queue.poll()) != null) {
            if (!visited.add(current)) {
                //don't visit types more than onces.
                continue;
            }

            if (current.getKind() == TypeKind.TYPEVAR) {
                final TypeVariable typeVariable = (TypeVariable) current;
                final TypeMirror lowerBound = typeVariable.getLowerBound();
                final TypeMirror upperBound = typeVariable.getUpperBound();

                if (ret.add(typeVariable)) {
                    if (lowerBound.getKind() != TypeKind.NULL && !isObjectType(lowerBound)) {
                        queue.add(lowerBound);
                    }

                    if (!isObjectType(upperBound)) {
                        queue.add(upperBound);
                    }
                }

            } else if (current.getKind() == TypeKind.DECLARED) {
                final DeclaredType declaredType = (DeclaredType) current;

                for (TypeMirror type : declaredType.getTypeArguments()) {
                    queue.add(type);
                }

                /*
                 for (TypeMirror directSupertype : typeUtils.directSupertypes(current)) {
                 if(isObjectType(directSupertype)) {
                 continue;
                 }
                 queue.add(directSupertype);
                 }
                 */
            } else if (current.getKind() == TypeKind.WILDCARD) {
                final WildcardType wildcardType = (WildcardType) current;
                if (wildcardType.getExtendsBound() != null) {
                    queue.add(wildcardType.getExtendsBound());

                } else if (wildcardType.getSuperBound() != null) {
                    queue.add(wildcardType.getSuperBound());
                }

            } else if (current.getKind() == TypeKind.ARRAY) {
                final ArrayType arrayType = (ArrayType) current;
                final TypeMirror componentType = arrayType.getComponentType();
                queue.add(componentType);
            }
        }

        return ret;
    }

    public String typeToString(TypeMirror rootType, Map<TypeVariable, String> typeVariableNameMapping,
            boolean suppressTypeVarDecl) {
        final StringBuilder ret = new StringBuilder();
        final Set<TypeMirror> definedTypeArgs = new HashSet<TypeMirror>();

        final LinkedList<Context> queue = new LinkedList<Context>();
        queue.add(createTypeContext(rootType));

        Context context;
        while ((context = queue.poll()) != null) {
            if (context instanceof TypeContext) {
                typeToString(queue, (TypeContext) context, definedTypeArgs, typeVariableNameMapping,
                        suppressTypeVarDecl);
            } else {
                ret.append(context.toString());
            }
        }
        return ret.toString();
    }

    private TypeContext createTypeContext(TypeMirror type) {
        switch (type.getKind()) {
        case TYPEVAR:
            return new TypeVarContext(type);

        case DECLARED:
            return new DeclaredTypeContext(type);

        case ARRAY:
            return new ArrayTypeContext(type);

        case WILDCARD:
            return new WildCardTypeContext(type);

        default:
            return new TypeContext(type);
        }
    }

    private void typeToString(LinkedList<Context> queue, TypeContext typeContext, Set<TypeMirror> definedTypeArgs,
            Map<TypeVariable, String> typeVariableNameMapping, boolean suppressTypeVarDecl) {
        final TypeMirror type = typeContext.getType();

        if (type.getKind() == TypeKind.TYPEVAR) {
            final TypeVariable typeVariable = (TypeVariable) type;

            //bound
            if (definedTypeArgs.add(typeVariable) && (typeContext instanceof TypeVarContext)
                    && !suppressTypeVarDecl) {
                final TypeMirror upperBound = isObjectType(typeVariable.getUpperBound()) ? typeUtils.getNullType()
                        : typeVariable.getUpperBound();
                final TypeMirror lowerBound = isObjectType(typeVariable.getLowerBound()) ? typeUtils.getNullType()
                        : typeVariable.getLowerBound();

                if (upperBound.getKind() != TypeKind.NULL && definedTypeArgs.add(upperBound)) {
                    queue.addFirst(new TypeVarContext(upperBound));
                    queue.addFirst(TypeBoundContext.UPPER_BOUND);

                } else if (lowerBound.getKind() != TypeKind.NULL && definedTypeArgs.add(lowerBound)) {
                    queue.addFirst(new TypeVarContext(lowerBound));
                    queue.addFirst(TypeBoundContext.LOWER_BOUND);
                }
            }

            //type variable name
            String typeVariableName = typeVariable.asElement().getSimpleName().toString();
            if (typeVariableNameMapping.containsKey(typeVariable)) {
                typeVariableName = typeVariableNameMapping.get(typeVariable);
            }
            queue.addFirst(new CustomContext(typeVariableName));

        } else if (type.getKind() == TypeKind.DECLARED) {
            final DeclaredType declaredType = (DeclaredType) type;

            //bounds
            final List<? extends TypeMirror> typeArguments = declaredType.getTypeArguments();
            final List<? extends TypeMirror> intersectionTypes = typeUtils.directSupertypes(declaredType);

            if (!typeArguments.isEmpty()) {
                queue.addFirst(TypeBoundContext.CLOSED_BRACKET);
                for (int i = typeArguments.size() - 1; i >= 0; i--) {
                    queue.addFirst(new DeclaredTypeContext(typeArguments.get(i)));
                    if (i != 0) {
                        queue.addFirst(TypeBoundContext.COMMA);
                    }
                }
                queue.addFirst(TypeBoundContext.OPEN_BRACKET);

            } else if (intersectionTypes.size() > 1 && (typeContext instanceof TypeVarContext)) {
                //check for intersection types (@see DeclaredType)
                //example: T extends Number & Comparable<T>
                for (int i = intersectionTypes.size() - 1; i >= 0; i--) {
                    final TypeMirror intersectionType = intersectionTypes.get(i);
                    queue.addFirst(new DeclaredTypeContext(intersectionType));

                    if (i != 0) {
                        queue.addFirst(TypeBoundContext.INTERSECTION_BOUND);
                    }
                }
            }

            //string representation
            final TypeElement typeElement = (TypeElement) declaredType.asElement();
            queue.addFirst(new CustomContext(rawTypeElementToString(typeElement)));

        } else if (type.getKind() == TypeKind.WILDCARD) {
            final WildcardType wildcardType = (WildcardType) type;

            //bounds
            final TypeMirror extendsBound = wildcardType.getExtendsBound();
            final TypeMirror superBound = wildcardType.getSuperBound();
            if (extendsBound != null) {
                queue.addFirst(new WildCardTypeContext(extendsBound));
                queue.addFirst(TypeBoundContext.UPPER_BOUND);

            } else if (superBound != null) {
                queue.addFirst(new WildCardTypeContext(superBound));
                queue.addFirst(TypeBoundContext.LOWER_BOUND);
            }
            queue.addFirst(TypeBoundContext.WILDCARD);

        } else if (type.getKind() == TypeKind.ARRAY) {
            final ArrayType arrayType = (ArrayType) type;
            final TypeMirror componentType = arrayType.getComponentType();
            queue.addFirst(TypeBoundContext.ARRAY_BRACKETS);

            if (componentType.getKind().isPrimitive()) {
                queue.addFirst(new CustomContext(rawTypeMirrorToString(componentType)));
            } else {
                queue.addFirst(new ArrayTypeContext(componentType));
            }

        } else if (type.getKind().isPrimitive()) {
            final PrimitiveType primitive = (PrimitiveType) type;
            queue.addFirst(new CustomContext(rawTypeMirrorToString(boxTypeIfNeeded(primitive))));

        } else if (type.getKind() == TypeKind.VOID) {
            queue.addFirst(new CustomContext("void"));

        } /*else {
          //Error (maybe an unknown feature of Java > 7)
          throw new UnknownTypeException(type, String.format("Type '%s' cannot be resolved.", type));
          }*/
    }

    private String rawTypeMirrorToString(TypeMirror typeMirror) {
        if (typeMirror.getKind() == TypeKind.DECLARED) {
            return rawTypeElementToString((TypeElement) typeUtils.asElement(typeMirror));
        }
        return typeMirror.toString();
    }

    public String rawTypeElementToString(TypeElement typeElement) {
        final PackageElement packageOfType = elementUtils.getPackageOf(typeElement);
        final String qualifiedName = typeElement.getQualifiedName().toString();

        if (javaLangPackage.equals(packageOfType)) {
            if (typeElement.getNestingKind().isNested()) {
                return qualifiedName.substring(packageOfType.getQualifiedName().toString().length() + 1);
            }
            return typeElement.getSimpleName().toString();
        }
        return qualifiedName;
    }

    private boolean isObjectType(TypeMirror type) {
        return typeUtils.isSameType(objectTypeElement.asType(), type);
    }
}