org.checkerframework.common.wholeprograminference.AnnotationConverter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of checker Show documentation
Show all versions of checker Show documentation
The Checker Framework enhances Java's type system to
make it more powerful and useful. This lets software developers
detect and prevent errors in their Java programs.
The Checker Framework includes compiler plug-ins ("checkers")
that find bugs or verify their absence. It also permits you to
write your own compiler plug-ins.
package org.checkerframework.common.wholeprograminference;
import com.sun.tools.javac.code.Type.ArrayType;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.javacutil.AnnotationBuilder;
import org.checkerframework.javacutil.AnnotationUtils;
import org.checkerframework.javacutil.BugInCF;
import org.checkerframework.javacutil.TypesUtils;
import org.plumelib.reflection.Signatures;
import org.plumelib.util.CollectionsPlume;
import scenelib.annotations.Annotation;
import scenelib.annotations.el.AnnotationDef;
import scenelib.annotations.field.AnnotationFieldType;
import scenelib.annotations.field.ArrayAFT;
import scenelib.annotations.field.BasicAFT;
import scenelib.annotations.field.ClassTokenAFT;
import scenelib.annotations.field.EnumAFT;
import scenelib.annotations.field.ScalarAFT;
/**
* This class contains static methods that convert between {@link scenelib.annotations.Annotation}
* and {@link javax.lang.model.element.AnnotationMirror}.
*/
public class AnnotationConverter {
/**
* Converts an {@link javax.lang.model.element.AnnotationMirror} into an {@link
* scenelib.annotations.Annotation}.
*
* @param am the AnnotationMirror
* @return the Annotation
*/
public static Annotation annotationMirrorToAnnotation(AnnotationMirror am) {
@SuppressWarnings("signature:argument") // TODO: bug for inner classes
AnnotationDef def =
new AnnotationDef(
AnnotationUtils.annotationName(am),
String.format(
"annotationMirrorToAnnotation %s [%s] keyset=%s",
am, am.getClass(), am.getElementValues().keySet()));
Map fieldTypes = new HashMap<>(am.getElementValues().size());
// Handling cases where there are fields in annotations.
for (ExecutableElement ee : am.getElementValues().keySet()) {
AnnotationFieldType aft = getAnnotationFieldType(ee);
fieldTypes.put(ee.getSimpleName().toString(), aft);
}
def.setFieldTypes(fieldTypes);
// Now, we handle the values of those types below
Map values = am.getElementValues();
Map newValues = new HashMap<>(values.size());
for (ExecutableElement ee : values.keySet()) {
Object value = values.get(ee).getValue();
if (value instanceof List) {
// If we have a List here, then it is a List of AnnotationValue.
// Convert each AnnotationValue to its respective Java type.
@SuppressWarnings("unchecked")
List valueList = (List) value;
value = CollectionsPlume.mapList(AnnotationValue::getValue, valueList);
} else if (value instanceof TypeMirror) {
try {
value = Class.forName(TypesUtils.binaryName((TypeMirror) value));
} catch (ClassNotFoundException e) {
throw new BugInCF(e, "value = %s [%s]", value, value.getClass());
}
}
newValues.put(ee.getSimpleName().toString(), value);
}
Annotation out = new Annotation(def, newValues);
return out;
}
/**
* Converts an {@link scenelib.annotations.Annotation} into an {@link
* javax.lang.model.element.AnnotationMirror}.
*
* @param anno the Annotation
* @param processingEnv the ProcessingEnvironment
* @return the AnnotationMirror
*/
protected static AnnotationMirror annotationToAnnotationMirror(
Annotation anno, ProcessingEnvironment processingEnv) {
final AnnotationBuilder builder =
new AnnotationBuilder(
processingEnv, Signatures.binaryNameToFullyQualified(anno.def().name));
for (String fieldKey : anno.fieldValues.keySet()) {
addFieldToAnnotationBuilder(fieldKey, anno.fieldValues.get(fieldKey), builder);
}
return builder.build();
}
/**
* Returns the type of an element (that is, a field) of an annotation.
*
* @param ee an element (that is, a field) of an annotation
* @return the type of the given annotation field
*/
protected static @Nullable AnnotationFieldType getAnnotationFieldType(ExecutableElement ee) {
return typeMirrorToAnnotationFieldType(ee.getReturnType());
}
/**
* Converts a TypeMirror to an AnnotationFieldType.
*
* @param tm a type for an annotation element/field: primitive, String, class, enum constant, or
* array thereof
* @return an AnnotationFieldType corresponding to the argument
*/
protected static AnnotationFieldType typeMirrorToAnnotationFieldType(TypeMirror tm) {
switch (tm.getKind()) {
case BOOLEAN:
return BasicAFT.forType(boolean.class);
// Primitves
case BYTE:
return BasicAFT.forType(byte.class);
case CHAR:
return BasicAFT.forType(char.class);
case DOUBLE:
return BasicAFT.forType(double.class);
case FLOAT:
return BasicAFT.forType(float.class);
case INT:
return BasicAFT.forType(int.class);
case LONG:
return BasicAFT.forType(long.class);
case SHORT:
return BasicAFT.forType(short.class);
case ARRAY:
TypeMirror componentType = ((ArrayType) tm).getComponentType();
AnnotationFieldType componentAFT = typeMirrorToAnnotationFieldType(componentType);
return new ArrayAFT((ScalarAFT) componentAFT);
case DECLARED:
String className = TypesUtils.getQualifiedName((DeclaredType) tm);
if (className.equals("java.lang.String")) {
return BasicAFT.forType(String.class);
} else if (className.equals("java.lang.Class")) {
return ClassTokenAFT.ctaft;
} else {
// This must be an enum constant.
return new EnumAFT(className);
}
default:
throw new BugInCF(
"typeMirrorToAnnotationFieldType: unexpected argument %s [%s %s]",
tm, tm.getKind(), tm.getClass());
}
}
/**
* Adds a field to an AnnotationBuilder.
*
* @param fieldKey is the name of the field
* @param obj is the value of the field
* @param builder is the AnnotationBuilder
*/
@SuppressWarnings("unchecked") // This is actually checked in the first instanceOf call below.
protected static void addFieldToAnnotationBuilder(
String fieldKey, Object obj, AnnotationBuilder builder) {
if (obj instanceof List) {
builder.setValue(fieldKey, (List