package org.checkerframework.common.wholeprograminference;
import annotations.Annotation;
import annotations.el.AnnotationDef;
import annotations.field.AnnotationFieldType;
import annotations.field.ArrayAFT;
import annotations.field.BasicAFT;
import annotations.field.ScalarAFT;
import com.sun.tools.javac.code.Attribute.Array;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.code.Type.ArrayType;
import java.util.ArrayList;
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.TypeMirror;
import org.checkerframework.framework.util.AnnotationBuilder;
import org.checkerframework.javacutil.AnnotationUtils;
import org.checkerframework.javacutil.ErrorReporter;
/**
* This class has auxiliary methods that performs conversion between {@link annotations.Annotation}
* and {@link javax.lang.model.element.AnnotationMirror}.
*
* @author pbsf
*/
public class AnnotationConverter {
/**
* Converts an {@link javax.lang.model.element.AnnotationMirror} into an {@link
* annotations.Annotation}.
*/
protected static Annotation annotationMirrorToAnnotation(AnnotationMirror am) {
AnnotationDef def = new AnnotationDef(AnnotationUtils.annotationName(am));
Map<String, AnnotationFieldType> fieldTypes = new HashMap<>();
// Handling cases where there are fields in annotations.
for (ExecutableElement ee : am.getElementValues().keySet()) {
AnnotationFieldType aft =
getAnnotationFieldType(ee, am.getElementValues().get(ee).getValue());
if (aft == null) return null;
// Here we just add the type of the field into fieldTypes.
fieldTypes.put(ee.getSimpleName().toString(), aft);
}
def.setFieldTypes(fieldTypes);
// Now, we handle the values of those types below
Map<? extends ExecutableElement, ? extends AnnotationValue> values = am.getElementValues();
Map<String, Object> newValues = new HashMap<>();
for (ExecutableElement ee : values.keySet()) {
Object value = values.get(ee).getValue();
if (value instanceof List) {
@SuppressWarnings("unchecked")
List<Object> valueList = (List<Object>) value;
List<Object> newList = new ArrayList<>();
// If we have a List here, then it is a List of AnnotatedValue.
// Converting each AnnotatedValue to its respective Java type:
for (Object o : valueList) {
newList.add(((AnnotationValue) o).getValue());
}
value = newList;
}
newValues.put(ee.getSimpleName().toString(), value);
}
Annotation out = new Annotation(def, newValues);
return out;
}
/**
* Converts an {@link annotations.Annotation} into an {@link
* javax.lang.model.element.AnnotationMirror}.
*/
protected static AnnotationMirror annotationToAnnotationMirror(
Annotation anno, ProcessingEnvironment processingEnv) {
final AnnotationBuilder builder = new AnnotationBuilder(processingEnv, anno.def().name);
for (String fieldKey : anno.fieldValues.keySet()) {
addFieldToAnnotationBuilder(fieldKey, anno.fieldValues.get(fieldKey), builder);
}
return builder.build();
}
/** Returns an AnnotationFieldType given an ExecutableElement or value. */
protected static AnnotationFieldType getAnnotationFieldType(
ExecutableElement ee, Object value) {
if (value instanceof List<?>) {
AnnotationValue defaultValue = ee.getDefaultValue();
if (defaultValue == null || ((ArrayType) ((Array) defaultValue).type) == null) {
List<?> listV = (List<?>) value;
if (!listV.isEmpty()) {
ScalarAFT scalarAFT =
(ScalarAFT)
getAnnotationFieldType(
ee, ((AnnotationValue) listV.get(0)).getValue());
if (scalarAFT != null) {
return new ArrayAFT(scalarAFT);
}
}
return null;
}
Type elemType = ((ArrayType) ((Array) defaultValue).type).elemtype;
try {
return new ArrayAFT(BasicAFT.forType(Class.forName(elemType.toString())));
} catch (ClassNotFoundException e) {
ErrorReporter.errorAbort(e.getMessage());
}
} else if (value instanceof Boolean) {
return BasicAFT.forType(boolean.class);
} else if (value instanceof Character) {
return BasicAFT.forType(char.class);
} else if (value instanceof Double) {
return BasicAFT.forType(double.class);
} else if (value instanceof Float) {
return BasicAFT.forType(float.class);
} else if (value instanceof Integer) {
return BasicAFT.forType(int.class);
} else if (value instanceof Long) {
return BasicAFT.forType(long.class);
} else if (value instanceof Short) {
return BasicAFT.forType(short.class);
} else if (value instanceof String) {
return BasicAFT.forType(String.class);
}
return null;
}
/**
* 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<Object>) obj);
} else if (obj instanceof String) {
builder.setValue(fieldKey, (String) obj);
} else if (obj instanceof Integer) {
builder.setValue(fieldKey, (Integer) obj);
} else if (obj instanceof Float) {
builder.setValue(fieldKey, (Float) obj);
} else if (obj instanceof Long) {
builder.setValue(fieldKey, (Long) obj);
} else if (obj instanceof Boolean) {
builder.setValue(fieldKey, (Boolean) obj);
} else if (obj instanceof Character) {
builder.setValue(fieldKey, (Character) obj);
} else if (obj instanceof Class<?>) {
builder.setValue(fieldKey, (Class<?>) obj);
} else if (obj instanceof Double) {
builder.setValue(fieldKey, (Double) obj);
} else if (obj instanceof Float) {
builder.setValue(fieldKey, (Float) obj);
} else if (obj instanceof Enum<?>) {
builder.setValue(fieldKey, (Enum<?>) obj);
} else if (obj instanceof Enum<?>[]) {
builder.setValue(fieldKey, (Enum<?>[]) obj);
} else if (obj instanceof AnnotationMirror) {
builder.setValue(fieldKey, (AnnotationMirror) obj);
} else if (obj instanceof Object[]) {
builder.setValue(fieldKey, (Object[]) obj);
} else if (obj instanceof TypeMirror) {
builder.setValue(fieldKey, (TypeMirror) obj);
} else if (obj instanceof Short) {
builder.setValue(fieldKey, (Short) obj);
} else if (obj instanceof VariableElement) {
builder.setValue(fieldKey, (VariableElement) obj);
} else if (obj instanceof VariableElement[]) {
builder.setValue(fieldKey, (VariableElement[]) obj);
} else {
ErrorReporter.errorAbort("Unrecognized type: " + obj.getClass());
}
}
}