• 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.common.base.Preconditions.checkNotNull;
20 
21 import com.google.common.base.Equivalence;
22 import com.google.common.base.Predicate;
23 import com.google.common.collect.FluentIterable;
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.List;
29 import java.util.Map;
30 import javax.lang.model.element.AnnotationMirror;
31 import javax.lang.model.element.AnnotationValue;
32 import javax.lang.model.element.Element;
33 import javax.lang.model.element.ExecutableElement;
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().equivalent(left.getAnnotationType(),
49               right.getAnnotationType()) && AnnotationValues.equivalence().pairwise().equivalent(
50               getAnnotationValuesWithDefaults(left).values(),
51               getAnnotationValuesWithDefaults(right).values());
52         }
53         @Override
54         protected int doHash(AnnotationMirror annotation) {
55           DeclaredType type = annotation.getAnnotationType();
56           Iterable<AnnotationValue> annotationValues =
57               getAnnotationValuesWithDefaults(annotation).values();
58           return Arrays.hashCode(new int[] {MoreTypes.equivalence().hash(type),
59               AnnotationValues.equivalence().pairwise().hash(annotationValues)});
60         }
61     };
62 
63   /**
64    * Returns an {@link Equivalence} for {@link AnnotationMirror} as some implementations
65    * delegate equality tests to {@link Object#equals} whereas the documentation explicitly
66    * states that instance/reference equality is not the proper test.
67    */
equivalence()68   public static Equivalence<AnnotationMirror> equivalence() {
69     return ANNOTATION_MIRROR_EQUIVALENCE;
70   }
71 
72   /**
73    * Returns the {@link AnnotationMirror}'s map of {@link AnnotationValue} indexed by {@link
74    * ExecutableElement}, supplying default values from the annotation if the annotation property has
75    * not been set. This is equivalent to {@link
76    * Elements#getElementValuesWithDefaults(AnnotationMirror)} but can be called statically without
77    * an {@link Elements} instance.
78    *
79    * <p>The iteration order of elements of the returned map will be the order in which the {@link
80    * ExecutableElement}s are defined in {@code annotation}'s {@linkplain
81    * AnnotationMirror#getAnnotationType() type}.
82    */
getAnnotationValuesWithDefaults( AnnotationMirror annotation)83   public static ImmutableMap<ExecutableElement, AnnotationValue> getAnnotationValuesWithDefaults(
84       AnnotationMirror annotation) {
85     ImmutableMap.Builder<ExecutableElement, AnnotationValue> values = ImmutableMap.builder();
86     Map<? extends ExecutableElement, ? extends AnnotationValue> declaredValues =
87         annotation.getElementValues();
88     for (ExecutableElement method :
89         ElementFilter.methodsIn(annotation.getAnnotationType().asElement().getEnclosedElements())) {
90       // Must iterate and put in this order, to ensure consistency in generated code.
91       if (declaredValues.containsKey(method)) {
92         values.put(method, declaredValues.get(method));
93       } else if (method.getDefaultValue() != null) {
94         values.put(method, method.getDefaultValue());
95       } else {
96         throw new IllegalStateException(
97             "Unset annotation value without default should never happen: "
98             + MoreElements.asType(method.getEnclosingElement()).getQualifiedName()
99             + '.' + method.getSimpleName() + "()");
100       }
101     }
102     return values.build();
103   }
104 
105   /**
106    * Returns an {@link AnnotationValue} for the named element if such an element was
107    * either declared in the usage represented by the provided {@link AnnotationMirror}, or if
108    * such an element was defined with a default.
109    *
110    * @throws IllegalArgumentException if no element is defined with the given elementName.
111    */
getAnnotationValue( AnnotationMirror annotationMirror, String elementName)112   public static AnnotationValue getAnnotationValue(
113       AnnotationMirror annotationMirror, String elementName) {
114     return getAnnotationElementAndValue(annotationMirror, elementName).getValue();
115   }
116 
117   /**
118    * Returns a {@link ExecutableElement} and its associated {@link AnnotationValue} if such
119    * an element was either declared in the usage represented by the provided
120    * {@link AnnotationMirror}, or if such an element was defined with a default.
121    *
122    * @throws IllegalArgumentException if no element is defined with the given elementName.
123    */
getAnnotationElementAndValue( AnnotationMirror annotationMirror, final String elementName)124   public static Map.Entry<ExecutableElement, AnnotationValue> getAnnotationElementAndValue(
125       AnnotationMirror annotationMirror, final String elementName) {
126     checkNotNull(annotationMirror);
127     checkNotNull(elementName);
128     for (Map.Entry<ExecutableElement, AnnotationValue> entry :
129         getAnnotationValuesWithDefaults(annotationMirror).entrySet()) {
130       if (entry.getKey().getSimpleName().contentEquals(elementName)) {
131         return entry;
132       }
133     }
134     throw new IllegalArgumentException(String.format("@%s does not define an element %s()",
135         MoreElements.asType(annotationMirror.getAnnotationType().asElement()).getQualifiedName(),
136         elementName));
137   }
138 
139   /**
140    * Returns all {@linkplain AnnotationMirror annotations} that are present on the given
141    * {@link Element} which are themselves annotated with {@code annotationType}.
142    */
getAnnotatedAnnotations(Element element, final Class<? extends Annotation> annotationType)143   public static ImmutableSet<? extends AnnotationMirror> getAnnotatedAnnotations(Element element,
144       final Class<? extends Annotation> annotationType) {
145     List<? extends AnnotationMirror> annotations = element.getAnnotationMirrors();
146     return FluentIterable.from(annotations)
147         .filter(new Predicate<AnnotationMirror>() {
148           @Override public boolean apply(AnnotationMirror input) {
149             return isAnnotationPresent(input.getAnnotationType().asElement(), annotationType);
150           }
151         })
152         .toSet();
153   }
154 
155   private AnnotationMirrors() {}
156 }
157