dagger.internal.codegen.MapKeys.java Source code

Java tutorial

Introduction

Here is the source code for dagger.internal.codegen.MapKeys.java

Source

/*
 * Copyright (C) 2015 The Dagger Authors.
 *
 * 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 dagger.internal.codegen;

import static com.google.auto.common.AnnotationMirrors.getAnnotatedAnnotations;
import static com.google.auto.common.AnnotationMirrors.getAnnotationValuesWithDefaults;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.Iterables.getOnlyElement;
import static javax.lang.model.util.ElementFilter.methodsIn;

import com.google.auto.common.MoreTypes;
import com.google.common.collect.ImmutableSet;
import com.squareup.javapoet.CodeBlock;
import dagger.MapKey;
import java.util.NoSuchElementException;
import java.util.Optional;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.PrimitiveType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.SimpleTypeVisitor6;
import javax.lang.model.util.Types;

/**
 * Methods for extracting {@link MapKey} annotations and key code blocks from binding elements.
 */
final class MapKeys {

    /**
     * If {@code bindingElement} is annotated with a {@link MapKey} annotation, returns it.
     *
     * @throws IllegalArgumentException if the element is annotated with more than one {@code MapKey}
     *     annotation
     */
    static Optional<AnnotationMirror> getMapKey(Element bindingElement) {
        ImmutableSet<? extends AnnotationMirror> mapKeys = getMapKeys(bindingElement);
        return mapKeys.isEmpty() ? Optional.empty() : Optional.<AnnotationMirror>of(getOnlyElement(mapKeys));
    }

    /**
     * Returns all of the {@link MapKey} annotations that annotate {@code bindingElement}.
     */
    static ImmutableSet<? extends AnnotationMirror> getMapKeys(Element bindingElement) {
        return getAnnotatedAnnotations(bindingElement, MapKey.class);
    }

    /**
     * Returns the annotation value if {@code mapKey}'s type is annotated with
     * {@link MapKey @MapKey(unwrapValue = true)}.
     *
     * @throws IllegalArgumentException if {@code mapKey}'s type is not annotated with
     *     {@link MapKey @MapKey} at all.
     */
    static Optional<? extends AnnotationValue> unwrapValue(AnnotationMirror mapKey) {
        MapKey mapKeyAnnotation = mapKey.getAnnotationType().asElement().getAnnotation(MapKey.class);
        checkArgument(mapKeyAnnotation != null, "%s is not annotated with @MapKey", mapKey.getAnnotationType());
        return mapKeyAnnotation.unwrapValue()
                ? Optional.of(getOnlyElement(getAnnotationValuesWithDefaults(mapKey).values()))
                : Optional.empty();
    }

    /**
     * Returns the map key type for an unwrapped {@link MapKey} annotation type. If the single member
     * type is primitive, returns the boxed type.
     *
     * @throws IllegalArgumentException if {@code mapKeyAnnotationType} is not an annotation type or
     *     has more than one member, or if its single member is an array
     * @throws NoSuchElementException if the annotation has no members
     */
    public static DeclaredType getUnwrappedMapKeyType(final DeclaredType mapKeyAnnotationType, final Types types) {
        checkArgument(MoreTypes.asTypeElement(mapKeyAnnotationType).getKind() == ElementKind.ANNOTATION_TYPE,
                "%s is not an annotation type", mapKeyAnnotationType);

        final ExecutableElement onlyElement = getOnlyElement(
                methodsIn(mapKeyAnnotationType.asElement().getEnclosedElements()));

        SimpleTypeVisitor6<DeclaredType, Void> keyTypeElementVisitor = new SimpleTypeVisitor6<DeclaredType, Void>() {

            @Override
            public DeclaredType visitArray(ArrayType t, Void p) {
                throw new IllegalArgumentException(
                        mapKeyAnnotationType + "." + onlyElement.getSimpleName() + " cannot be an array");
            }

            @Override
            public DeclaredType visitPrimitive(PrimitiveType t, Void p) {
                return MoreTypes.asDeclared(types.boxedClass(t).asType());
            }

            @Override
            public DeclaredType visitDeclared(DeclaredType t, Void p) {
                return t;
            }
        };
        return keyTypeElementVisitor.visit(onlyElement.getReturnType());
    }

    /**
     * Returns a code block for the map key specified by the {@link MapKey} annotation on
     * {@code bindingElement}.
     *
     * @throws IllegalArgumentException if the element is annotated with more than one {@code MapKey}
     *     annotation
     * @throws IllegalStateException if {@code bindingElement} is not annotated with a {@code MapKey}
     *     annotation
     */
    static CodeBlock getMapKeyExpression(AnnotationMirror mapKey) {
        Optional<? extends AnnotationValue> unwrappedValue = unwrapValue(mapKey);
        AnnotationExpression annotationExpression = new AnnotationExpression(mapKey);
        if (unwrappedValue.isPresent()) {
            TypeMirror unwrappedValueType = getOnlyElement(getAnnotationValuesWithDefaults(mapKey).keySet())
                    .getReturnType();
            return annotationExpression.getValueExpression(unwrappedValueType, unwrappedValue.get());
        } else {
            return annotationExpression.getAnnotationInstanceExpression();
        }
    }

    private MapKeys() {
    }
}