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