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

org.checkerframework.common.value.ValueCheckerUtils 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.43.0
Show newest version
package org.checkerframework.common.value;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import org.checkerframework.common.value.qual.ArrayLen;
import org.checkerframework.common.value.qual.BoolVal;
import org.checkerframework.common.value.qual.BottomVal;
import org.checkerframework.common.value.qual.DoubleVal;
import org.checkerframework.common.value.qual.IntRange;
import org.checkerframework.common.value.qual.IntVal;
import org.checkerframework.common.value.qual.StringVal;
import org.checkerframework.common.value.qual.UnknownVal;
import org.checkerframework.common.value.util.NumberUtils;
import org.checkerframework.common.value.util.Range;
import org.checkerframework.javacutil.AnnotationUtils;
import org.checkerframework.javacutil.TypesUtils;

public class ValueCheckerUtils {
    public static Class getClassFromType(TypeMirror type) {

        switch (type.getKind()) {
            case INT:
                return int.class;
            case LONG:
                return long.class;
            case SHORT:
                return short.class;
            case BYTE:
                return byte.class;
            case CHAR:
                return char.class;
            case DOUBLE:
                return double.class;
            case FLOAT:
                return float.class;
            case BOOLEAN:
                return boolean.class;
            case ARRAY:
                return getArrayClassObject(((ArrayType) type).getComponentType());
            case DECLARED:
                String stringType = TypesUtils.getQualifiedName((DeclaredType) type).toString();
                if (stringType.equals("")) {
                    return Object.class;
                }

                try {
                    return Class.forName(stringType);
                } catch (ClassNotFoundException | UnsupportedClassVersionError e) {
                    return Object.class;
                }

            default:
                return Object.class;
        }
    }

    public static Class getArrayClassObject(TypeMirror componentType) {
        switch (componentType.getKind()) {
            case INT:
                return int[].class;
            case LONG:
                return long[].class;
            case SHORT:
                return short[].class;
            case BYTE:
                return byte[].class;
            case CHAR:
                return char[].class;
            case DOUBLE:
                return double[].class;
            case FLOAT:
                return float[].class;
            case BOOLEAN:
                return boolean[].class;
            default:
                return Object[].class;
        }
    }

    /**
     * Get a list of values of annotation, and then cast them to a given type
     *
     * @param anno the annotation that contains values
     * @param castTo the type that is casted to
     * @return a list of values after the casting
     */
    public static List getValuesCastedToType(AnnotationMirror anno, TypeMirror castTo) {
        Class castType = ValueCheckerUtils.getClassFromType(castTo);
        List values = null;

        if (AnnotationUtils.areSameByClass(anno, DoubleVal.class)) {
            values = convertDoubleVal(anno, castType, castTo);
        } else if (AnnotationUtils.areSameByClass(anno, IntVal.class)) {
            List longs = ValueAnnotatedTypeFactory.getIntValues(anno);
            values = convertIntVal(longs, castType, castTo);
        } else if (AnnotationUtils.areSameByClass(anno, IntRange.class)) {
            Range range = ValueAnnotatedTypeFactory.getRange(anno);
            List longs = getValuesFromRange(range, Long.class);
            values = convertIntVal(longs, castType, castTo);
        } else if (AnnotationUtils.areSameByClass(anno, StringVal.class)) {
            values = convertStringVal(anno, castType);
        } else if (AnnotationUtils.areSameByClass(anno, BoolVal.class)) {
            values = convertBoolVal(anno, castType);
        } else if (AnnotationUtils.areSameByClass(anno, BottomVal.class)) {
            values = new ArrayList<>();
        } else if (AnnotationUtils.areSameByClass(anno, UnknownVal.class)) {
            values = null;
        } else if (AnnotationUtils.areSameByClass(anno, ArrayLen.class)) {
            values = new ArrayList<>();
        }
        return values;
    }

    /** Get the minimum and maximum of a list and return a range bounded by them. */
    public static Range getRangeFromValues(List values) {
        if (values == null) {
            return null;
        } else if (values.isEmpty()) {
            return Range.NOTHING;
        }
        // The number elements in the values list should not exceed MAX_VALUES (10).
        List longValues = new ArrayList<>();
        for (Number value : values) {
            longValues.add(value.longValue());
        }
        return new Range(Collections.min(longValues), Collections.max(longValues));
    }

    /**
     * Converts a long value to a boxed numeric type.
     *
     * @param value a long value
     * @param expectedType the boxed numeric type of the result
     * @return {@code value} converted to {@code expectedType} using standard conversion rules
     */
    private static  T convertLongToType(long value, Class expectedType) {
        Object convertedValue;
        if (expectedType == Integer.class) {
            convertedValue = (int) value;
        } else if (expectedType == Short.class) {
            convertedValue = (short) value;
        } else if (expectedType == Byte.class) {
            convertedValue = (byte) value;
        } else if (expectedType == Long.class) {
            convertedValue = value;
        } else if (expectedType == Double.class) {
            convertedValue = (double) value;
        } else if (expectedType == Float.class) {
            convertedValue = (float) value;
        } else if (expectedType == Character.class) {
            convertedValue = (char) value;
        } else {
            throw new UnsupportedOperationException(
                    "ValueCheckerUtils: unexpected class: " + expectedType);
        }
        return expectedType.cast(convertedValue);
    }

    /**
     * Get all possible values from the given type and cast them into a boxed primitive type.
     *
     * 

{@code expectedType} must be a boxed type, not a primitive type, because primitive types * cannot be stored in a list. * * @param range the given range * @param expectedType the expected type * @return a list of all the values in the range */ public static List getValuesFromRange(Range range, Class expectedType) { if (range == null || range.isWiderThan(ValueAnnotatedTypeFactory.MAX_VALUES)) { return null; } List values = new ArrayList<>(); if (range.isNothing()) { return values; } // The subtraction does not overflow, because the width has already been checked, so the // bound difference is less than ValueAnnotatedTypeFactory.MAX_VALUES. long boundDifference = range.to - range.from; // Each value is computed as a sum of the first value and an offset within the range, // to avoid having range.to as an upper bound of the loop. range.to can be Long.MAX_VALUE, // in which case a comparison value <= range.to would be always true. // boundDifference is always much smaller than Long.MAX_VALUE for (long offset = 0; offset <= boundDifference; offset++) { long value = range.from + offset; values.add(convertLongToType(value, expectedType)); } return values; } private static List convertToStringVal(List origValues) { if (origValues == null) { return null; } List strings = new ArrayList<>(); for (Object value : origValues) { strings.add(value.toString()); } return strings; } private static List convertBoolVal(AnnotationMirror anno, Class newClass) { List bools = AnnotationUtils.getElementValueArray(anno, "value", Boolean.class, true); if (newClass == String.class) { return convertToStringVal(bools); } return bools; } private static List convertStringVal(AnnotationMirror anno, Class newClass) { List strings = ValueAnnotatedTypeFactory.getStringValues(anno); if (newClass == char[].class) { List chars = new ArrayList<>(); for (String s : strings) { chars.add(s.toCharArray()); } return chars; } return strings; } private static List convertIntVal(List longs, Class newClass, TypeMirror newType) { if (longs == null) { return null; } if (newClass == String.class) { return convertToStringVal(longs); } else if (newClass == Character.class || newClass == char.class) { List chars = new ArrayList<>(); for (Long l : longs) { chars.add((char) l.longValue()); } return chars; } else if (newClass == Boolean.class) { throw new UnsupportedOperationException( "ValueAnnotatedTypeFactory: can't convert int to boolean"); } return NumberUtils.castNumbers(newType, longs); } private static List convertDoubleVal( AnnotationMirror anno, Class newClass, TypeMirror newType) { List doubles = ValueAnnotatedTypeFactory.getDoubleValues(anno); if (doubles == null) { return null; } if (newClass == String.class) { return convertToStringVal(doubles); } else if (newClass == Character.class || newClass == char.class) { List chars = new ArrayList<>(); for (Double l : doubles) { chars.add((char) l.doubleValue()); } return chars; } else if (newClass == Boolean.class) { throw new UnsupportedOperationException( "ValueAnnotatedTypeFactory: can't convert double to boolean"); } return NumberUtils.castNumbers(newType, doubles); } public static > List removeDuplicates(List values) { Set set = new TreeSet<>(values); return new ArrayList(set); } /** * Gets a list of lengths for a list of string values. * * @param values list of string values * @return list of unique lengths of strings in {@code values} */ public static List getLengthsForStringValues(List values) { List lengths = new ArrayList(); for (String str : values) { lengths.add(str.length()); } return ValueCheckerUtils.removeDuplicates(lengths); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy