1 /* 2 * Copyright (C) 2015 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.common.base.Function; 20 import com.google.common.base.Optional; 21 import com.google.common.collect.ImmutableList; 22 import com.google.common.collect.ImmutableSet; 23 import dagger.MapKey; 24 import dagger.internal.codegen.writer.ClassName; 25 import dagger.internal.codegen.writer.Snippet; 26 import dagger.internal.codegen.writer.TypeName; 27 import dagger.internal.codegen.writer.TypeNames; 28 import java.util.List; 29 import java.util.Map; 30 import java.util.NoSuchElementException; 31 import javax.lang.model.element.AnnotationMirror; 32 import javax.lang.model.element.AnnotationValue; 33 import javax.lang.model.element.Element; 34 import javax.lang.model.element.ElementKind; 35 import javax.lang.model.element.ExecutableElement; 36 import javax.lang.model.element.TypeElement; 37 import javax.lang.model.element.VariableElement; 38 import javax.lang.model.type.ArrayType; 39 import javax.lang.model.type.DeclaredType; 40 import javax.lang.model.type.PrimitiveType; 41 import javax.lang.model.type.TypeMirror; 42 import javax.lang.model.util.SimpleAnnotationValueVisitor6; 43 import javax.lang.model.util.SimpleTypeVisitor6; 44 import javax.lang.model.util.Types; 45 46 import static com.google.auto.common.AnnotationMirrors.getAnnotatedAnnotations; 47 import static com.google.auto.common.AnnotationMirrors.getAnnotationValuesWithDefaults; 48 import static com.google.common.base.Preconditions.checkArgument; 49 import static com.google.common.collect.Iterables.getOnlyElement; 50 import static com.google.common.collect.Iterables.transform; 51 import static dagger.internal.codegen.writer.Snippet.makeParametersSnippet; 52 import static javax.lang.model.util.ElementFilter.methodsIn; 53 54 /** 55 * Methods for extracting {@link MapKey} annotations and key snippets from binding elements. 56 */ 57 final class MapKeys { 58 59 /** 60 * If {@code bindingElement} is annotated with a {@link MapKey} annotation, returns it. 61 * 62 * @throws IllegalArgumentException if the element is annotated with more than one {@code MapKey} 63 * annotation 64 */ getMapKey(Element bindingElement)65 static Optional<? extends AnnotationMirror> getMapKey(Element bindingElement) { 66 ImmutableSet<? extends AnnotationMirror> mapKeys = getMapKeys(bindingElement); 67 return mapKeys.isEmpty() 68 ? Optional.<AnnotationMirror>absent() 69 : Optional.of(getOnlyElement(mapKeys)); 70 } 71 72 /** 73 * Returns all of the {@link MapKey} annotations that annotate {@code bindingElement}. 74 */ getMapKeys(Element bindingElement)75 static ImmutableSet<? extends AnnotationMirror> getMapKeys(Element bindingElement) { 76 return getAnnotatedAnnotations(bindingElement, MapKey.class); 77 } 78 79 /** 80 * Returns the annotation value if {@code mapKey}'s type is annotated with 81 * {@link MapKey @MapKey(unwrapValue = true)}. 82 * 83 * @throws IllegalArgumentException if {@code mapKey}'s type is not annotated with 84 * {@link MapKey @MapKey} at all. 85 */ unwrapValue(AnnotationMirror mapKey)86 static Optional<? extends AnnotationValue> unwrapValue(AnnotationMirror mapKey) { 87 MapKey mapKeyAnnotation = mapKey.getAnnotationType().asElement().getAnnotation(MapKey.class); 88 checkArgument( 89 mapKeyAnnotation != null, "%s is not annotated with @MapKey", mapKey.getAnnotationType()); 90 return mapKeyAnnotation.unwrapValue() 91 ? Optional.of(getOnlyElement(mapKey.getElementValues().values())) 92 : Optional.<AnnotationValue>absent(); 93 } 94 95 /** 96 * Returns the map key type for an unwrapped {@link MapKey} annotation type. If the single member 97 * type is primitive, returns the boxed type. 98 * 99 * @throws IllegalArgumentException if {@code mapKeyAnnotationType} is not an annotation type or 100 * has more than one member, or if its single member is an array 101 * @throws NoSuchElementException if the annotation has no members 102 */ getUnwrappedMapKeyType( final DeclaredType mapKeyAnnotationType, final Types types)103 public static DeclaredType getUnwrappedMapKeyType( 104 final DeclaredType mapKeyAnnotationType, final Types types) { 105 checkArgument( 106 MoreTypes.asTypeElement(mapKeyAnnotationType).getKind() == ElementKind.ANNOTATION_TYPE, 107 "%s is not an annotation type", 108 mapKeyAnnotationType); 109 110 final ExecutableElement onlyElement = 111 getOnlyElement(methodsIn(mapKeyAnnotationType.asElement().getEnclosedElements())); 112 113 SimpleTypeVisitor6<DeclaredType, Void> keyTypeElementVisitor = 114 new SimpleTypeVisitor6<DeclaredType, Void>() { 115 116 @Override 117 public DeclaredType visitArray(ArrayType t, Void p) { 118 throw new IllegalArgumentException( 119 mapKeyAnnotationType + "." + onlyElement.getSimpleName() + " cannot be an array"); 120 } 121 122 @Override 123 public DeclaredType visitPrimitive(PrimitiveType t, Void p) { 124 return MoreTypes.asDeclared(types.boxedClass(t).asType()); 125 } 126 127 @Override 128 public DeclaredType visitDeclared(DeclaredType t, Void p) { 129 return t; 130 } 131 }; 132 return keyTypeElementVisitor.visit(onlyElement.getReturnType()); 133 } 134 135 /** 136 * Returns the name of the generated class that contains the static {@code create} methods for a 137 * {@link MapKey} annotation type. 138 */ getMapKeyCreatorClassName(TypeElement mapKeyType)139 public static ClassName getMapKeyCreatorClassName(TypeElement mapKeyType) { 140 ClassName mapKeyTypeName = ClassName.fromTypeElement(mapKeyType); 141 return mapKeyTypeName.topLevelClassName().peerNamed(mapKeyTypeName.classFileName() + "Creator"); 142 } 143 144 /** 145 * Returns a snippet for the map key specified by the {@link MapKey} annotation on 146 * {@code bindingElement}. 147 * 148 * @throws IllegalArgumentException if the element is annotated with more than one {@code MapKey} 149 * annotation 150 * @throws IllegalStateException if {@code bindingElement} is not annotated with a {@code MapKey} 151 * annotation 152 */ getMapKeySnippet(Element bindingElement)153 static Snippet getMapKeySnippet(Element bindingElement) { 154 AnnotationMirror mapKey = getMapKey(bindingElement).get(); 155 ClassName mapKeyCreator = 156 getMapKeyCreatorClassName(MoreTypes.asTypeElement(mapKey.getAnnotationType())); 157 Optional<? extends AnnotationValue> unwrappedValue = unwrapValue(mapKey); 158 if (unwrappedValue.isPresent()) { 159 return new MapKeySnippetExceptArrays(mapKeyCreator) 160 .visit(unwrappedValue.get(), unwrappedValue.get()); 161 } else { 162 return annotationSnippet(mapKey, new MapKeySnippet(mapKeyCreator)); 163 } 164 } 165 166 /** 167 * Returns a snippet to create the visited value in code. Expects its parameter to be a class with 168 * static creation methods for all nested annotation types. 169 * 170 * <p>Note that {@link AnnotationValue#toString()} is the source-code representation of the value 171 * <em>when used in an annotation</em>, which is not always the same as the representation needed 172 * when creating the value in a method body. 173 * 174 * <p>For example, inside an annotation, a nested array of {@code int}s is simply 175 * <code>{1, 2, 3}</code>, but in code it would have to be <code> new int[] {1, 2, 3}</code>. 176 */ 177 private static class MapKeySnippet 178 extends SimpleAnnotationValueVisitor6<Snippet, AnnotationValue> { 179 180 final ClassName mapKeyCreator; 181 MapKeySnippet(ClassName mapKeyCreator)182 MapKeySnippet(ClassName mapKeyCreator) { 183 this.mapKeyCreator = mapKeyCreator; 184 } 185 186 @Override visitEnumConstant(VariableElement c, AnnotationValue p)187 public Snippet visitEnumConstant(VariableElement c, AnnotationValue p) { 188 return Snippet.format( 189 "%s.%s", TypeNames.forTypeMirror(c.getEnclosingElement().asType()), c.getSimpleName()); 190 } 191 192 @Override visitAnnotation(AnnotationMirror a, AnnotationValue p)193 public Snippet visitAnnotation(AnnotationMirror a, AnnotationValue p) { 194 return annotationSnippet(a, this); 195 } 196 197 @Override visitType(TypeMirror t, AnnotationValue p)198 public Snippet visitType(TypeMirror t, AnnotationValue p) { 199 return Snippet.format("%s.class", TypeNames.forTypeMirror(t)); 200 } 201 202 @Override visitString(String s, AnnotationValue p)203 public Snippet visitString(String s, AnnotationValue p) { 204 return Snippet.format("%s", p); 205 } 206 207 @Override visitByte(byte b, AnnotationValue p)208 public Snippet visitByte(byte b, AnnotationValue p) { 209 return Snippet.format("(byte) %s", b); 210 } 211 212 @Override visitChar(char c, AnnotationValue p)213 public Snippet visitChar(char c, AnnotationValue p) { 214 return Snippet.format("%s", p); 215 } 216 217 @Override visitDouble(double d, AnnotationValue p)218 public Snippet visitDouble(double d, AnnotationValue p) { 219 return Snippet.format("%sD", d); 220 } 221 222 @Override visitFloat(float f, AnnotationValue p)223 public Snippet visitFloat(float f, AnnotationValue p) { 224 return Snippet.format("%sF", f); 225 } 226 227 @Override visitInt(int i, AnnotationValue p)228 public Snippet visitInt(int i, AnnotationValue p) { 229 return Snippet.format("(int) %s", i); 230 } 231 232 @Override visitLong(long i, AnnotationValue p)233 public Snippet visitLong(long i, AnnotationValue p) { 234 return Snippet.format("%sL", i); 235 } 236 237 @Override visitShort(short s, AnnotationValue p)238 public Snippet visitShort(short s, AnnotationValue p) { 239 return Snippet.format("(short) %s", s); 240 } 241 242 @Override defaultAction(Object o, AnnotationValue p)243 protected Snippet defaultAction(Object o, AnnotationValue p) { 244 return Snippet.format("%s", o); 245 } 246 247 @Override visitArray(List<? extends AnnotationValue> values, AnnotationValue p)248 public Snippet visitArray(List<? extends AnnotationValue> values, AnnotationValue p) { 249 ImmutableList.Builder<Snippet> snippets = ImmutableList.builder(); 250 for (int i = 0; i < values.size(); i++) { 251 snippets.add(this.visit(values.get(i), p)); 252 } 253 return Snippet.format("{%s}", makeParametersSnippet(snippets.build())); 254 } 255 } 256 257 /** 258 * Returns a snippet for the visited value. Expects its parameter to be a class with static 259 * creation methods for all nested annotation types. 260 * 261 * <p>Throws {@link IllegalArgumentException} if the visited value is an array. 262 */ 263 private static class MapKeySnippetExceptArrays extends MapKeySnippet { 264 MapKeySnippetExceptArrays(ClassName mapKeyCreator)265 MapKeySnippetExceptArrays(ClassName mapKeyCreator) { 266 super(mapKeyCreator); 267 } 268 269 @Override visitArray(List<? extends AnnotationValue> values, AnnotationValue p)270 public Snippet visitArray(List<? extends AnnotationValue> values, AnnotationValue p) { 271 throw new IllegalArgumentException("Cannot unwrap arrays"); 272 } 273 } 274 275 /** 276 * Returns a snippet that calls a static method on {@code mapKeySnippet.mapKeyCreator} to create 277 * an annotation from {@code mapKeyAnnotation}. 278 */ annotationSnippet( AnnotationMirror mapKeyAnnotation, final MapKeySnippet mapKeySnippet)279 private static Snippet annotationSnippet( 280 AnnotationMirror mapKeyAnnotation, final MapKeySnippet mapKeySnippet) { 281 return Snippet.format( 282 "%s.create%s(%s)", 283 mapKeySnippet.mapKeyCreator, 284 mapKeyAnnotation.getAnnotationType().asElement().getSimpleName(), 285 makeParametersSnippet( 286 transform( 287 getAnnotationValuesWithDefaults(mapKeyAnnotation).entrySet(), 288 new Function<Map.Entry<ExecutableElement, AnnotationValue>, Snippet>() { 289 @Override 290 public Snippet apply(Map.Entry<ExecutableElement, AnnotationValue> entry) { 291 return ARRAY_LITERAL_PREFIX.visit( 292 entry.getKey().getReturnType(), 293 mapKeySnippet.visit(entry.getValue(), entry.getValue())); 294 } 295 }))); 296 } 297 298 /** 299 * If the visited type is an array, prefixes the parameter snippet with {@code new T[]}, where 300 * {@code T} is the raw array component type. 301 */ 302 private static final SimpleTypeVisitor6<Snippet, Snippet> ARRAY_LITERAL_PREFIX = 303 new SimpleTypeVisitor6<Snippet, Snippet>() { 304 305 @Override 306 public Snippet visitArray(ArrayType t, Snippet p) { 307 return Snippet.format("new %s[] %s", RAW_TYPE_NAME.visit(t.getComponentType()), p); 308 } 309 310 @Override 311 protected Snippet defaultAction(TypeMirror e, Snippet p) { 312 return p; 313 } 314 }; 315 316 /** 317 * If the visited type is an array, returns the name of its raw component type; otherwise returns 318 * the name of the type itself. 319 */ 320 private static final SimpleTypeVisitor6<TypeName, Void> RAW_TYPE_NAME = 321 new SimpleTypeVisitor6<TypeName, Void>() { 322 @Override 323 public TypeName visitDeclared(DeclaredType t, Void p) { 324 return ClassName.fromTypeElement(MoreTypes.asTypeElement(t)); 325 } 326 327 @Override 328 protected TypeName defaultAction(TypeMirror e, Void p) { 329 return TypeNames.forTypeMirror(e); 330 } 331 }; 332 333 private MapKeys() {} 334 } 335