diff options
Diffstat (limited to 'third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/AnnotationUtils.java')
-rw-r--r-- | third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/AnnotationUtils.java | 501 |
1 files changed, 307 insertions, 194 deletions
diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/AnnotationUtils.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/AnnotationUtils.java index 54eb54d2fb..c80f57cc5d 100644 --- a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/AnnotationUtils.java +++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/AnnotationUtils.java @@ -7,7 +7,12 @@ import org.checkerframework.checker.nullness.qual.*; import org.checkerframework.checker.interning.qual.*; */ - +import com.sun.source.tree.AnnotationTree; +import com.sun.source.tree.MethodTree; +import com.sun.source.tree.ModifiersTree; +import com.sun.tools.javac.code.Symbol.VarSymbol; +import com.sun.tools.javac.code.Type; +import com.sun.tools.javac.model.JavacElements; import java.lang.annotation.Annotation; import java.lang.annotation.Inherited; import java.util.ArrayList; @@ -21,7 +26,6 @@ import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; - import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.ElementKind; @@ -33,23 +37,19 @@ import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; import javax.lang.model.util.Elements; -import com.sun.tools.javac.code.Symbol.VarSymbol; -import com.sun.tools.javac.code.Type; -import com.sun.tools.javac.model.JavacElements; -/** - * A utility class for working with annotations. - */ +/** A utility class for working with annotations. */ public class AnnotationUtils { // Class cannot be instantiated. - private AnnotationUtils() { throw new AssertionError("Class AnnotationUtils cannot be instantiated."); } + private AnnotationUtils() { + throw new AssertionError("Class AnnotationUtils cannot be instantiated."); + } // TODO: hack to clear out static state. - // {@link org.checkerframework.qualframework.util.QualifierContext} should - // handle instantiation of utility classes. public static void clear() { annotationsFromNames.clear(); annotationMirrorNames.clear(); + annotationMirrorSimpleNames.clear(); annotationClassNames.clear(); } @@ -58,68 +58,85 @@ public class AnnotationUtils { // ********************************************************************** /** Caching for annotation creation. */ - private static final Map<CharSequence, AnnotationMirror> annotationsFromNames - = new HashMap<CharSequence, AnnotationMirror>(); + private static final Map<CharSequence, AnnotationMirror> annotationsFromNames = + Collections.synchronizedMap(new HashMap<CharSequence, AnnotationMirror>()); + + private static final int ANNOTATION_CACHE_SIZE = 500; /** - * Cache names of AnnotationMirrors for faster access. Values in - * the map are interned Strings, so they can be compared with ==. + * Cache names of AnnotationMirrors for faster access. Values in the map are interned Strings, + * so they can be compared with ==. */ - private static final Map<AnnotationMirror, /*@Interned*/ String> annotationMirrorNames - = new HashMap<AnnotationMirror, /*@Interned*/ String>(); + private static final Map<AnnotationMirror, /*@Interned*/ String> annotationMirrorNames = + Collections.synchronizedMap( + CollectionUtils.<AnnotationMirror, /*@Interned*/ String>createLRUCache( + ANNOTATION_CACHE_SIZE)); /** - * Cache names of classes representing AnnotationMirrors for - * faster access. Values in the map are interned Strings, so they - * can be compared with ==. + * Cache simple names of AnnotationMirrors for faster access. Values in the map are interned + * Strings, so they can be compared with ==. */ - private static final Map<Class<? extends Annotation>, /*@Interned*/ String> annotationClassNames - = new HashMap<Class<? extends Annotation>, /*@Interned*/ String>(); + private static final Map<AnnotationMirror, /*@Interned*/ String> annotationMirrorSimpleNames = + Collections.synchronizedMap( + CollectionUtils.<AnnotationMirror, /*@Interned*/ String>createLRUCache( + ANNOTATION_CACHE_SIZE)); /** - * Creates an {@link AnnotationMirror} given by a particular - * fully-qualified name. getElementValues on the result returns an - * empty map. + * Cache names of classes representing AnnotationMirrors for faster access. Values in the map + * are interned Strings, so they can be compared with ==. + */ + private static final Map<Class<? extends Annotation>, /*@Interned*/ String> + annotationClassNames = + Collections.synchronizedMap( + new HashMap<Class<? extends Annotation>, /*@Interned*/ String>()); + + /** + * Creates an {@link AnnotationMirror} given by a particular fully-qualified name. + * getElementValues on the result returns an empty map. * * @param elements the element utilities to use * @param name the name of the annotation to create * @return an {@link AnnotationMirror} of type {@code} name */ public static AnnotationMirror fromName(Elements elements, CharSequence name) { - if (annotationsFromNames.containsKey(name)) - return annotationsFromNames.get(name); + AnnotationMirror res = annotationsFromNames.get(name); + if (res != null) { + return res; + } final DeclaredType annoType = typeFromName(elements, name); - if (annoType == null) + if (annoType == null) { return null; + } if (annoType.asElement().getKind() != ElementKind.ANNOTATION_TYPE) { ErrorReporter.errorAbort(annoType + " is not an annotation"); return null; // dead code } - AnnotationMirror result = new AnnotationMirror() { - String toString = "@" + annoType; - - @Override - public DeclaredType getAnnotationType() { - return annoType; - } - @Override - public Map<? extends ExecutableElement, ? extends AnnotationValue> - getElementValues() { - return Collections.emptyMap(); - } - /*@SideEffectFree*/ - @Override - public String toString() { - return toString; - } - }; + AnnotationMirror result = + new AnnotationMirror() { + String toString = "@" + annoType; + + @Override + public DeclaredType getAnnotationType() { + return annoType; + } + + @Override + public Map<? extends ExecutableElement, ? extends AnnotationValue> + getElementValues() { + return Collections.emptyMap(); + } + /*@SideEffectFree*/ + @Override + public String toString() { + return toString; + } + }; annotationsFromNames.put(name, result); return result; } /** - * Creates an {@link AnnotationMirror} given by a particular annotation - * class. + * Creates an {@link AnnotationMirror} given by a particular annotation class. * * @param elements the element utilities to use * @param clazz the annotation class @@ -130,8 +147,8 @@ public class AnnotationUtils { } /** - * A utility method that converts a {@link CharSequence} (usually a {@link - * String}) into a {@link TypeMirror} named thereby. + * A utility method that converts a {@link CharSequence} (usually a {@link String}) into a + * {@link TypeMirror} named thereby. * * @param elements the element utilities to use * @param name the name of a type @@ -139,26 +156,25 @@ public class AnnotationUtils { */ private static DeclaredType typeFromName(Elements elements, CharSequence name) { /*@Nullable*/ TypeElement typeElt = elements.getTypeElement(name); - if (typeElt == null) + if (typeElt == null) { return null; + } return (DeclaredType) typeElt.asType(); } - // ********************************************************************** // Helper methods to handle annotations. mainly workaround // AnnotationMirror.equals undesired property // (I think the undesired property is that it's reference equality.) // ********************************************************************** - /** - * @return the fully-qualified name of an annotation as a Name - */ + /** @return the fully-qualified name of an annotation as a String */ public static final /*@Interned*/ String annotationName(AnnotationMirror annotation) { - if (annotationMirrorNames.containsKey(annotation)) - return annotationMirrorNames.get(annotation); - + String res = annotationMirrorNames.get(annotation); + if (res != null) { + return res; + } final DeclaredType annoType = annotation.getAnnotationType(); final TypeElement elm = (TypeElement) annoType.asElement(); /*@Interned*/ String name = elm.getQualifiedName().toString().intern(); @@ -166,25 +182,40 @@ public class AnnotationUtils { return name; } + /** @return the simple name of an annotation as a String */ + public static String annotationSimpleName(AnnotationMirror annotation) { + String res = annotationMirrorSimpleNames.get(annotation); + if (res != null) { + return res; + } + final DeclaredType annoType = annotation.getAnnotationType(); + final TypeElement elm = (TypeElement) annoType.asElement(); + /*@Interned*/ String name = elm.getSimpleName().toString().intern(); + annotationMirrorSimpleNames.put(annotation, name); + return name; + } + /** * Checks if both annotations are the same. * - * Returns true iff both annotations are of the same type and have the - * same annotation values. This behavior differs from - * {@code AnnotationMirror.equals(Object)}. The equals method returns - * true iff both annotations are the same and annotate the same annotation - * target (e.g. field, variable, etc). + * <p>Returns true iff both annotations are of the same type and have the same annotation + * values. This behavior differs from {@code AnnotationMirror.equals(Object)}. The equals method + * returns true iff both annotations are the same and annotate the same annotation target (e.g. + * field, variable, etc). * * @return true iff a1 and a2 are the same annotation */ - public static boolean areSame(/*@Nullable*/ AnnotationMirror a1, /*@Nullable*/ AnnotationMirror a2) { + public static boolean areSame( + /*@Nullable*/ AnnotationMirror a1, /*@Nullable*/ AnnotationMirror a2) { if (a1 != null && a2 != null) { if (annotationName(a1) != annotationName(a2)) { return false; } - Map<? extends ExecutableElement, ? extends AnnotationValue> elval1 = getElementValuesWithDefaults(a1); - Map<? extends ExecutableElement, ? extends AnnotationValue> elval2 = getElementValuesWithDefaults(a2); + Map<? extends ExecutableElement, ? extends AnnotationValue> elval1 = + getElementValuesWithDefaults(a1); + Map<? extends ExecutableElement, ? extends AnnotationValue> elval2 = + getElementValuesWithDefaults(a2); return elval1.toString().equals(elval2.toString()); } @@ -198,30 +229,22 @@ public class AnnotationUtils { * @return true iff a1 and a2 have the same annotation type */ public static boolean areSameIgnoringValues(AnnotationMirror a1, AnnotationMirror a2) { - if (a1 != null && a2 != null) + if (a1 != null && a2 != null) { return annotationName(a1) == annotationName(a2); + } return a1 == a2; } - /** - * Checks that the annotation {@code am} has the name {@code aname}. Values - * are ignored. - */ + /** Checks that the annotation {@code am} has the name {@code aname}. Values are ignored. */ public static boolean areSameByName(AnnotationMirror am, /*@Interned*/ String aname) { // Both strings are interned. return annotationName(am) == aname; } - /** - * Checks that the annotation {@code am} has the name of {@code anno}. - * Values are ignored. - */ - public static boolean areSameByClass(AnnotationMirror am, - Class<? extends Annotation> anno) { - /*@Interned*/ String canonicalName; - if (annotationClassNames.containsKey(anno)) { - canonicalName = annotationClassNames.get(anno).intern(); - } else { + /** Checks that the annotation {@code am} has the name of {@code anno}. Values are ignored. */ + public static boolean areSameByClass(AnnotationMirror am, Class<? extends Annotation> anno) { + /*@Interned*/ String canonicalName = annotationClassNames.get(anno); + if (canonicalName == null) { canonicalName = anno.getCanonicalName().intern(); annotationClassNames.put(anno, canonicalName); } @@ -233,11 +256,14 @@ public class AnnotationUtils { * * @return true iff c1 and c2 contain the same annotations */ - public static boolean areSame(Collection<? extends AnnotationMirror> c1, Collection<? extends AnnotationMirror> c2) { - if (c1.size() != c2.size()) + public static boolean areSame( + Collection<? extends AnnotationMirror> c1, Collection<? extends AnnotationMirror> c2) { + if (c1.size() != c2.size()) { return false; - if (c1.size() == 1) + } + if (c1.size() == 1) { return areSame(c1.iterator().next(), c2.iterator().next()); + } Set<AnnotationMirror> s1 = createAnnotationSet(); Set<AnnotationMirror> s2 = createAnnotationSet(); @@ -251,78 +277,122 @@ public class AnnotationUtils { while (iter1.hasNext()) { AnnotationMirror anno1 = iter1.next(); AnnotationMirror anno2 = iter2.next(); - if (!areSame(anno1, anno2)) + if (!areSame(anno1, anno2)) { return false; + } } return true; } /** - * Checks that the collection contains the annotation. - * Using Collection.contains does not always work, because it - * does not use areSame for comparison. + * Checks that the collection contains the annotation. Using Collection.contains does not always + * work, because it does not use areSame for comparison. * - * @return true iff c contains anno, according to areSame. + * @return true iff c contains anno, according to areSame */ - public static boolean containsSame(Collection<? extends AnnotationMirror> c, AnnotationMirror anno) { - for(AnnotationMirror an : c) { - if(AnnotationUtils.areSame(an, anno)) { - return true; - } - } - return false; + public static boolean containsSame( + Collection<? extends AnnotationMirror> c, AnnotationMirror anno) { + return getSame(c, anno) != null; } /** - * Checks that the collection contains the annotation. - * Using Collection.contains does not always work, because it - * does not use areSame for comparison. + * Returns the AnnotationMirror in {@code c} that is the same annotation as {@code anno}. * - * @return true iff c contains anno, according to areSameByClass. + * @return AnnotationMirror with the same class as {@code anno} iff c contains anno, according + * to areSame; otherwise, {@code null} */ - public static boolean containsSameByClass(Collection<? extends AnnotationMirror> c, Class<? extends Annotation> anno) { - for(AnnotationMirror an : c) { - if(AnnotationUtils.areSameByClass(an, anno)) { - return true; + public static AnnotationMirror getSame( + Collection<? extends AnnotationMirror> c, AnnotationMirror anno) { + for (AnnotationMirror an : c) { + if (AnnotationUtils.areSame(an, anno)) { + return an; } } - return false; + return null; } /** - * Checks that the collection contains the annotation ignoring values. - * Using Collection.contains does not always work, because it - * does not use areSameIgnoringValues for comparison. + * Checks that the collection contains the annotation. Using Collection.contains does not always + * work, because it does not use areSame for comparison. * - * @return true iff c contains anno, according to areSameIgnoringValues. + * @return true iff c contains anno, according to areSameByClass */ - public static boolean containsSameIgnoringValues(Collection<? extends AnnotationMirror> c, AnnotationMirror anno) { - for(AnnotationMirror an : c) { - if(AnnotationUtils.areSameIgnoringValues(an, anno)) { - return true; + public static boolean containsSameByClass( + Collection<? extends AnnotationMirror> c, Class<? extends Annotation> anno) { + return getAnnotationByClass(c, anno) != null; + } + + /** + * Returns the AnnotationMirror in {@code c} that has the same class as {@code anno}. + * + * @return AnnotationMirror with the same class as {@code anno} iff c contains anno, according + * to areSameByClass; otherwise, {@code null} + */ + public static AnnotationMirror getAnnotationByClass( + Collection<? extends AnnotationMirror> c, Class<? extends Annotation> anno) { + for (AnnotationMirror an : c) { + if (AnnotationUtils.areSameByClass(an, anno)) { + return an; } } - return false; + return null; } - private static final Comparator<AnnotationMirror> ANNOTATION_ORDERING - = new Comparator<AnnotationMirror>() { - @Override - public int compare(AnnotationMirror a1, AnnotationMirror a2) { - String n1 = a1.toString(); - String n2 = a2.toString(); + /** + * Checks that the collection contains the annotation ignoring values. Using Collection.contains + * does not always work, because it does not use areSameIgnoringValues for comparison. + * + * @return true iff c contains anno, according to areSameIgnoringValues + */ + public static boolean containsSameIgnoringValues( + Collection<? extends AnnotationMirror> c, AnnotationMirror anno) { + return getSameIgnoringValues(c, anno) != null; + } - return n1.compareTo(n2); + /** + * Returns the AnnotationMirror in {@code c} that is the same annotation as {@code anno} + * ignoring values. + * + * @return AnnotationMirror with the same class as {@code anno} iff c contains anno, according + * to areSameIgnoringValues; otherwise, {@code null} + */ + public static AnnotationMirror getSameIgnoringValues( + Collection<? extends AnnotationMirror> c, AnnotationMirror anno) { + for (AnnotationMirror an : c) { + if (AnnotationUtils.areSameIgnoringValues(an, anno)) { + return an; + } } - }; + return null; + } + + private static final Comparator<AnnotationMirror> ANNOTATION_ORDERING = + new Comparator<AnnotationMirror>() { + @Override + public int compare(AnnotationMirror a1, AnnotationMirror a2) { + // AnnotationMirror.toString() prints the elements of an annotation in the + // order in which they were written. So, use areSame to check for equality. + if (AnnotationUtils.areSame(a1, a2)) { + return 0; + } + + String n1 = a1.toString(); + String n2 = a2.toString(); + + // Because the AnnotationMirror.toString prints the annotation as it appears + // in source code, the order in which annotations of the same class are + // sorted may be confusing. For example, it might order + // @IntRange(from=1, to=MAX) before @IntRange(to=MAX,from=0). + return n1.compareTo(n2); + } + }; /** - * provide ordering for {@link AnnotationMirror} based on their fully - * qualified name. The ordering ignores annotation values when ordering. + * provide ordering for {@link AnnotationMirror} based on their fully qualified name. The + * ordering ignores annotation values when ordering. * - * The ordering is meant to be used as {@link TreeSet} or {@link TreeMap} - * ordering. A {@link Set} should not contain two annotations that only - * differ in values. + * <p>The ordering is meant to be used as {@link TreeSet} or {@link TreeMap} ordering. A {@link + * Set} should not contain two annotations that only differ in values. */ public static Comparator<AnnotationMirror> annotationOrdering() { return ANNOTATION_ORDERING; @@ -331,8 +401,8 @@ public class AnnotationUtils { /** * Create a map suitable for storing {@link AnnotationMirror} as keys. * - * It can store one instance of {@link AnnotationMirror} of a given - * declared type, regardless of the annotation element values. + * <p>It can store one instance of {@link AnnotationMirror} of a given declared type, regardless + * of the annotation element values. * * @param <V> the value of the map * @return a new map with {@link AnnotationMirror} as key @@ -344,8 +414,8 @@ public class AnnotationUtils { /** * Constructs a {@link Set} suitable for storing {@link AnnotationMirror}s. * - * It stores at most once instance of {@link AnnotationMirror} of a given - * type, regardless of the annotation element values. + * <p>It stores at most once instance of {@link AnnotationMirror} of a given type, regardless of + * the annotation element values. * * @return a new set to store {@link AnnotationMirror} as element */ @@ -358,62 +428,75 @@ public class AnnotationUtils { return anno.getAnnotationType().asElement().getAnnotation(Inherited.class) != null; } - // ********************************************************************** // Extractors for annotation values // ********************************************************************** /** - * Returns the values of an annotation's attributes, including defaults. - * The method with the same name in JavacElements cannot be used directly, - * because it includes a cast to Attribute.Compound, which doesn't hold - * for annotations generated by the Checker Framework. + * Returns the values of an annotation's attributes, including defaults. The method with the + * same name in JavacElements cannot be used directly, because it includes a cast to + * Attribute.Compound, which doesn't hold for annotations generated by the Checker Framework. * * @see AnnotationMirror#getElementValues() * @see JavacElements#getElementValuesWithDefaults(AnnotationMirror) - * - * @param ad annotation to examine + * @param ad annotation to examine * @return the values of the annotation's elements, including defaults */ public static Map<? extends ExecutableElement, ? extends AnnotationValue> - getElementValuesWithDefaults(AnnotationMirror ad) { - Map<ExecutableElement, AnnotationValue> valMap - = new HashMap<ExecutableElement, AnnotationValue>(); + getElementValuesWithDefaults(AnnotationMirror ad) { + Map<ExecutableElement, AnnotationValue> valMap = + new HashMap<ExecutableElement, AnnotationValue>(); if (ad.getElementValues() != null) { valMap.putAll(ad.getElementValues()); } for (ExecutableElement meth : - ElementFilter.methodsIn(ad.getAnnotationType().asElement().getEnclosedElements())) { + ElementFilter.methodsIn(ad.getAnnotationType().asElement().getEnclosedElements())) { AnnotationValue defaultValue = meth.getDefaultValue(); - if (defaultValue != null && !valMap.containsKey(meth)) + if (defaultValue != null && !valMap.containsKey(meth)) { valMap.put(meth, defaultValue); + } } return valMap; } /** - * Get the attribute with the name {@code name} of the annotation - * {@code anno}. The result is expected to have type {@code expectedType}. + * Verify whether the attribute with the name {@code name} exists in the annotation {@code + * anno}. * - * <p> - * <em>Note 1</em>: The method does not work well for attributes of an array - * type (as it would return a list of {@link AnnotationValue}s). Use - * {@code getElementValueArray} instead. + * @param anno the annotation to examine + * @param name the name of the attribute + * @return whether the attribute exists in anno + */ + public static <T> boolean hasElementValue(AnnotationMirror anno, CharSequence name) { + Map<? extends ExecutableElement, ? extends AnnotationValue> valmap = + anno.getElementValues(); + for (ExecutableElement elem : valmap.keySet()) { + if (elem.getSimpleName().contentEquals(name)) { + return true; + } + } + return false; + } + + /** + * Get the attribute with the name {@code name} of the annotation {@code anno}. The result is + * expected to have type {@code expectedType}. * - * <p> - * <em>Note 2</em>: The method does not work for attributes of an enum type, - * as the AnnotationValue is a VarSymbol and would be cast to the enum type, - * which doesn't work. Use {@code getElementValueEnum} instead. + * <p><em>Note 1</em>: The method does not work well for attributes of an array type (as it + * would return a list of {@link AnnotationValue}s). Use {@code getElementValueArray} instead. * + * <p><em>Note 2</em>: The method does not work for attributes of an enum type, as the + * AnnotationValue is a VarSymbol and would be cast to the enum type, which doesn't work. Use + * {@code getElementValueEnum} instead. * * @param anno the annotation to disassemble * @param name the name of the attribute to access * @param expectedType the expected type used to cast the return type - * @param useDefaults whether to apply default values to the attribute. + * @param useDefaults whether to apply default values to the attribute * @return the value of the attribute with the given name */ - public static <T> T getElementValue(AnnotationMirror anno, CharSequence name, - Class<T> expectedType, boolean useDefaults) { + public static <T> T getElementValue( + AnnotationMirror anno, CharSequence name, Class<T> expectedType, boolean useDefaults) { Map<? extends ExecutableElement, ? extends AnnotationValue> valmap; if (useDefaults) { valmap = getElementValuesWithDefaults(anno); @@ -430,34 +513,31 @@ public class AnnotationUtils { return null; // dead code } - /** - * Version that is suitable for Enum elements. - */ + /** Version that is suitable for Enum elements. */ public static <T extends Enum<T>> T getElementValueEnum( - AnnotationMirror anno, CharSequence name, Class<T> t, - boolean useDefaults) { + AnnotationMirror anno, CharSequence name, Class<T> t, boolean useDefaults) { VarSymbol vs = getElementValue(anno, name, VarSymbol.class, useDefaults); T value = Enum.valueOf(t, vs.getSimpleName().toString()); return value; } /** - * Get the attribute with the name {@code name} of the annotation - * {@code anno}, where the attribute has an array type. One element of the - * result is expected to have type {@code expectedType}. + * Get the attribute with the name {@code name} of the annotation {@code anno}, where the + * attribute has an array type. One element of the result is expected to have type {@code + * expectedType}. * - * Parameter useDefaults is used to determine whether default values - * should be used for annotation values. Finding defaults requires - * more computation, so should be false when no defaulting is needed. + * <p>Parameter useDefaults is used to determine whether default values should be used for + * annotation values. Finding defaults requires more computation, so should be false when no + * defaulting is needed. * * @param anno the annotation to disassemble * @param name the name of the attribute to access * @param expectedType the expected type used to cast the return type - * @param useDefaults whether to apply default values to the attribute. + * @param useDefaults whether to apply default values to the attribute * @return the value of the attribute with the given name */ - public static <T> List<T> getElementValueArray(AnnotationMirror anno, - CharSequence name, Class<T> expectedType, boolean useDefaults) { + public static <T> List<T> getElementValueArray( + AnnotationMirror anno, CharSequence name, Class<T> expectedType, boolean useDefaults) { @SuppressWarnings("unchecked") List<AnnotationValue> la = getElementValue(anno, name, List.class, useDefaults); List<T> result = new ArrayList<T>(la.size()); @@ -468,14 +548,13 @@ public class AnnotationUtils { } /** - * Get the attribute with the name {@code name} of the annotation - * {@code anno}, or the default value if no attribute is present explicitly, - * where the attribute has an array type and the elements are {@code Enum}s. - * One element of the result is expected to have type {@code expectedType}. + * Get the attribute with the name {@code name} of the annotation {@code anno}, or the default + * value if no attribute is present explicitly, where the attribute has an array type and the + * elements are {@code Enum}s. One element of the result is expected to have type {@code + * expectedType}. */ public static <T extends Enum<T>> List<T> getElementValueEnumArray( - AnnotationMirror anno, CharSequence name, Class<T> t, - boolean useDefaults) { + AnnotationMirror anno, CharSequence name, Class<T> t, boolean useDefaults) { @SuppressWarnings("unchecked") List<AnnotationValue> la = getElementValue(anno, name, List.class, useDefaults); List<T> result = new ArrayList<T>(la.size()); @@ -487,42 +566,58 @@ public class AnnotationUtils { } /** - * Get the Name of the class that is referenced by attribute 'name'. - * This is a convenience method for the most common use-case. - * Like getElementValue(anno, name, ClassType.class).getQualifiedName(), but - * this method ensures consistent use of the qualified name. + * Get the Name of the class that is referenced by attribute {@code name}. + * + * <p>This is a convenience method for the most common use-case. Like getElementValue(anno, + * name, ClassType.class).getQualifiedName(), but this method ensures consistent use of the + * qualified name. */ - public static Name getElementValueClassName(AnnotationMirror anno, CharSequence name, - boolean useDefaults) { + public static Name getElementValueClassName( + AnnotationMirror anno, CharSequence name, boolean useDefaults) { Type.ClassType ct = getElementValue(anno, name, Type.ClassType.class, useDefaults); // TODO: Is it a problem that this returns the type parameters too? Should I cut them off? return ct.asElement().getQualifiedName(); } + /** Get the list of Names of the classes that are referenced by attribute {@code name}. */ + public static List<Name> getElementValueClassNames( + AnnotationMirror anno, CharSequence name, boolean useDefaults) { + List<Type.ClassType> la = + getElementValueArray(anno, name, Type.ClassType.class, useDefaults); + List<Name> names = new ArrayList<>(); + for (Type.ClassType classType : la) { + names.add(classType.asElement().getQualifiedName()); + } + return names; + } + /** - * Get the Class that is referenced by attribute 'name'. - * This method uses Class.forName to load the class. It returns - * null if the class wasn't found. + * Get the Class that is referenced by attribute {@code name}. This method uses Class.forName to + * load the class. It returns null if the class wasn't found. */ - public static Class<?> getElementValueClass(AnnotationMirror anno, CharSequence name, - boolean useDefaults) { + public static Class<?> getElementValueClass( + AnnotationMirror anno, CharSequence name, boolean useDefaults) { Name cn = getElementValueClassName(anno, name, useDefaults); try { - Class<?> cls = Class.forName(cn.toString()); + ClassLoader classLoader = InternalUtils.getClassLoaderForClass(AnnotationUtils.class); + Class<?> cls = Class.forName(cn.toString(), true, classLoader); return cls; } catch (ClassNotFoundException e) { - ErrorReporter.errorAbort("Could not load class '" + cn + "' for field '" + name + - "' in annotation " + anno, e); + String msg = + String.format( + "Could not load class '%s' for field '%s' in annotation %s", + cn, name, anno); + ErrorReporter.errorAbort(msg, e); return null; // dead code } } /** - * See checkers.types.QualifierHierarchy#updateMappingToMutableSet(QualifierHierarchy, Map, Object, AnnotationMirror) - * (Not linked because it is in an independent project. + * See checkers.types.QualifierHierarchy#updateMappingToMutableSet(QualifierHierarchy, Map, + * Object, AnnotationMirror) (Not linked because it is in an independent project. */ - public static <T> void updateMappingToImmutableSet(Map<T, Set<AnnotationMirror>> map, - T key, Set<AnnotationMirror> newQual) { + public static <T> void updateMappingToImmutableSet( + Map<T, Set<AnnotationMirror>> map, T key, Set<AnnotationMirror> newQual) { Set<AnnotationMirror> result = AnnotationUtils.createAnnotationSet(); // TODO: if T is also an AnnotationMirror, should we use areSame? @@ -534,4 +629,22 @@ public class AnnotationUtils { } map.put(key, Collections.unmodifiableSet(result)); } + + /** + * Returns the annotations explicitly written on a constructor result. Callers should check that + * {@code constructorDeclaration} is in fact a declaration of a constructor. + * + * @param constructorDeclaration declaration tree of constructor + * @return set of annotations explicit on the resulting type of the constructor + */ + public static Set<AnnotationMirror> getExplicitAnnotationsOnConstructorResult( + MethodTree constructorDeclaration) { + Set<AnnotationMirror> annotationSet = AnnotationUtils.createAnnotationSet(); + ModifiersTree modifiersTree = constructorDeclaration.getModifiers(); + if (modifiersTree != null) { + List<? extends AnnotationTree> annotationTrees = modifiersTree.getAnnotations(); + annotationSet.addAll(InternalUtils.annotationsFromTypeAnnotationTrees(annotationTrees)); + } + return annotationSet; + } } |