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 noAnnotations; public static Map noFieldTypes; public static Map noFieldValues; public static Set typeQualifierMetaAnnotations; public static EnumAFT aftRetentionPolicy; public static AnnotationDef adRetention; public static Annotation aRetentionClass; public static Annotation aRetentionRuntime; public static Annotation aRetentionSource; public static Set asRetentionClass; public static Set asRetentionRuntime; public static Set 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 standardDefs; // the field types for an annotation with only one field, named "value". static Map valueFieldTypeOnly(AnnotationFieldType aft) { return Collections.singletonMap("value", aft); } // the field values for an annotation with only one field, named "value". public static Map valueFieldOnly(Object valueValue) { return Collections.singletonMap("value", valueValue); } // Create an annotation definition with only a value field. public static AnnotationDef createValueAnnotationDef(String name, Set 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 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. emptySet(); noFieldTypes = Collections. emptyMap(); noFieldValues = Collections. 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(); 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(); 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 af. * Conversion is only necessary if x is a subannotation, in * which case we rebuild it with af since * {@link AnnotationBuilder#addScalarField addScalarField} of an * {@link AnnotationBuilder} created by af only accepts * subannotations built by af. */ private static Object convertAFV(ScalarAFT aft, Object x) { if (aft instanceof AnnotationAFT) { return rebuild((Annotation) x); } else { return x; } } /** * Rebuilds the annotation a using the factory * af 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 a. */ public static final Annotation rebuild(Annotation a) { AnnotationBuilder ab = AnnotationFactory.saf.beginAnnotation(a.def()); if (ab != null) { for (Map. Entry 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 l = (List) 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; } } }