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

org.checkerframework.checker.units.UnitsRelationsTools 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.checker.units;

import java.lang.annotation.Annotation;
import java.util.Map;
import java.util.Set;
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.util.Elements;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.checker.signature.qual.FullyQualifiedName;
import org.checkerframework.checker.units.qual.Prefix;
import org.checkerframework.checker.units.qual.UnknownUnits;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.javacutil.AnnotationBuilder;
import org.checkerframework.javacutil.AnnotationUtils;

/**
 * A helper class for UnitsRelations, providing numerous methods which help process Annotations and
 * Annotated Types representing various units.
 */
public class UnitsRelationsTools {

  /**
   * Creates an AnnotationMirror representing a unit defined by annoClass, with the specific Prefix
   * p.
   *
   * @param env the Checker Processing Environment, provided as a parameter in init() of a
   *     UnitsRelations implementation
   * @param annoClass the fully-qualified name of an Annotation representing a Unit (eg m.class for
   *     meters)
   * @param p a Prefix value
   * @return an AnnotationMirror of the Unit with the Prefix p, or null if it cannot be constructed
   */
  public static @Nullable AnnotationMirror buildAnnoMirrorWithSpecificPrefix(
      final ProcessingEnvironment env,
      final @FullyQualifiedName CharSequence annoClass,
      final Prefix p) {
    AnnotationBuilder builder = new AnnotationBuilder(env, annoClass);
    builder.setValue("value", p);
    return builder.build();
  }

  /**
   * Creates an AnnotationMirror representing a unit defined by annoClass, with no prefix.
   *
   * @param env checker Processing Environment, provided as a parameter in init() of a
   *     UnitsRelations implementation
   * @param annoClass the getElementValueClassname of an Annotation representing a Unit (eg m.class
   *     for meters)
   * @return an AnnotationMirror of the Unit with no prefix, or null if it cannot be constructed
   */
  public static @Nullable AnnotationMirror buildAnnoMirrorWithNoPrefix(
      final ProcessingEnvironment env, final @FullyQualifiedName CharSequence annoClass) {
    return AnnotationBuilder.fromName(env.getElementUtils(), annoClass);
  }

  /**
   * Retrieves the SI Prefix of an Annotated Type.
   *
   * @param annoType an AnnotatedTypeMirror representing a Units Annotated Type
   * @return a Prefix value (including Prefix.one), or null if it has none
   */
  public static @Nullable Prefix getPrefix(final AnnotatedTypeMirror annoType) {
    Prefix result = null;

    // go through each Annotation of an Annotated Type, find the prefix and return it
    for (AnnotationMirror mirror : annoType.getAnnotations()) {
      // try to get a prefix
      result = getPrefix(mirror);
      // if it is not null, then return the retrieved prefix immediately
      if (result != null) {
        return result;
      }
    }

    // if it can't find any prefix at all, then return null
    return result;
  }

  /**
   * Retrieves the SI Prefix of an Annotation.
   *
   * @param unitsAnnotation an AnnotationMirror representing a Units Annotation
   * @return a Prefix value (including Prefix.one), or null if it has none
   */
  public static @Nullable Prefix getPrefix(final AnnotationMirror unitsAnnotation) {
    AnnotationValue annotationValue = getAnnotationMirrorPrefix(unitsAnnotation);

    // if this Annotation has no prefix, return null
    if (hasNoPrefix(annotationValue)) {
      return null;
    }

    // if the Annotation has a value, then detect and match the string name of the prefix, and
    // return the matching Prefix
    String prefixString = annotationValue.getValue().toString();
    for (Prefix prefix : Prefix.values()) {
      if (prefixString.equals(prefix.toString())) {
        return prefix;
      }
    }

    // if none of the strings match, then return null
    return null;
  }

  /**
   * Checks to see if an Annotated Type has no prefix.
   *
   * @param annoType an AnnotatedTypeMirror representing a Units Annotated Type
   * @return true if it has no prefix, false otherwise
   */
  public static boolean hasNoPrefix(final AnnotatedTypeMirror annoType) {
    for (AnnotationMirror mirror : annoType.getAnnotations()) {
      // if any Annotation has a prefix, return false
      if (!hasNoPrefix(mirror)) {
        return false;
      }
    }

    return true;
  }

  /**
   * Checks to see if an Annotation has no prefix (ie, no value element).
   *
   * @param unitsAnnotation an AnnotationMirror representing a Units Annotation
   * @return true if it has no prefix, false otherwise
   */
  public static boolean hasNoPrefix(final AnnotationMirror unitsAnnotation) {
    AnnotationValue annotationValue = getAnnotationMirrorPrefix(unitsAnnotation);
    return hasNoPrefix(annotationValue);
  }

  private static boolean hasNoPrefix(final AnnotationValue annotationValue) {
    // Annotation has no element value (ie no SI prefix)
    if (annotationValue == null) {
      return true;
    } else {
      return false;
    }
  }

  /**
   * Given an Annotation, returns the prefix (eg kilo) as an AnnotationValue if there is any,
   * otherwise returns null.
   */
  private static @Nullable AnnotationValue getAnnotationMirrorPrefix(
      final AnnotationMirror unitsAnnotation) {
    Map elementValues =
        unitsAnnotation.getElementValues();

    for (Map.Entry entry :
        elementValues.entrySet()) {
      if (entry.getKey().getSimpleName().contentEquals("value")) {
        return entry.getValue();
      }
    }

    return null;
  }

  /**
   * Removes the prefix value from an Annotation, by constructing and returning a copy of its base
   * SI unit's Annotation.
   *
   * @param elements the Element Utilities from a checker's processing environment, typically
   *     obtained by calling env.getElementUtils() in init() of a Units Relations implementation
   * @param unitsAnnotation an AnnotationMirror representing a Units Annotation
   * @return the base SI Unit's AnnotationMirror, or null if the base SI Unit cannot be constructed
   */
  public static @Nullable AnnotationMirror removePrefix(
      final Elements elements, final AnnotationMirror unitsAnnotation) {
    if (hasNoPrefix(unitsAnnotation)) {
      // Optimization, though the else case would also work.
      return unitsAnnotation;
    } else {
      String unitsAnnoName = AnnotationUtils.annotationName(unitsAnnotation);
      // In the Units Checker, the only annotation value is the prefix value.  Therefore,
      // fromName (which creates an annotation with no values) is acceptable.
      // TODO: refine sensitivity of removal for extension units, in case extension
      // Annotations have more than just Prefix in its values.
      return AnnotationBuilder.fromName(elements, unitsAnnoName);
    }
  }

  /**
   * Removes the Prefix value from an Annotated Type, by constructing and returning a copy of the
   * Annotated Type without the prefix.
   *
   * @param elements the Element Utilities from a checker's processing environment, typically
   *     obtained by calling env.getElementUtils() in init() of a Units Relations implementation
   * @param annoType an AnnotatedTypeMirror representing a Units Annotated Type
   * @return a copy of the Annotated Type without the prefix
   */
  public static AnnotatedTypeMirror removePrefix(
      final Elements elements, final AnnotatedTypeMirror annoType) {
    // deep copy the Annotated Type Mirror without any of the Annotations
    AnnotatedTypeMirror result = annoType.deepCopy(false);

    // get all of the original Annotations in the Annotated Type
    Set annos = annoType.getAnnotations();

    // loop through all the Annotations to see if they use Prefix.one, remove Prefix.one if it does
    for (AnnotationMirror anno : annos) {
      // try to clean the Annotation Mirror of the Prefix
      AnnotationMirror cleanedMirror = removePrefix(elements, anno);
      // if successful, add the cleaned annotation to the deep copy
      if (cleanedMirror != null) {
        result.addAnnotation(cleanedMirror);
      }
      // if unsuccessful, add the original annotation
      else {
        result.addAnnotation(anno);
      }
    }

    return result;
  }

  /**
   * Checks to see if a particular Annotated Type has no units, such as scalar constants in
   * calculations.
   *
   * 

Any number that isn't assigned a unit will automatically get the Annotation UnknownUnits. * eg: int x = 5; // x has @UnknownUnits * * @param annoType an AnnotatedTypeMirror representing a Units Annotated Type * @return true if the Type has no units, false otherwise */ public static boolean hasNoUnits(final AnnotatedTypeMirror annoType) { return (annoType.getAnnotation(UnknownUnits.class) != null); } /** * Checks to see if a particular Annotated Type has a specific unit (represented by its * Annotation). * * @param annoType an AnnotatedTypeMirror representing a Units Annotated Type * @param unitsAnnotation an AnnotationMirror representing a Units Annotation of a specific unit * @return true if the Type has the specific unit, false otherwise */ public static boolean hasSpecificUnit( final AnnotatedTypeMirror annoType, final AnnotationMirror unitsAnnotation) { return AnnotationUtils.containsSame(annoType.getAnnotations(), unitsAnnotation); } /** * Checks to see if a particular Annotated Type has a particular base unit (represented by its * Annotation). * * @param annoType an AnnotatedTypeMirror representing a Units Annotated Type * @param unitsAnnotation an AnnotationMirror representing a Units Annotation of the base unit * @return true if the Type has the specific unit, false otherwise */ public static boolean hasSpecificUnitIgnoringPrefix( final AnnotatedTypeMirror annoType, final AnnotationMirror unitsAnnotation) { return AnnotationUtils.containsSameByName(annoType.getAnnotations(), unitsAnnotation); } /** * Creates an AnnotationMirror representing a unit defined by annoClass, with the specific Prefix * p. * *

This interface is intended only for subclasses of UnitsRelations; other clients should use * {@link #buildAnnoMirrorWithSpecificPrefix(ProcessingEnvironment, CharSequence, Prefix)} * * @param env the Checker Processing Environment, provided as a parameter in init() of a * UnitsRelations implementation * @param annoClass the Class of an Annotation representing a Unit (eg m.class for meters) * @param p a Prefix value * @return an AnnotationMirror of the Unit with the Prefix p, or null if it cannot be constructed */ public static @Nullable AnnotationMirror buildAnnoMirrorWithSpecificPrefix( final ProcessingEnvironment env, final Class annoClass, final Prefix p) { AnnotationBuilder builder = new AnnotationBuilder(env, annoClass); builder.setValue("value", p); return builder.build(); } /** * Creates an AnnotationMirror representing a unit defined by annoClass, with the default Prefix * of {@code Prefix.one}. * *

This interface is intended only for subclasses of UnitsRelations; other clients should not * use it. * * @param env the Checker Processing Environment, provided as a parameter in init() of a * UnitsRelations implementation * @param annoClass the Class of an Annotation representing a Unit (eg m.class for meters) * @return an AnnotationMirror of the Unit with Prefix.one, or null if it cannot be constructed */ public static @Nullable AnnotationMirror buildAnnoMirrorWithDefaultPrefix( final ProcessingEnvironment env, final Class annoClass) { return buildAnnoMirrorWithSpecificPrefix(env, annoClass, Prefix.one); } /** * Creates an AnnotationMirror representing a unit defined by annoClass, with no prefix. * *

This interface is intended only for subclasses of UnitsRelations; other clients should use * {@link #buildAnnoMirrorWithNoPrefix(ProcessingEnvironment, CharSequence)}. * * @param env checker Processing Environment, provided as a parameter in init() of a * UnitsRelations implementation * @param annoClass the Class of an Annotation representing a Unit (eg m.class for meters) * @return an AnnotationMirror of the Unit with no prefix, or null if it cannot be constructed */ static @Nullable AnnotationMirror buildAnnoMirrorWithNoPrefix( final ProcessingEnvironment env, final Class annoClass) { return AnnotationBuilder.fromClass(env.getElementUtils(), annoClass); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy