• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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