blob: cadf8d5b14ac9ed673e2e0ea4a56f9caa2dd2ba3 [file] [log] [blame]
package annotations;
import annotations.el.AnnotationDef;
import annotations.field.AnnotationAFT;
import annotations.field.AnnotationFieldType;
import annotations.field.ArrayAFT;
import annotations.field.EnumAFT;
import annotations.field.ScalarAFT;
import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/*>>>
import org.checkerframework.checker.nullness.qual.*;
*/
/**
* This noninstantiable class provides useful static methods related to
* annotations, following the convention of {@link java.util.Collections}.
*/
public abstract class Annotations {
private Annotations() {}
public static Set<Annotation> noAnnotations;
public static Map<String, ? extends AnnotationFieldType> noFieldTypes;
public static Map<String, ? extends Object> noFieldValues;
public static Set<Annotation> typeQualifierMetaAnnotations;
public static EnumAFT aftRetentionPolicy;
public static AnnotationDef adRetention;
public static Annotation aRetentionClass;
public static Annotation aRetentionRuntime;
public static Annotation aRetentionSource;
public static Set<Annotation> asRetentionClass;
public static Set<Annotation> asRetentionRuntime;
public static Set<Annotation> asRetentionSource;
public static AnnotationDef adTarget;
public static Annotation aTargetTypeUse;
public static AnnotationDef adDocumented;
public static Annotation aDocumented;
public static AnnotationDef adNonNull;
public static Annotation aNonNull;
public static AnnotationDef adTypeQualifier;
public static Annotation aTypeQualifier;
/**
* Annotations that are meta-annotated with themselves. Due to a flaw
* in the the Scene Library, it is unable to read them from classfiles.
* An expedient workaround is to pre-define them, so they never need be
* read from a classfile.
*/
public static Set<AnnotationDef> standardDefs;
// the field types for an annotation with only one field, named "value".
static Map<String, ? extends AnnotationFieldType>
valueFieldTypeOnly(AnnotationFieldType aft) {
return Collections.singletonMap("value", aft);
}
// the field values for an annotation with only one field, named "value".
public static Map<String, ? extends Object> valueFieldOnly(Object valueValue) {
return Collections.singletonMap("value", valueValue);
}
// Create an annotation definition with only a value field.
public static AnnotationDef createValueAnnotationDef(String name, Set<Annotation> metaAnnotations, AnnotationFieldType aft) {
return new AnnotationDef(name, metaAnnotations, valueFieldTypeOnly(aft));
}
// Create an annotation with only a value field.
public static Annotation createValueAnnotation(AnnotationDef ad, Object value) {
return new Annotation(ad, valueFieldOnly(value));
}
public static Annotation getRetentionPolicyMetaAnnotation(RetentionPolicy rp) {
switch (rp) {
case CLASS: return aRetentionClass;
case RUNTIME: return aRetentionRuntime;
case SOURCE: return aRetentionSource;
default:
throw new Error("This can't happen");
}
}
public static Set<Annotation> getRetentionPolicyMetaAnnotationSet(RetentionPolicy rp) {
switch (rp) {
case CLASS: return asRetentionClass;
case RUNTIME: return asRetentionRuntime;
case SOURCE: return asRetentionSource;
default:
throw new Error("This can't happen");
}
}
static {
noAnnotations = Collections.<Annotation> emptySet();
noFieldTypes = Collections.<String, AnnotationFieldType> emptyMap();
noFieldValues = Collections.<String, Object> emptyMap();
// This is slightly complicated because Retention's definition is
// meta-annotated by itself, we have to define the annotation
// before we can create the annotation on it.
aftRetentionPolicy = new EnumAFT("java.lang.annotation.RetentionPolicy");
adRetention = new AnnotationDef("java.lang.annotation.Retention");
adRetention.setFieldTypes(valueFieldTypeOnly(aftRetentionPolicy));
aRetentionRuntime = createValueAnnotation(adRetention, "RUNTIME");
adRetention.tlAnnotationsHere.add(aRetentionRuntime);
aRetentionClass = createValueAnnotation(adRetention, "CLASS");
aRetentionSource = createValueAnnotation(adRetention, "SOURCE");
asRetentionClass = Collections.singleton(aRetentionClass);
asRetentionRuntime = Collections.singleton(aRetentionRuntime);
asRetentionSource = Collections.singleton(aRetentionSource);
// Documented's definition is also self-meta-annotated.
adDocumented = new AnnotationDef("java.lang.annotation.Documented");
adDocumented.setFieldTypes(noFieldTypes);
aDocumented = new Annotation(adDocumented, noFieldValues);
adDocumented.tlAnnotationsHere.add(aDocumented);
adTarget = createValueAnnotationDef("java.lang.annotation.Target",
asRetentionRuntime,
new ArrayAFT(new EnumAFT("java.lang.annotation.ElementType")));
aTargetTypeUse = createValueAnnotation(adTarget,
// Problem: ElementType.TYPE_USE is defined only in JDK 7.
// need to decide what the canonical format for these strings is.
// Collections.singletonList("java.lang.annotation.ElementType.TYPE_USE")
// This is the way that naively reading them from classfile gives.
Collections.singletonList("TYPE_USE")
);
typeQualifierMetaAnnotations = new HashSet<Annotation>();
typeQualifierMetaAnnotations.add(aRetentionRuntime);
typeQualifierMetaAnnotations.add(aTargetTypeUse);
adNonNull = new AnnotationDef("org.checkerframework.checker.nullness.qual.NonNull",
typeQualifierMetaAnnotations,
noFieldTypes);
aNonNull = new Annotation(adNonNull, noFieldValues);
adTypeQualifier = new AnnotationDef("org.checkerframework.framework.qual.TypeQualifier",
asRetentionRuntime,
noFieldTypes);
aTypeQualifier = new Annotation(adTypeQualifier, noFieldValues);
standardDefs = new LinkedHashSet<AnnotationDef>();
standardDefs.add(adTarget);
standardDefs.add(adDocumented);
standardDefs.add(adRetention);
// Because annotations can be read from classfiles, it isn't really
// necessary to add any more here.
}
/**
* Converts the given scalar annotation field value to one appropriate for
* passing to an {@link AnnotationBuilder} created by <code>af</code>.
* Conversion is only necessary if <code>x</code> is a subannotation, in
* which case we rebuild it with <code>af</code> since
* {@link AnnotationBuilder#addScalarField addScalarField} of an
* {@link AnnotationBuilder} created by <code>af</code> only accepts
* subannotations built by <code>af</code>.
*/
private static Object convertAFV(ScalarAFT aft, Object x) {
if (aft instanceof AnnotationAFT) {
return rebuild((Annotation) x);
} else {
return x;
}
}
/**
* Rebuilds the annotation <code>a</code> using the factory
* <code>af</code> by iterating through its fields according to its
* definition and getting the values with {@link Annotation#getFieldValue}.
* Returns null if the factory is not interested in <code>a</code>.
*/
public static final Annotation rebuild(Annotation a) {
AnnotationBuilder ab = AnnotationFactory.saf.beginAnnotation(a.def());
if (ab != null) {
for (Map. Entry<String, AnnotationFieldType> fieldDef
: a.def().fieldTypes.entrySet()) {
String fieldName = fieldDef.getKey();
AnnotationFieldType fieldType =
fieldDef.getValue();
Object fieldValue =
a.getFieldValue(fieldName);
Object nnFieldValue;
if (fieldValue != null) {
nnFieldValue = fieldValue;
} else throw new IllegalArgumentException(
"annotation has no field value");
if (fieldType instanceof ArrayAFT) {
ArrayAFT aFieldType =
(ArrayAFT) fieldType;
ArrayBuilder arrb =
ab.beginArrayField(fieldName, aFieldType);
List<? extends Object> l =
(List<? extends Object>) fieldValue;
ScalarAFT nnElementType;
if (aFieldType.elementType != null) {
nnElementType = aFieldType.elementType;
} else {
throw new IllegalArgumentException(
"annotation field type is missing element type");
}
for (Object o : l) {
arrb.appendElement(convertAFV(
nnElementType, o));
}
arrb.finish();
} else {
ScalarAFT sFieldType =
(ScalarAFT) fieldType;
ab.addScalarField(fieldName, sFieldType,
convertAFV(sFieldType, fieldValue));
}
}
return ab.finish();
} else {
return null;
}
}
}