• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Dagger Authors.
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 
17 package dagger.internal.codegen;
18 
19 import static com.squareup.javapoet.MethodSpec.constructorBuilder;
20 import static com.squareup.javapoet.MethodSpec.methodBuilder;
21 import static com.squareup.javapoet.TypeSpec.classBuilder;
22 import static dagger.internal.codegen.AnnotationExpression.createMethodName;
23 import static dagger.internal.codegen.AnnotationExpression.getAnnotationCreatorClassName;
24 import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
25 import static javax.lang.model.element.Modifier.FINAL;
26 import static javax.lang.model.element.Modifier.PRIVATE;
27 import static javax.lang.model.element.Modifier.PUBLIC;
28 import static javax.lang.model.element.Modifier.STATIC;
29 import static javax.lang.model.util.ElementFilter.methodsIn;
30 
31 import com.google.auto.common.MoreTypes;
32 import com.google.common.collect.ImmutableList;
33 import com.google.errorprone.annotations.CanIgnoreReturnValue;
34 import com.squareup.javapoet.ClassName;
35 import com.squareup.javapoet.CodeBlock;
36 import com.squareup.javapoet.MethodSpec;
37 import com.squareup.javapoet.TypeName;
38 import com.squareup.javapoet.TypeSpec;
39 import dagger.internal.codegen.langmodel.DaggerElements;
40 import java.util.LinkedHashSet;
41 import java.util.Optional;
42 import java.util.Set;
43 import javax.annotation.processing.Filer;
44 import javax.inject.Inject;
45 import javax.lang.model.SourceVersion;
46 import javax.lang.model.element.Element;
47 import javax.lang.model.element.ElementKind;
48 import javax.lang.model.element.ExecutableElement;
49 import javax.lang.model.element.TypeElement;
50 import javax.lang.model.type.DeclaredType;
51 import javax.lang.model.util.SimpleTypeVisitor6;
52 
53 /**
54  * Generates classes that create annotation instances for an annotation type. The generated class
55  * will have a private empty constructor, a static method that creates the annotation type itself,
56  * and a static method that creates each annotation type that is nested in the top-level annotation
57  * type.
58  *
59  * <p>So for an example annotation:
60  *
61  * <pre>
62  *   {@literal @interface} Foo {
63  *     String s();
64  *     int i();
65  *     Bar bar(); // an annotation defined elsewhere
66  *   }
67  * </pre>
68  *
69  * the generated class will look like:
70  *
71  * <pre>
72  *   public final class FooCreator {
73  *     private FooCreator() {}
74  *
75  *     public static Foo createFoo(String s, int i, Bar bar) { … }
76  *     public static Bar createBar(…) { … }
77  *   }
78  * </pre>
79  */
80 class AnnotationCreatorGenerator extends SourceFileGenerator<TypeElement> {
81   private static final ClassName AUTO_ANNOTATION =
82       ClassName.get("com.google.auto.value", "AutoAnnotation");
83 
84   @Inject
AnnotationCreatorGenerator(Filer filer, DaggerElements elements, SourceVersion sourceVersion)85   AnnotationCreatorGenerator(Filer filer, DaggerElements elements, SourceVersion sourceVersion) {
86     super(filer, elements, sourceVersion);
87   }
88 
89   @Override
nameGeneratedType(TypeElement annotationType)90   ClassName nameGeneratedType(TypeElement annotationType) {
91     return getAnnotationCreatorClassName(annotationType);
92   }
93 
94   @Override
originatingElement(TypeElement annotationType)95   Element originatingElement(TypeElement annotationType) {
96     return annotationType;
97   }
98 
99   @Override
write(ClassName generatedTypeName, TypeElement annotationType)100   Optional<TypeSpec.Builder> write(ClassName generatedTypeName, TypeElement annotationType) {
101     TypeSpec.Builder annotationCreatorBuilder =
102         classBuilder(generatedTypeName)
103             .addModifiers(PUBLIC, FINAL)
104             .addMethod(constructorBuilder().addModifiers(PRIVATE).build());
105 
106     for (TypeElement annotationElement : annotationsToCreate(annotationType)) {
107       annotationCreatorBuilder.addMethod(buildCreateMethod(generatedTypeName, annotationElement));
108     }
109 
110     return Optional.of(annotationCreatorBuilder);
111   }
112 
buildCreateMethod(ClassName generatedTypeName, TypeElement annotationElement)113   private MethodSpec buildCreateMethod(ClassName generatedTypeName, TypeElement annotationElement) {
114     String createMethodName = createMethodName(annotationElement);
115     MethodSpec.Builder createMethod =
116         methodBuilder(createMethodName)
117             .addAnnotation(AUTO_ANNOTATION)
118             .addModifiers(PUBLIC, STATIC)
119             .returns(TypeName.get(annotationElement.asType()));
120 
121     ImmutableList.Builder<CodeBlock> parameters = ImmutableList.builder();
122     for (ExecutableElement annotationMember : methodsIn(annotationElement.getEnclosedElements())) {
123       String parameterName = annotationMember.getSimpleName().toString();
124       TypeName parameterType = TypeName.get(annotationMember.getReturnType());
125       createMethod.addParameter(parameterType, parameterName);
126       parameters.add(CodeBlock.of("$L", parameterName));
127     }
128 
129     ClassName autoAnnotationClass =
130         generatedTypeName.peerClass(
131             "AutoAnnotation_" + generatedTypeName.simpleName() + "_" + createMethodName);
132     createMethod.addStatement(
133         "return new $T($L)", autoAnnotationClass, makeParametersCodeBlock(parameters.build()));
134     return createMethod.build();
135   }
136 
137   /**
138    * Returns the annotation types for which {@code @AutoAnnotation static Foo createFoo(…)} methods
139    * should be written.
140    */
annotationsToCreate(TypeElement annotationElement)141   protected Set<TypeElement> annotationsToCreate(TypeElement annotationElement) {
142     return nestedAnnotationElements(annotationElement, new LinkedHashSet<>());
143   }
144 
145   @CanIgnoreReturnValue
nestedAnnotationElements( TypeElement annotationElement, Set<TypeElement> annotationElements)146   private static Set<TypeElement> nestedAnnotationElements(
147       TypeElement annotationElement, Set<TypeElement> annotationElements) {
148     if (annotationElements.add(annotationElement)) {
149       for (ExecutableElement method : methodsIn(annotationElement.getEnclosedElements())) {
150         TRAVERSE_NESTED_ANNOTATIONS.visit(method.getReturnType(), annotationElements);
151       }
152     }
153     return annotationElements;
154   }
155 
156   private static final SimpleTypeVisitor6<Void, Set<TypeElement>> TRAVERSE_NESTED_ANNOTATIONS =
157       new SimpleTypeVisitor6<Void, Set<TypeElement>>() {
158         @Override
159         public Void visitDeclared(DeclaredType t, Set<TypeElement> p) {
160           TypeElement typeElement = MoreTypes.asTypeElement(t);
161           if (typeElement.getKind() == ElementKind.ANNOTATION_TYPE) {
162             nestedAnnotationElements(typeElement, p);
163           }
164           return null;
165         }
166       };
167 }
168