1 /* 2 * Copyright 2014 Google LLC 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 com.google.auto.value.processor; 17 18 import static com.google.auto.common.GeneratedAnnotations.generatedAnnotation; 19 import static com.google.auto.value.processor.ClassNames.AUTO_ANNOTATION_NAME; 20 import static com.google.common.collect.Maps.immutableEntry; 21 import static java.util.Comparator.comparing; 22 import static java.util.stream.Collectors.joining; 23 24 import com.google.auto.common.MoreElements; 25 import com.google.auto.common.MoreTypes; 26 import com.google.auto.common.SuperficialValidation; 27 import com.google.auto.service.AutoService; 28 import com.google.common.base.Preconditions; 29 import com.google.common.base.Throwables; 30 import com.google.common.collect.ImmutableList; 31 import com.google.common.collect.ImmutableMap; 32 import com.google.common.collect.ImmutableSet; 33 import com.google.common.hash.Hashing; 34 import com.google.common.primitives.Primitives; 35 import com.google.errorprone.annotations.FormatMethod; 36 import java.io.IOException; 37 import java.io.Writer; 38 import java.util.Collection; 39 import java.util.HashSet; 40 import java.util.List; 41 import java.util.Map; 42 import java.util.Optional; 43 import java.util.Set; 44 import javax.annotation.processing.AbstractProcessor; 45 import javax.annotation.processing.ProcessingEnvironment; 46 import javax.annotation.processing.Processor; 47 import javax.annotation.processing.RoundEnvironment; 48 import javax.annotation.processing.SupportedAnnotationTypes; 49 import javax.lang.model.SourceVersion; 50 import javax.lang.model.element.AnnotationMirror; 51 import javax.lang.model.element.AnnotationValue; 52 import javax.lang.model.element.Element; 53 import javax.lang.model.element.ElementKind; 54 import javax.lang.model.element.ExecutableElement; 55 import javax.lang.model.element.TypeElement; 56 import javax.lang.model.element.VariableElement; 57 import javax.lang.model.type.ArrayType; 58 import javax.lang.model.type.DeclaredType; 59 import javax.lang.model.type.PrimitiveType; 60 import javax.lang.model.type.TypeKind; 61 import javax.lang.model.type.TypeMirror; 62 import javax.lang.model.type.WildcardType; 63 import javax.lang.model.util.ElementFilter; 64 import javax.lang.model.util.Elements; 65 import javax.lang.model.util.Types; 66 import javax.tools.Diagnostic; 67 import javax.tools.JavaFileObject; 68 import net.ltgt.gradle.incap.IncrementalAnnotationProcessor; 69 import net.ltgt.gradle.incap.IncrementalAnnotationProcessorType; 70 71 /** 72 * Javac annotation processor (compiler plugin) to generate annotation implementations. User code 73 * never references this class. 74 * 75 * @author emcmanus@google.com (Éamonn McManus) 76 */ 77 @AutoService(Processor.class) 78 @IncrementalAnnotationProcessor(IncrementalAnnotationProcessorType.ISOLATING) 79 @SupportedAnnotationTypes(AUTO_ANNOTATION_NAME) 80 public class AutoAnnotationProcessor extends AbstractProcessor { AutoAnnotationProcessor()81 public AutoAnnotationProcessor() {} 82 83 private Elements elementUtils; 84 private Types typeUtils; 85 private TypeMirror javaLangObject; 86 87 @Override getSupportedSourceVersion()88 public SourceVersion getSupportedSourceVersion() { 89 return SourceVersion.latestSupported(); 90 } 91 92 @Override getSupportedOptions()93 public ImmutableSet<String> getSupportedOptions() { 94 return ImmutableSet.of(Nullables.NULLABLE_OPTION); 95 } 96 97 @Override init(ProcessingEnvironment processingEnv)98 public synchronized void init(ProcessingEnvironment processingEnv) { 99 super.init(processingEnv); 100 this.elementUtils = processingEnv.getElementUtils(); 101 this.typeUtils = processingEnv.getTypeUtils(); 102 this.javaLangObject = elementUtils.getTypeElement("java.lang.Object").asType(); 103 } 104 105 /** 106 * Issue a compilation error. This method does not throw an exception, since we want to continue 107 * processing and perhaps report other errors. 108 */ 109 @FormatMethod reportError(Element e, String msg, Object... msgParams)110 private void reportError(Element e, String msg, Object... msgParams) { 111 String formattedMessage = String.format(msg, msgParams); 112 processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, formattedMessage, e); 113 } 114 115 /** 116 * Issue a compilation error and return an exception that, when thrown, will cause the processing 117 * of this class to be abandoned. This does not prevent the processing of other classes. 118 */ 119 @FormatMethod abortWithError(Element e, String msg, Object... msgParams)120 private AbortProcessingException abortWithError(Element e, String msg, Object... msgParams) { 121 reportError(e, msg, msgParams); 122 return new AbortProcessingException(); 123 } 124 125 @Override process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)126 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 127 process(roundEnv); 128 return false; 129 } 130 process(RoundEnvironment roundEnv)131 private void process(RoundEnvironment roundEnv) { 132 TypeElement autoAnnotation = elementUtils.getTypeElement(AUTO_ANNOTATION_NAME); 133 Collection<? extends Element> annotatedElements = 134 roundEnv.getElementsAnnotatedWith(autoAnnotation); 135 List<ExecutableElement> methods = ElementFilter.methodsIn(annotatedElements); 136 if (!SuperficialValidation.validateElements(methods) || methodsAreOverloaded(methods)) { 137 return; 138 } 139 for (ExecutableElement method : methods) { 140 try { 141 processMethod(method); 142 } catch (AbortProcessingException e) { 143 // We abandoned this type, but continue with the next. 144 } catch (RuntimeException e) { 145 String trace = Throwables.getStackTraceAsString(e); 146 reportError(method, "@AutoAnnotation processor threw an exception: %s", trace); 147 throw e; 148 } 149 } 150 } 151 processMethod(ExecutableElement method)152 private void processMethod(ExecutableElement method) { 153 TypeElement annotationElement = getAnnotationReturnType(method); 154 155 ImmutableSet<Class<?>> wrapperTypesUsedInCollections = wrapperTypesUsedInCollections(method); 156 ImmutableMap<String, ExecutableElement> memberMethods = getMemberMethods(annotationElement); 157 TypeElement methodClass = MoreElements.asType(method.getEnclosingElement()); 158 String pkg = TypeSimplifier.packageNameOf(methodClass); 159 160 ImmutableMap<String, AnnotationValue> defaultValues = getDefaultValues(annotationElement); 161 ImmutableMap<String, Member> members = getMembers(method, memberMethods); 162 ImmutableMap<String, Parameter> parameters = getParameters(annotationElement, method, members); 163 validateParameters(annotationElement, method, members, parameters, defaultValues); 164 165 String generatedClassName = generatedClassName(method); 166 167 AutoAnnotationTemplateVars vars = new AutoAnnotationTemplateVars(); 168 vars.annotationFullName = annotationElement.toString(); 169 vars.annotationName = TypeEncoder.encode(annotationElement.asType()); 170 vars.className = generatedClassName; 171 vars.generated = getGeneratedTypeName(); 172 vars.members = members; 173 vars.params = parameters; 174 vars.equalsParameterType = equalsParameterType(); 175 vars.pkg = pkg; 176 vars.wrapperTypesUsedInCollections = wrapperTypesUsedInCollections; 177 vars.gwtCompatible = isGwtCompatible(annotationElement); 178 vars.serialVersionUID = computeSerialVersionUid(members, parameters); 179 ImmutableMap<String, Integer> invariableHashes = invariableHashes(members, parameters.keySet()); 180 vars.invariableHashSum = 0; 181 for (int h : invariableHashes.values()) { 182 vars.invariableHashSum += h; 183 } 184 vars.invariableHashes = invariableHashes.keySet(); 185 String text = vars.toText(); 186 text = TypeEncoder.decode(text, processingEnv, pkg, annotationElement.asType()); 187 text = Reformatter.fixup(text); 188 String fullName = fullyQualifiedName(pkg, generatedClassName); 189 writeSourceFile(fullName, text, methodClass); 190 } 191 getGeneratedTypeName()192 private String getGeneratedTypeName() { 193 return generatedAnnotation(elementUtils, processingEnv.getSourceVersion()) 194 .map(generatedAnnotation -> TypeEncoder.encode(generatedAnnotation.asType())) 195 .orElse(""); 196 } 197 equalsParameterType()198 private String equalsParameterType() { 199 // Unlike AutoValue, we don't currently try to guess a @Nullable based on the methods in your 200 // class. It's the default one or nothing. 201 ImmutableList<AnnotationMirror> equalsParameterAnnotations = 202 Nullables.fromMethods(processingEnv, ImmutableList.of()).nullableTypeAnnotations(); 203 return TypeEncoder.encodeWithAnnotations(javaLangObject, equalsParameterAnnotations); 204 } 205 206 /** 207 * Returns the hashCode of the given AnnotationValue, if that hashCode is guaranteed to be always 208 * the same. The hashCode of a String or primitive type never changes. The hashCode of a Class or 209 * an enum constant does potentially change in different runs of the same program. The hashCode of 210 * an array doesn't change if the hashCodes of its elements don't. Although we could have a 211 * similar rule for nested annotation values, we currently don't. 212 */ invariableHash(AnnotationValue annotationValue)213 private static Optional<Integer> invariableHash(AnnotationValue annotationValue) { 214 Object value = annotationValue.getValue(); 215 if (value instanceof String || Primitives.isWrapperType(value.getClass())) { 216 return Optional.of(value.hashCode()); 217 } else if (value instanceof List<?>) { 218 @SuppressWarnings("unchecked") // by specification 219 List<? extends AnnotationValue> list = (List<? extends AnnotationValue>) value; 220 return invariableHash(list); 221 } else { 222 return Optional.empty(); 223 } 224 } 225 invariableHash( List<? extends AnnotationValue> annotationValues)226 private static Optional<Integer> invariableHash( 227 List<? extends AnnotationValue> annotationValues) { 228 int h = 1; 229 for (AnnotationValue annotationValue : annotationValues) { 230 Optional<Integer> maybeHash = invariableHash(annotationValue); 231 if (!maybeHash.isPresent()) { 232 return Optional.empty(); 233 } 234 h = h * 31 + maybeHash.get(); 235 } 236 return Optional.of(h); 237 } 238 239 /** 240 * Returns a map from the names of members with invariable hashCodes to the values of those 241 * hashCodes. 242 */ invariableHashes( ImmutableMap<String, Member> members, ImmutableSet<String> parameters)243 private static ImmutableMap<String, Integer> invariableHashes( 244 ImmutableMap<String, Member> members, ImmutableSet<String> parameters) { 245 ImmutableMap.Builder<String, Integer> builder = ImmutableMap.builder(); 246 for (String element : members.keySet()) { 247 if (!parameters.contains(element)) { 248 Member member = members.get(element); 249 AnnotationValue annotationValue = member.method.getDefaultValue(); 250 Optional<Integer> invariableHash = invariableHash(annotationValue); 251 if (invariableHash.isPresent()) { 252 builder.put(element, (element.hashCode() * 127) ^ invariableHash.get()); 253 } 254 } 255 } 256 return builder.build(); 257 } 258 methodsAreOverloaded(List<ExecutableElement> methods)259 private boolean methodsAreOverloaded(List<ExecutableElement> methods) { 260 boolean overloaded = false; 261 Set<String> classNames = new HashSet<>(); 262 for (ExecutableElement method : methods) { 263 String qualifiedClassName = 264 fullyQualifiedName( 265 MoreElements.getPackage(method).getQualifiedName().toString(), 266 generatedClassName(method)); 267 if (!classNames.add(qualifiedClassName)) { 268 overloaded = true; 269 reportError(method, "@AutoAnnotation methods cannot be overloaded"); 270 } 271 } 272 return overloaded; 273 } 274 generatedClassName(ExecutableElement method)275 private String generatedClassName(ExecutableElement method) { 276 TypeElement type = MoreElements.asType(method.getEnclosingElement()); 277 String name = type.getSimpleName().toString(); 278 while (MoreElements.isType(type.getEnclosingElement())) { 279 type = MoreElements.asType(type.getEnclosingElement()); 280 name = type.getSimpleName() + "_" + name; 281 } 282 return "AutoAnnotation_" + name + "_" + method.getSimpleName(); 283 } 284 getAnnotationReturnType(ExecutableElement method)285 private TypeElement getAnnotationReturnType(ExecutableElement method) { 286 TypeMirror returnTypeMirror = method.getReturnType(); 287 if (returnTypeMirror.getKind() == TypeKind.DECLARED) { 288 Element returnTypeElement = typeUtils.asElement(method.getReturnType()); 289 if (returnTypeElement.getKind() == ElementKind.ANNOTATION_TYPE) { 290 return MoreElements.asType(returnTypeElement); 291 } 292 } 293 throw abortWithError( 294 method, 295 "Return type of @AutoAnnotation method must be an annotation type, not %s", 296 returnTypeMirror); 297 } 298 getMemberMethods(TypeElement annotationElement)299 private ImmutableMap<String, ExecutableElement> getMemberMethods(TypeElement annotationElement) { 300 ImmutableMap.Builder<String, ExecutableElement> members = ImmutableMap.builder(); 301 for (ExecutableElement member : 302 ElementFilter.methodsIn(annotationElement.getEnclosedElements())) { 303 String name = member.getSimpleName().toString(); 304 members.put(name, member); 305 } 306 return members.build(); 307 } 308 getMembers( Element context, ImmutableMap<String, ExecutableElement> memberMethods)309 private ImmutableMap<String, Member> getMembers( 310 Element context, ImmutableMap<String, ExecutableElement> memberMethods) { 311 ImmutableMap.Builder<String, Member> members = ImmutableMap.builder(); 312 for (Map.Entry<String, ExecutableElement> entry : memberMethods.entrySet()) { 313 ExecutableElement memberMethod = entry.getValue(); 314 String name = memberMethod.getSimpleName().toString(); 315 members.put(name, new Member(processingEnv, context, memberMethod)); 316 } 317 return members.build(); 318 } 319 getDefaultValues(TypeElement annotationElement)320 private ImmutableMap<String, AnnotationValue> getDefaultValues(TypeElement annotationElement) { 321 ImmutableMap.Builder<String, AnnotationValue> defaultValues = ImmutableMap.builder(); 322 for (ExecutableElement member : 323 ElementFilter.methodsIn(annotationElement.getEnclosedElements())) { 324 String name = member.getSimpleName().toString(); 325 AnnotationValue defaultValue = member.getDefaultValue(); 326 if (defaultValue != null) { 327 defaultValues.put(name, defaultValue); 328 } 329 } 330 return defaultValues.build(); 331 } 332 getParameters( TypeElement annotationElement, ExecutableElement method, Map<String, Member> members)333 private ImmutableMap<String, Parameter> getParameters( 334 TypeElement annotationElement, ExecutableElement method, Map<String, Member> members) { 335 ImmutableMap.Builder<String, Parameter> parameters = ImmutableMap.builder(); 336 boolean error = false; 337 for (VariableElement parameter : method.getParameters()) { 338 String name = parameter.getSimpleName().toString(); 339 Member member = members.get(name); 340 if (member == null) { 341 reportError( 342 parameter, 343 "@AutoAnnotation method parameter '%s' must have the same name as a member of %s", 344 name, 345 annotationElement); 346 error = true; 347 } else { 348 TypeMirror parameterType = parameter.asType(); 349 TypeMirror memberType = member.getTypeMirror(); 350 if (compatibleTypes(parameterType, memberType)) { 351 parameters.put(name, new Parameter(parameterType)); 352 } else { 353 reportError( 354 parameter, 355 "@AutoAnnotation method parameter '%s' has type %s but %s.%s has type %s", 356 name, 357 parameterType, 358 annotationElement, 359 name, 360 memberType); 361 error = true; 362 } 363 } 364 } 365 if (error) { 366 throw new AbortProcessingException(); 367 } 368 return parameters.build(); 369 } 370 validateParameters( TypeElement annotationElement, ExecutableElement method, ImmutableMap<String, Member> members, ImmutableMap<String, Parameter> parameters, ImmutableMap<String, AnnotationValue> defaultValues)371 private void validateParameters( 372 TypeElement annotationElement, 373 ExecutableElement method, 374 ImmutableMap<String, Member> members, 375 ImmutableMap<String, Parameter> parameters, 376 ImmutableMap<String, AnnotationValue> defaultValues) { 377 boolean error = false; 378 for (String memberName : members.keySet()) { 379 if (!parameters.containsKey(memberName) && !defaultValues.containsKey(memberName)) { 380 reportError( 381 method, 382 "@AutoAnnotation method needs a parameter with name '%s' and type %s" 383 + " corresponding to %s.%s, which has no default value", 384 memberName, 385 members.get(memberName).getType(), 386 annotationElement, 387 memberName); 388 error = true; 389 } 390 } 391 if (error) { 392 throw new AbortProcessingException(); 393 } 394 } 395 396 /** 397 * Returns true if {@code parameterType} can be used to provide the value of an annotation member 398 * of type {@code memberType}. They must either be the same type, or the member type must be an 399 * array and the parameter type must be a collection of a compatible type. 400 */ compatibleTypes(TypeMirror parameterType, TypeMirror memberType)401 private boolean compatibleTypes(TypeMirror parameterType, TypeMirror memberType) { 402 if (typeUtils.isAssignable(parameterType, memberType)) { 403 // parameterType assignable to memberType, which in the restricted world of annotations 404 // means they are the same type, or maybe memberType is an annotation type and parameterType 405 // is a subtype of that annotation interface (why would you do that?). 406 return true; 407 } 408 // They're not the same, but we could still consider them compatible if for example 409 // parameterType is List<Integer> and memberType is int[]. We accept any type that is assignable 410 // to Collection<Integer> (in this example). 411 if (memberType.getKind() != TypeKind.ARRAY) { 412 return false; 413 } 414 TypeMirror arrayElementType = MoreTypes.asArray(memberType).getComponentType(); 415 TypeMirror wrappedArrayElementType = 416 arrayElementType.getKind().isPrimitive() 417 ? typeUtils.boxedClass((PrimitiveType) arrayElementType).asType() 418 : arrayElementType; 419 TypeElement javaUtilCollection = 420 elementUtils.getTypeElement(Collection.class.getCanonicalName()); 421 DeclaredType collectionOfElement = 422 typeUtils.getDeclaredType(javaUtilCollection, wrappedArrayElementType); 423 return typeUtils.isAssignable(parameterType, collectionOfElement); 424 } 425 426 /** 427 * Returns the wrapper types ({@code Integer.class} etc) that are used in collection parameters 428 * like {@code List<Integer>}. This is needed because we will emit a helper method for each such 429 * type, for example to convert {@code Collection<Integer>} into {@code int[]}. 430 */ wrapperTypesUsedInCollections(ExecutableElement method)431 private ImmutableSet<Class<?>> wrapperTypesUsedInCollections(ExecutableElement method) { 432 TypeElement javaUtilCollection = elementUtils.getTypeElement(Collection.class.getName()); 433 ImmutableSet.Builder<Class<?>> usedInCollections = ImmutableSet.builder(); 434 for (Class<?> wrapper : Primitives.allWrapperTypes()) { 435 DeclaredType collectionOfWrapper = 436 typeUtils.getDeclaredType(javaUtilCollection, getTypeMirror(wrapper)); 437 for (VariableElement parameter : method.getParameters()) { 438 if (typeUtils.isAssignable(parameter.asType(), collectionOfWrapper)) { 439 usedInCollections.add(wrapper); 440 break; 441 } 442 } 443 } 444 return usedInCollections.build(); 445 } 446 getTypeMirror(Class<?> c)447 private TypeMirror getTypeMirror(Class<?> c) { 448 return elementUtils.getTypeElement(c.getName()).asType(); 449 } 450 isGwtCompatible(TypeElement annotationElement)451 private static boolean isGwtCompatible(TypeElement annotationElement) { 452 return annotationElement.getAnnotationMirrors().stream() 453 .map(mirror -> mirror.getAnnotationType().asElement()) 454 .anyMatch(element -> element.getSimpleName().contentEquals("GwtCompatible")); 455 } 456 fullyQualifiedName(String pkg, String cls)457 private static String fullyQualifiedName(String pkg, String cls) { 458 return pkg.isEmpty() ? cls : pkg + "." + cls; 459 } 460 461 /** 462 * We compute a {@code serialVersionUID} for the generated class based on the names and types of 463 * the annotation members that the {@code @AutoAnnotation} method defines. These are exactly the 464 * names and types of the instance fields in the generated class. So in the common case where the 465 * annotation acquires a new member with a default value, if the {@code @AutoAnnotation} method is 466 * not changed then the generated class will acquire an implementation of the new member method 467 * which just returns the default value. The {@code serialVersionUID} will not change, which makes 468 * sense because the instance fields haven't changed, and instances that were serialized before 469 * the new member was added should deserialize fine. On the other hand, if you then add a 470 * parameter to the {@code @AutoAnnotation} method for the new member, the implementation class 471 * will acquire a new instance field, and we will compute a different {@code serialVersionUID}. 472 * That's because an instance serialized before that change would not have a value for the new 473 * instance field, which would end up zero or null. Users don't expect annotation methods to 474 * return null so that would be bad. 475 * 476 * <p>We could instead add a {@code readObject(ObjectInputStream)} method that would check that 477 * all of the instance fields are really present in the deserialized instance, and perhaps 478 * replace them with their default values from the annotation if not. That seems a lot more 479 * complicated than is justified, though, especially since the instance fields are final and 480 * would have to be set in the deserialized object through reflection. 481 */ computeSerialVersionUid( ImmutableMap<String, Member> members, ImmutableMap<String, Parameter> parameters)482 private static long computeSerialVersionUid( 483 ImmutableMap<String, Member> members, ImmutableMap<String, Parameter> parameters) { 484 // TypeMirror.toString() isn't fully specified so it could potentially differ between 485 // implementations. Our member.getType() string comes from TypeEncoder and is predictable, but 486 // it includes `...` markers around fully-qualified type names, which are used to handle 487 // imports. So we remove those markers below. 488 String namesAndTypesString = 489 members.entrySet().stream() 490 .filter(e -> parameters.containsKey(e.getKey())) 491 .map(e -> immutableEntry(e.getKey(), e.getValue().getType().replace("`", ""))) 492 .sorted(comparing(Map.Entry::getKey)) 493 .map(e -> e.getKey() + ":" + e.getValue()) 494 .collect(joining(";")); 495 return Hashing.murmur3_128().hashUnencodedChars(namesAndTypesString).asLong(); 496 } 497 writeSourceFile(String className, String text, TypeElement originatingType)498 private void writeSourceFile(String className, String text, TypeElement originatingType) { 499 try { 500 JavaFileObject sourceFile = 501 processingEnv.getFiler().createSourceFile(className, originatingType); 502 try (Writer writer = sourceFile.openWriter()) { 503 writer.write(text); 504 } 505 } catch (IOException e) { 506 // This should really be an error, but we make it a warning in the hope of resisting Eclipse 507 // bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=367599. If that bug manifests, we may get 508 // invoked more than once for the same file, so ignoring the ability to overwrite it is the 509 // right thing to do. If we are unable to write for some other reason, we should get a compile 510 // error later because user code will have a reference to the code we were supposed to 511 // generate (new AutoValue_Foo() or whatever) and that reference will be undefined. 512 processingEnv 513 .getMessager() 514 .printMessage( 515 Diagnostic.Kind.WARNING, "Could not write generated class " + className + ": " + e); 516 } 517 } 518 519 public static class Member { 520 private final ProcessingEnvironment processingEnv; 521 private final Element context; 522 private final ExecutableElement method; 523 Member(ProcessingEnvironment processingEnv, Element context, ExecutableElement method)524 Member(ProcessingEnvironment processingEnv, Element context, ExecutableElement method) { 525 this.processingEnv = processingEnv; 526 this.context = context; 527 this.method = method; 528 } 529 530 @Override toString()531 public String toString() { 532 return method.getSimpleName().toString(); 533 } 534 getType()535 public String getType() { 536 return TypeEncoder.encode(getTypeMirror()); 537 } 538 getComponentType()539 public String getComponentType() { 540 Preconditions.checkState(getTypeMirror().getKind() == TypeKind.ARRAY); 541 ArrayType arrayType = MoreTypes.asArray(getTypeMirror()); 542 return TypeEncoder.encode(arrayType.getComponentType()); 543 } 544 getTypeMirror()545 public TypeMirror getTypeMirror() { 546 return method.getReturnType(); 547 } 548 getKind()549 public TypeKind getKind() { 550 return getTypeMirror().getKind(); 551 } 552 553 // Used as part of the hashCode() computation. 554 // See https://docs.oracle.com/javase/8/docs/api/java/lang/annotation/Annotation.html#hashCode-- getNameHash()555 public int getNameHash() { 556 return 127 * toString().hashCode(); 557 } 558 isArrayOfClassWithBounds()559 public boolean isArrayOfClassWithBounds() { 560 if (getTypeMirror().getKind() != TypeKind.ARRAY) { 561 return false; 562 } 563 TypeMirror componentType = MoreTypes.asArray(getTypeMirror()).getComponentType(); 564 if (componentType.getKind() != TypeKind.DECLARED) { 565 return false; 566 } 567 DeclaredType declared = MoreTypes.asDeclared(componentType); 568 if (!MoreElements.asType(processingEnv.getTypeUtils().asElement(componentType)) 569 .getQualifiedName() 570 .contentEquals("java.lang.Class")) { 571 return false; 572 } 573 if (declared.getTypeArguments().size() != 1) { 574 return false; 575 } 576 TypeMirror parameter = declared.getTypeArguments().get(0); 577 if (parameter.getKind() != TypeKind.WILDCARD) { 578 return true; // for Class<Foo> 579 } 580 WildcardType wildcard = MoreTypes.asWildcard(parameter); 581 // In theory, we should check if getExtendsBound() != Object, since '?' is equivalent to 582 // '? extends Object', but, experimentally, neither javac or ecj will sets getExtendsBound() 583 // to 'Object', so there isn't a point in checking. 584 return wildcard.getSuperBound() != null || wildcard.getExtendsBound() != null; 585 } 586 getDefaultValue()587 public String getDefaultValue() { 588 AnnotationValue defaultValue = method.getDefaultValue(); 589 if (defaultValue == null) { 590 return null; 591 } else { 592 return AnnotationOutput.sourceFormForInitializer( 593 defaultValue, processingEnv, method.getSimpleName().toString(), context); 594 } 595 } 596 } 597 598 public static class Parameter { 599 private final String typeName; 600 private final TypeKind kind; 601 Parameter(TypeMirror type)602 Parameter(TypeMirror type) { 603 this.typeName = TypeEncoder.encode(type); 604 this.kind = type.getKind(); 605 } 606 getType()607 public String getType() { 608 return typeName; 609 } 610 getKind()611 public TypeKind getKind() { 612 return kind; 613 } 614 } 615 } 616