• 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.common;
17 
18 import static com.google.auto.common.MoreElements.isAnnotationPresent;
19 import static com.google.auto.common.MoreStreams.toImmutableSet;
20 import static com.google.common.base.Preconditions.checkNotNull;
21 import static java.util.Collections.unmodifiableMap;
22 
23 import com.google.common.base.Equivalence;
24 import com.google.common.collect.ImmutableMap;
25 import com.google.common.collect.ImmutableSet;
26 import java.lang.annotation.Annotation;
27 import java.util.Arrays;
28 import java.util.Map;
29 import javax.lang.model.element.AnnotationMirror;
30 import javax.lang.model.element.AnnotationValue;
31 import javax.lang.model.element.Element;
32 import javax.lang.model.element.ExecutableElement;
33 import javax.lang.model.element.TypeElement;
34 import javax.lang.model.type.DeclaredType;
35 import javax.lang.model.util.ElementFilter;
36 import javax.lang.model.util.Elements;
37 
38 /**
39  * A utility class for working with {@link AnnotationMirror} instances.
40  *
41  * @author Gregory Kick
42  */
43 public final class AnnotationMirrors {
44   private static final Equivalence<AnnotationMirror> ANNOTATION_MIRROR_EQUIVALENCE =
45       new Equivalence<AnnotationMirror>() {
46         @Override
47         protected boolean doEquivalent(AnnotationMirror left, AnnotationMirror right) {
48           return MoreTypes.equivalence()
49                   .equivalent(left.getAnnotationType(), right.getAnnotationType())
50               && AnnotationValues.equivalence()
51                   .pairwise()
52                   .equivalent(
53                       getAnnotationValuesWithDefaults(left).values(),
54                       getAnnotationValuesWithDefaults(right).values());
55         }
56 
57         @Override
58         protected int doHash(AnnotationMirror annotation) {
59           DeclaredType type = annotation.getAnnotationType();
60           Iterable<AnnotationValue> annotationValues =
61               getAnnotationValuesWithDefaults(annotation).values();
62           return Arrays.hashCode(
63               new int[] {
64                 MoreTypes.equivalence().hash(type),
65                 AnnotationValues.equivalence().pairwise().hash(annotationValues)
66               });
67         }
68 
69         @Override
70         public String toString() {
71           return "AnnotationMirrors.equivalence()";
72         }
73       };
74 
75   /**
76    * Returns an {@link Equivalence} for {@link AnnotationMirror} as some implementations
77    * delegate equality tests to {@link Object#equals} whereas the documentation explicitly
78    * states that instance/reference equality is not the proper test.
79    */
equivalence()80   public static Equivalence<AnnotationMirror> equivalence() {
81     return ANNOTATION_MIRROR_EQUIVALENCE;
82   }
83 
84   /**
85    * Returns the {@link AnnotationMirror}'s map of {@link AnnotationValue} indexed by {@link
86    * ExecutableElement}, supplying default values from the annotation if the annotation property has
87    * not been set. This is equivalent to {@link
88    * Elements#getElementValuesWithDefaults(AnnotationMirror)} but can be called statically without
89    * an {@link Elements} instance.
90    *
91    * <p>The iteration order of elements of the returned map will be the order in which the {@link
92    * ExecutableElement}s are defined in {@code annotation}'s {@linkplain
93    * AnnotationMirror#getAnnotationType() type}.
94    */
getAnnotationValuesWithDefaults( AnnotationMirror annotation)95   public static ImmutableMap<ExecutableElement, AnnotationValue> getAnnotationValuesWithDefaults(
96       AnnotationMirror annotation) {
97     ImmutableMap.Builder<ExecutableElement, AnnotationValue> values = ImmutableMap.builder();
98     // Use unmodifiableMap to eliminate wildcards, which cause issues for our nullness checker.
99     @SuppressWarnings("GetElementValues")
100     Map<ExecutableElement, AnnotationValue> declaredValues =
101         unmodifiableMap(annotation.getElementValues());
102     for (ExecutableElement method :
103         ElementFilter.methodsIn(annotation.getAnnotationType().asElement().getEnclosedElements())) {
104       // Must iterate and put in this order, to ensure consistency in generated code.
105       if (declaredValues.containsKey(method)) {
106         values.put(method, declaredValues.get(method));
107       } else if (method.getDefaultValue() != null) {
108         values.put(method, method.getDefaultValue());
109       } else {
110         throw new IllegalStateException(
111             "Unset annotation value without default should never happen: "
112                 + MoreElements.asType(method.getEnclosingElement()).getQualifiedName()
113                 + '.'
114                 + method.getSimpleName()
115                 + "()");
116       }
117     }
118     return values.build();
119   }
120 
121   /**
122    * Returns an {@link AnnotationValue} for the named element if such an element was
123    * either declared in the usage represented by the provided {@link AnnotationMirror}, or if
124    * such an element was defined with a default.
125    *
126    * @throws IllegalArgumentException if no element is defined with the given elementName.
127    */
getAnnotationValue( AnnotationMirror annotationMirror, String elementName)128   public static AnnotationValue getAnnotationValue(
129       AnnotationMirror annotationMirror, String elementName) {
130     return getAnnotationElementAndValue(annotationMirror, elementName).getValue();
131   }
132 
133   /**
134    * Returns a {@link ExecutableElement} and its associated {@link AnnotationValue} if such
135    * an element was either declared in the usage represented by the provided
136    * {@link AnnotationMirror}, or if such an element was defined with a default.
137    *
138    * @throws IllegalArgumentException if no element is defined with the given elementName.
139    */
getAnnotationElementAndValue( AnnotationMirror annotationMirror, final String elementName)140   public static Map.Entry<ExecutableElement, AnnotationValue> getAnnotationElementAndValue(
141       AnnotationMirror annotationMirror, final String elementName) {
142     checkNotNull(annotationMirror);
143     checkNotNull(elementName);
144     for (Map.Entry<ExecutableElement, AnnotationValue> entry :
145         getAnnotationValuesWithDefaults(annotationMirror).entrySet()) {
146       if (entry.getKey().getSimpleName().contentEquals(elementName)) {
147         return entry;
148       }
149     }
150     throw new IllegalArgumentException(
151         String.format(
152             "@%s does not define an element %s()",
153             MoreElements.asType(annotationMirror.getAnnotationType().asElement())
154                 .getQualifiedName(),
155             elementName));
156   }
157 
158   /**
159    * Returns all {@linkplain AnnotationMirror annotations} that are present on the given {@link
160    * Element} which are themselves annotated with {@code annotationClass}.
161    */
getAnnotatedAnnotations( Element element, Class<? extends Annotation> annotationClass)162   public static ImmutableSet<AnnotationMirror> getAnnotatedAnnotations(
163       Element element, Class<? extends Annotation> annotationClass) {
164     String name = annotationClass.getCanonicalName();
165     if (name == null) {
166       return ImmutableSet.of();
167     }
168     return getAnnotatedAnnotations(element, name);
169   }
170 
171   /**
172    * Returns all {@linkplain AnnotationMirror annotations} that are present on the given {@link
173    * Element} which are themselves annotated with {@code annotation}.
174    */
getAnnotatedAnnotations( Element element, TypeElement annotation)175   public static ImmutableSet<AnnotationMirror> getAnnotatedAnnotations(
176       Element element, TypeElement annotation) {
177     return element.getAnnotationMirrors().stream()
178         .filter(input -> isAnnotationPresent(input.getAnnotationType().asElement(), annotation))
179         .collect(toImmutableSet());
180   }
181 
182   /**
183    * Returns all {@linkplain AnnotationMirror annotations} that are present on the given {@link
184    * Element} which are themselves annotated with an annotation whose type's canonical name is
185    * {@code annotationName}.
186    */
getAnnotatedAnnotations( Element element, String annotationName)187   public static ImmutableSet<AnnotationMirror> getAnnotatedAnnotations(
188       Element element, String annotationName) {
189     return element.getAnnotationMirrors().stream()
190         .filter(input -> isAnnotationPresent(input.getAnnotationType().asElement(), annotationName))
191         .collect(toImmutableSet());
192   }
193 
194   /**
195    * Returns a string representation of the given annotation mirror, suitable for inclusion in a
196    * Java source file to reproduce the annotation in source form.
197    *
198    * <p>Fully qualified names are used for types in annotations, class literals, and enum constants,
199    * ensuring that the source form will compile without requiring additional imports.
200    */
toString(AnnotationMirror annotationMirror)201   public static String toString(AnnotationMirror annotationMirror) {
202     return AnnotationOutput.toString(annotationMirror);
203   }
204 
AnnotationMirrors()205   private AnnotationMirrors() {}
206 }
207