Home | History | Annotate | Download | only in codegen
      1 /*
      2  * Copyright (C) 2014 Google, Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package dagger.internal.codegen;
     17 
     18 import com.google.auto.common.MoreTypes;
     19 import com.google.auto.value.AutoAnnotation;
     20 import com.google.auto.value.AutoValue;
     21 import com.google.common.base.Optional;
     22 import com.google.common.collect.ImmutableList;
     23 import com.google.common.collect.ImmutableSet;
     24 import dagger.MapKey;
     25 import dagger.internal.codegen.MapKeyGenerator.MapKeyCreatorSpecification;
     26 import dagger.internal.codegen.writer.ClassName;
     27 import dagger.internal.codegen.writer.JavaWriter;
     28 import dagger.internal.codegen.writer.MethodWriter;
     29 import dagger.internal.codegen.writer.Snippet;
     30 import dagger.internal.codegen.writer.TypeName;
     31 import dagger.internal.codegen.writer.TypeNames;
     32 import dagger.internal.codegen.writer.TypeWriter;
     33 import java.util.LinkedHashSet;
     34 import java.util.Set;
     35 import javax.annotation.Generated;
     36 import javax.annotation.processing.Filer;
     37 import javax.lang.model.element.Element;
     38 import javax.lang.model.element.ElementKind;
     39 import javax.lang.model.element.ExecutableElement;
     40 import javax.lang.model.element.TypeElement;
     41 import javax.lang.model.type.DeclaredType;
     42 import javax.lang.model.util.SimpleTypeVisitor6;
     43 
     44 import static dagger.internal.codegen.MapKeys.getMapKeyCreatorClassName;
     45 import static dagger.internal.codegen.writer.Snippet.makeParametersSnippet;
     46 import static javax.lang.model.element.Modifier.FINAL;
     47 import static javax.lang.model.element.Modifier.PUBLIC;
     48 import static javax.lang.model.element.Modifier.STATIC;
     49 import static javax.lang.model.util.ElementFilter.methodsIn;
     50 
     51 /**
     52  * Generates classes that create annotations required to instantiate {@link MapKey}s.
     53  *
     54  * @since 2.0
     55  */
     56 final class MapKeyGenerator extends SourceFileGenerator<MapKeyCreatorSpecification> {
     57 
     58   /**
     59    * Specification of the {@link MapKey} annotation and the annotation type to generate.
     60    */
     61   @AutoValue
     62   abstract static class MapKeyCreatorSpecification {
     63     /**
     64      * The {@link MapKey}-annotated annotation.
     65      */
     66     abstract TypeElement mapKeyElement();
     67 
     68     /**
     69      * The annotation type to write create methods for. For wrapped {@link MapKey}s, this is
     70      * {@link #mapKeyElement()}. For unwrapped {@code MapKey}s whose single element is an
     71      * annotation, this is that annotation element.
     72      */
     73     abstract TypeElement annotationElement();
     74 
     75     /**
     76      * Returns a specification for a wrapped {@link MapKey}-annotated annotation.
     77      */
     78     static MapKeyCreatorSpecification wrappedMapKey(TypeElement mapKeyElement) {
     79       return new AutoValue_MapKeyGenerator_MapKeyCreatorSpecification(mapKeyElement, mapKeyElement);
     80     }
     81 
     82     /**
     83      * Returns a specification for an unwrapped {@link MapKey}-annotated annotation whose single
     84      * element is a nested annotation.
     85      */
     86     static MapKeyCreatorSpecification unwrappedMapKeyWithAnnotationValue(
     87         TypeElement mapKeyElement, TypeElement annotationElement) {
     88       return new AutoValue_MapKeyGenerator_MapKeyCreatorSpecification(
     89           mapKeyElement, annotationElement);
     90     }
     91   }
     92 
     93   MapKeyGenerator(Filer filer) {
     94     super(filer);
     95   }
     96 
     97   @Override
     98   ClassName nameGeneratedType(MapKeyCreatorSpecification mapKeyCreatorType) {
     99     return getMapKeyCreatorClassName(mapKeyCreatorType.mapKeyElement());
    100   }
    101 
    102   @Override
    103   Iterable<? extends Element> getOriginatingElements(MapKeyCreatorSpecification mapKeyCreatorType) {
    104     return ImmutableSet.of(mapKeyCreatorType.mapKeyElement());
    105   }
    106 
    107   @Override
    108   Optional<? extends Element> getElementForErrorReporting(
    109       MapKeyCreatorSpecification mapKeyCreatorType) {
    110     return Optional.of(mapKeyCreatorType.mapKeyElement());
    111   }
    112 
    113   @Override
    114   ImmutableSet<JavaWriter> write(
    115       ClassName generatedTypeName, MapKeyCreatorSpecification mapKeyCreatorType) {
    116     JavaWriter writer = JavaWriter.inPackage(generatedTypeName.packageName());
    117     TypeWriter mapKeyCreatorWriter = writer.addClass(generatedTypeName.simpleName());
    118     mapKeyCreatorWriter.annotate(Generated.class).setValue(ComponentProcessor.class.getName());
    119     mapKeyCreatorWriter.addModifiers(PUBLIC, FINAL);
    120 
    121     for (TypeElement annotationElement :
    122         nestedAnnotationElements(mapKeyCreatorType.annotationElement())) {
    123       writeCreateMethod(mapKeyCreatorWriter, annotationElement);
    124     }
    125 
    126     return ImmutableSet.of(writer);
    127   }
    128 
    129   private void writeCreateMethod(TypeWriter mapKeyCreatorWriter, TypeElement annotationElement) {
    130     MethodWriter createMethod =
    131         mapKeyCreatorWriter.addMethod(
    132             annotationElement.asType(), "create" + annotationElement.getSimpleName());
    133 
    134     createMethod.annotate(AutoAnnotation.class);
    135     createMethod.addModifiers(PUBLIC, STATIC);
    136 
    137     ImmutableList.Builder<Snippet> parameters = ImmutableList.builder();
    138     for (ExecutableElement annotationMember : methodsIn(annotationElement.getEnclosedElements())) {
    139       String parameterName = annotationMember.getSimpleName().toString();
    140       TypeName parameterType = TypeNames.forTypeMirror(annotationMember.getReturnType());
    141       createMethod.addParameter(parameterType, parameterName);
    142       parameters.add(Snippet.format("%s", parameterName));
    143     }
    144 
    145     ClassName autoAnnotationClass = mapKeyCreatorWriter.name().peerNamed(
    146         "AutoAnnotation_" + mapKeyCreatorWriter.name().simpleName() + "_" + createMethod.name());
    147     createMethod.body().addSnippet(
    148         "return new %s(%s);", autoAnnotationClass, makeParametersSnippet(parameters.build()));
    149   }
    150 
    151   private static Set<TypeElement> nestedAnnotationElements(TypeElement annotationElement) {
    152     return nestedAnnotationElements(annotationElement, new LinkedHashSet<TypeElement>());
    153   }
    154 
    155   private static Set<TypeElement> nestedAnnotationElements(
    156       TypeElement annotationElement, Set<TypeElement> annotationElements) {
    157     if (annotationElements.add(annotationElement)) {
    158       for (ExecutableElement method : methodsIn(annotationElement.getEnclosedElements())) {
    159         TRAVERSE_NESTED_ANNOTATIONS.visit(method.getReturnType(), annotationElements);
    160       }
    161     }
    162     return annotationElements;
    163   }
    164 
    165   private static final SimpleTypeVisitor6<Void, Set<TypeElement>> TRAVERSE_NESTED_ANNOTATIONS =
    166       new SimpleTypeVisitor6<Void, Set<TypeElement>>() {
    167         @Override
    168         public Void visitDeclared(DeclaredType t, Set<TypeElement> p) {
    169           TypeElement typeElement = MoreTypes.asTypeElement(t);
    170           if (typeElement.getKind() == ElementKind.ANNOTATION_TYPE) {
    171             nestedAnnotationElements(typeElement, p);
    172           }
    173           return null;
    174         }
    175       };
    176 }
    177