• 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.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