aboutsummaryrefslogtreecommitdiffhomepage
path: root/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/AnnotationUtils.java
diff options
context:
space:
mode:
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.java501
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;
+ }
}