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