All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.checkerframework.common.wholeprograminference.AnnotationConverter Maven / Gradle / Ivy

Go to download

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.

There is a newer version: 3.44.0
Show newest version
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) 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 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 {
      throw new BugInCF("Unrecognized type: " + obj.getClass());
    }
  }
}