dagger2.internal.codegen.Util.java Source code

Java tutorial

Introduction

Here is the source code for dagger2.internal.codegen.Util.java

Source

/*
 * Copyright (C) 2013 Google, Inc.
 * Copyright (C) 2013 Square, Inc.
 *
 * 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 dagger2.internal.codegen;

import com.google.auto.common.MoreElements;
import com.google.auto.common.MoreTypes;
import com.google.common.base.Equivalence;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import dagger2.internal.codegen.writer.ClassName;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.AnnotationValueVisitor;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import javax.lang.model.util.SimpleAnnotationValueVisitor6;

import static com.google.common.base.Preconditions.checkState;
import static javax.lang.model.element.ElementKind.CONSTRUCTOR;
import static javax.lang.model.element.Modifier.ABSTRACT;
import static javax.lang.model.element.Modifier.STATIC;

/**
 * Utilities for handling types in annotation processors
 */
final class Util {
    /**
     * Returns the {@code V} type for a {@link Map} type like Map<K, Provider<V>>} if the map
     * includes such a construction
     */
    public static TypeMirror getProvidedValueTypeOfMap(DeclaredType mapType) {
        checkState(MoreTypes.isTypeOf(Map.class, mapType), "%s is not a Map.", mapType);
        return MoreTypes.asDeclared(mapType.getTypeArguments().get(1)).getTypeArguments().get(0);
    }

    // TODO(cgruber): Consider an object that holds and exposes the various parts of a Map type.
    /**
     * returns the value type for a {@link Map} type like Map<K, V>}.
     */
    public static TypeMirror getValueTypeOfMap(DeclaredType mapType) {
        checkState(MoreTypes.isTypeOf(Map.class, mapType), "%s is not a Map.", mapType);
        List<? extends TypeMirror> mapArgs = mapType.getTypeArguments();
        return mapArgs.get(1);
    }

    /**
     * Returns the key type for a {@link Map} type like Map<K, Provider<V>>}
     */
    public static DeclaredType getKeyTypeOfMap(DeclaredType mapType) {
        checkState(MoreTypes.isTypeOf(Map.class, mapType), "%s is not a Map.", mapType);
        List<? extends TypeMirror> mapArgs = mapType.getTypeArguments();
        return MoreTypes.asDeclared(mapArgs.get(0));
    }

    /**
     * Returns the unwrapped key's {@link TypeElement} for a {@link Map} given the
     * {@link AnnotationMirror} of the key.
     */
    public static TypeElement getKeyTypeElement(AnnotationMirror mapKey, final Elements elements) {
        Map<? extends ExecutableElement, ? extends AnnotationValue> map = mapKey.getElementValues();
        // TODO(user) Support literals other than String and Enum
        AnnotationValueVisitor<TypeElement, Void> mapKeyVisitor = new SimpleAnnotationValueVisitor6<TypeElement, Void>() {
            @Override
            public TypeElement visitEnumConstant(VariableElement c, Void p) {
                return MoreElements.asType(c.getEnclosingElement());
            }

            @Override
            public TypeElement visitString(String s, Void p) {
                return elements.getTypeElement(String.class.getCanonicalName());
            }

            @Override
            protected TypeElement defaultAction(Object o, Void v) {
                throw new IllegalStateException(
                        "Non-supported key type for map binding " + o.getClass().getCanonicalName());
            }
        };
        TypeElement keyTypeElement = Iterables.getOnlyElement(map.entrySet()).getValue().accept(mapKeyVisitor,
                null);
        return keyTypeElement;
    }

    /**
     * Returns the name of the generated class that contains the static {@code create} method for a
     * {@code @MapKey} annotation type.
     */
    public static ClassName getMapKeyCreatorClassName(TypeElement mapKeyType) {
        ClassName enclosingClassName = ClassName.fromTypeElement(mapKeyType);
        return enclosingClassName.topLevelClassName().peerNamed(enclosingClassName.classFileName() + "Creator");
    }

    /**
     * Wraps an {@link Optional} of a type in an {@code Optional} of a {@link Wrapper} for that type.
     */
    static <T> Optional<Equivalence.Wrapper<T>> wrapOptionalInEquivalence(Equivalence<T> equivalence,
            Optional<T> optional) {
        return optional.isPresent() ? Optional.of(equivalence.wrap(optional.get()))
                : Optional.<Equivalence.Wrapper<T>>absent();
    }

    /**
     * Unwraps an {@link Optional} of a {@link Wrapper} into an {@code Optional} of the underlying
     * type.
     */
    static <T> Optional<T> unwrapOptionalEquivalence(Optional<Equivalence.Wrapper<T>> wrappedOptional) {
        return wrappedOptional.isPresent() ? Optional.of(wrappedOptional.get().get()) : Optional.<T>absent();
    }

    private static boolean requiresEnclosingInstance(TypeElement typeElement) {
        switch (typeElement.getNestingKind()) {
        case TOP_LEVEL:
            return false;
        case MEMBER:
            return !typeElement.getModifiers().contains(STATIC);
        case ANONYMOUS:
        case LOCAL:
            return true;
        default:
            throw new AssertionError("TypeElement cannot have nesting kind: " + typeElement.getNestingKind());
        }
    }

    /**
     * Returns true if and only if a component can instantiate new instances (typically of a module)
     * rather than requiring that they be passed.
     */
    static boolean componentCanMakeNewInstances(TypeElement typeElement) {
        switch (typeElement.getKind()) {
        case CLASS:
            break;
        case ENUM:
        case ANNOTATION_TYPE:
        case INTERFACE:
            return false;
        default:
            throw new AssertionError("TypeElement cannot have kind: " + typeElement.getKind());
        }

        if (typeElement.getModifiers().contains(ABSTRACT)) {
            return false;
        }

        if (requiresEnclosingInstance(typeElement)) {
            return false;
        }

        for (Element enclosed : typeElement.getEnclosedElements()) {
            if (enclosed.getKind().equals(CONSTRUCTOR)
                    && ((ExecutableElement) enclosed).getParameters().isEmpty()) {
                return true;
            }
        }

        // TODO(gak): still need checks for visibility

        return false;
    }

    /*
     * Borrowed from AutoValue and slightly modified. TODO(gak): reconcile and put in autocommon.
     */
    private static ImmutableList<ExecutableElement> findLocalAndInheritedMethods(Elements elements,
            TypeElement type) {
        List<ExecutableElement> methods = Lists.newArrayList();
        TypeElement objectType = elements.getTypeElement(Object.class.getName());
        findLocalAndInheritedMethodsRecursive(objectType, elements, type, methods);
        return ImmutableList.copyOf(methods);
    }

    private static void findLocalAndInheritedMethodsRecursive(TypeElement objectType, Elements elements,
            TypeElement type, List<ExecutableElement> methods) {
        if (objectType.equals(type)) {
            return;
        }

        for (TypeMirror superInterface : type.getInterfaces()) {
            findLocalAndInheritedMethodsRecursive(objectType, elements,
                    MoreElements.asType(MoreTypes.asElement(superInterface)), methods);
        }
        if (type.getSuperclass().getKind() != TypeKind.NONE) {
            // Visit the superclass after superinterfaces so we will always see the implementation of a
            // method after any interfaces that declared it.
            findLocalAndInheritedMethodsRecursive(objectType, elements,
                    MoreElements.asType(MoreTypes.asElement(type.getSuperclass())), methods);
        }
        // Add each method of this class, and in so doing remove any inherited method it overrides.
        // This algorithm is quadratic in the number of methods but it's hard to see how to improve
        // that while still using Elements.overrides.
        List<ExecutableElement> theseMethods = ElementFilter.methodsIn(type.getEnclosedElements());
        for (ExecutableElement method : theseMethods) {
            if (!method.getModifiers().contains(Modifier.PRIVATE)) {
                boolean alreadySeen = false;
                for (Iterator<ExecutableElement> methodIter = methods.iterator(); methodIter.hasNext();) {
                    ExecutableElement otherMethod = methodIter.next();
                    if (elements.overrides(method, otherMethod, type)) {
                        methodIter.remove();
                    } else if (method.getSimpleName().equals(otherMethod.getSimpleName())
                            && method.getParameters().equals(otherMethod.getParameters())) {
                        // If we inherit this method on more than one path, we don't want to add it twice.
                        alreadySeen = true;
                    }
                }
                if (!alreadySeen) {
                    methods.add(method);
                }
            }
        }
    }

    /*
     * Borrowed from AutoValue and slightly modified. TODO(gak): reconcile and put in autocommon.
     */
    static ImmutableSet<ExecutableElement> getUnimplementedMethods(Elements elements, TypeElement type) {
        ImmutableSet.Builder<ExecutableElement> unimplementedMethods = ImmutableSet.builder();
        List<ExecutableElement> methods = findLocalAndInheritedMethods(elements, type);
        for (ExecutableElement method : methods) {
            if (method.getModifiers().contains(Modifier.ABSTRACT)) {
                unimplementedMethods.add(method);
            }
        }
        return unimplementedMethods.build();
    }

    private Util() {
    }
}