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