• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
mapKeyElement()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      */
annotationElement()73     abstract TypeElement annotationElement();
74 
75     /**
76      * Returns a specification for a wrapped {@link MapKey}-annotated annotation.
77      */
wrappedMapKey(TypeElement mapKeyElement)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      */
unwrappedMapKeyWithAnnotationValue( TypeElement mapKeyElement, TypeElement annotationElement)86     static MapKeyCreatorSpecification unwrappedMapKeyWithAnnotationValue(
87         TypeElement mapKeyElement, TypeElement annotationElement) {
88       return new AutoValue_MapKeyGenerator_MapKeyCreatorSpecification(
89           mapKeyElement, annotationElement);
90     }
91   }
92 
MapKeyGenerator(Filer filer)93   MapKeyGenerator(Filer filer) {
94     super(filer);
95   }
96 
97   @Override
nameGeneratedType(MapKeyCreatorSpecification mapKeyCreatorType)98   ClassName nameGeneratedType(MapKeyCreatorSpecification mapKeyCreatorType) {
99     return getMapKeyCreatorClassName(mapKeyCreatorType.mapKeyElement());
100   }
101 
102   @Override
getOriginatingElements(MapKeyCreatorSpecification mapKeyCreatorType)103   Iterable<? extends Element> getOriginatingElements(MapKeyCreatorSpecification mapKeyCreatorType) {
104     return ImmutableSet.of(mapKeyCreatorType.mapKeyElement());
105   }
106 
107   @Override
getElementForErrorReporting( MapKeyCreatorSpecification mapKeyCreatorType)108   Optional<? extends Element> getElementForErrorReporting(
109       MapKeyCreatorSpecification mapKeyCreatorType) {
110     return Optional.of(mapKeyCreatorType.mapKeyElement());
111   }
112 
113   @Override
write( ClassName generatedTypeName, MapKeyCreatorSpecification mapKeyCreatorType)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 
writeCreateMethod(TypeWriter mapKeyCreatorWriter, TypeElement annotationElement)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 
nestedAnnotationElements(TypeElement annotationElement)151   private static Set<TypeElement> nestedAnnotationElements(TypeElement annotationElement) {
152     return nestedAnnotationElements(annotationElement, new LinkedHashSet<TypeElement>());
153   }
154 
nestedAnnotationElements( TypeElement annotationElement, Set<TypeElement> annotationElements)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