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