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

org.tools4j.groovytables.ValueCoerser.groovy Maven / Gradle / Ivy

Go to download

A groovy API which allows you to create lists of objects using a table like grammar.

There is a newer version: 1.6
Show newest version
package org.tools4j.groovytables

import java.lang.reflect.Array

/**
 * User: ben
 * Date: 12/02/2016
 * Time: 6:25 AM
 */
class ValueCoerser {


    public static ValueCoercionResult coerceToType(final Object value, final Class clazz) {
        if(clazz == double){
            return coerceToDoublePrimitive(value)
        } else if(clazz == Double){
            return coerceToDouble(value)
        } else if(clazz == int){
            return coerceToIntPrimitive(value)
        } else if(clazz == Integer){
            return coerceToInteger(value)
        } else if(clazz == float){
            return coerceToFloatPrimitive(value)
        } else if(clazz == Float){
            return coerceToFloat(value)
        } else if(clazz == short){
            return coerceToShortPrimitive(value)
        } else if(clazz == Short){
            return coerceToShort(value)
        } else if(clazz == long){
            return coerceToLongPrimitive(value)
        } else if(clazz == Long){
            return coerceToLong(value)
        } else if(clazz == boolean){
            return coerceToBooleanPrimitive(value)
        } else if(clazz == Boolean){
            return coerceToBoolean(value)
        } else if(clazz == BigDecimal){
            return coerceToBigDecimal(value)
        } else if(clazz == String){
            return coerceToString(value)
        } else if(clazz == GString){
            return coerceToGString(value)
        } else if(clazz.isArray()){
            return coerceToArrayOfGivenType(value, clazz.getComponentType())
        } else if(clazz instanceof List){
            return coerceToListOfGivenType(value, clazz)
        } else if(value == null) {
            return new ValueCoercionResult(null, Suitability.SUITABLE)
        } else {
            return attemptSimpleAssignment(value, clazz)
        }
    }

    static  ValueCoercionResult> coerceToListOfGivenType(final Object value, final Class clazz) {
        if(value == null) {
            return new ValueCoercionResult(null, Suitability.SUITABLE)
        } else if(value instanceof Collection){
            Logger.info("            Coercing collection $value to list of $clazz.simpleName")
            final Collection objCollection = (Collection) value
            final List> coercionResults = new ArrayList<>()
            for(final Object object: objCollection){
                final ValueCoercionResult coercionResult = coerceToType(object, clazz)
                coercionResults.add(coercionResult)
                Logger.info("                Coersion result: $coercionResult")
            }
            final Suitability worstSuitability = coercionResults.collect{ it.suitability }.inject { Suitability worst, Suitability it -> worst.worseOf(it) }
            if(worstSuitability.isMoreSuitableThan(Suitability.NOT_SUITABLE)){
                List resultsAsList = coercionResults.collect{it.result}
                return new ValueCoercionResult>(resultsAsList, worstSuitability)
            }
        } else if(value.class.isArray()){
            final ValueCoercionResult> valueCoercionResult = coerceToListOfGivenType(Arrays.asList(value), clazz)
            //if suitability==SUITABLE downgrade to SUITABLE_WITH_EXPECTED_COERSION, as we've received an array, but have coerced to a List
            return new ValueCoercionResult>(valueCoercionResult.result, valueCoercionResult.suitability.worseOf(Suitability.SUITABLE_WITH_EXPECTED_COERCION))
        }
        return ValueCoercionResult.NOT_SUITABLE
    }

    /**
     * There could be much optimization done here.
     *
     * The method I'm using is to loop through the given array, and attempt to coerce each and every member.
     *
     * There are some much (presumably) faster methods.  e.g. if coercing to an array of integers, and if given
     * an array of doubles, it is possible just to use a simple cast (Integer[]) or (int[]).
     *
     * Might do this later.
     */
    static  ValueCoercionResult coerceToArrayOfGivenType(final Object value, final Class clazz) {
        if(value == null) {
            return new ValueCoercionResult(null, Suitability.SUITABLE)
        } else if(value.class.isArray()){
            Logger.info("            Coercing array $value to array of $clazz.simpleName")
            final Object[] objArray = (Object[]) value
            final List> coercionResults = new ArrayList<>()
            for(final Object object: objArray){
                final ValueCoercionResult coercionResult = coerceToType(object, clazz)
                coercionResults.add(coercionResult)
                Logger.info("                Coersion result: $coercionResult")
            }
            final Suitability worstSuitability = coercionResults.collect{ it.suitability }.inject { Suitability worst, Suitability it -> worst.worseOf(it) }
            if(worstSuitability.isMoreSuitableThan(Suitability.NOT_SUITABLE)){
                List resultsAsList = coercionResults.collect{it.result}
                // important not to try and cast this array to T.  Doing so will cast it to an Object[] if clazz is a primitive type
                Object array = Array.newInstance(clazz, resultsAsList.size())
                resultsAsList.eachWithIndex{ T entry, int i -> array[i] = entry}
                return new ValueCoercionResult(array, worstSuitability)
            }
        } else if(value instanceof Collection){
            final ValueCoercionResult valueCoercionResult = coerceToArrayOfGivenType(((Collection) value).toArray(new T[value.size()]), clazz)
            //if suitability==SUITABLE downgrade to SUITABLE_WITH_EXPECTED_COERSION, as we've received a collecion, but have coerced to an array
            return new ValueCoercionResult(valueCoercionResult.result, valueCoercionResult.suitability.worseOf(Suitability.SUITABLE_WITH_EXPECTED_COERCION))
        }
        return ValueCoercionResult.NOT_SUITABLE
    }

    static ValueCoercionResult attemptSimpleAssignment(final Object value, final Class clazz) {
        if(value.class == clazz){
            return new ValueCoercionResult(value, Suitability.SUITABLE)
        } else if(clazz.isAssignableFrom(value.class)){
            return new ValueCoercionResult(value, Suitability.SUITABLE_BY_UP_CASTING)
        } else {
            return ValueCoercionResult.NOT_SUITABLE
        }
    }

    public static ValueCoercionResult coerceToDouble(final Object value){
        if(value == null){
            return new ValueCoercionResult(null, Suitability.SUITABLE)

        } else if(value instanceof Double){
            return new ValueCoercionResult(value, Suitability.SUITABLE);

        } else if(value instanceof Integer){
            return new ValueCoercionResult((double) value, Suitability.SUITABLE_WITH_COERCION);

        } else if(value instanceof Short){
            return new ValueCoercionResult((double) value, Suitability.SUITABLE_WITH_COERCION);

        } else if(value instanceof Float
                && (((float) value) - ((int) value)) == 0.0d){
            //Am putting the conversion of floating point numbers from float to double in the too-hard-basket
            return new ValueCoercionResult((double) value, Suitability.SUITABLE_WITH_COERCION);

        } else if(value instanceof Long
                && value <= Double.MAX_VALUE
                && value >= -Double.MAX_VALUE){
            return new ValueCoercionResult((double) value, Suitability.SUITABLE_WITH_COERCION);

        } else if(value instanceof BigDecimal) {
            double doubleValue = ((BigDecimal) value).doubleValue()
            if(doubleValue != Double.NEGATIVE_INFINITY && doubleValue != Double.POSITIVE_INFINITY) {
                /**
                 * This has been marked with SUITABLE_WITH_EXPECTED_COERCION.  I've done this because in groovy, if you
                 * write a decimal value, e.g. 2.3 groovy will by default create a BigDecimal to store a value.  To save
                 * developers having to put a 'd' after each decimal, I'm ranking this coercion higher.
                 */
                return new ValueCoercionResult(doubleValue, Suitability.SUITABLE_WITH_EXPECTED_COERCION);
            }

        } else if(value instanceof String){
            try {
                final int num = Double.parseDouble((String) value)
                return new ValueCoercionResult(num, Suitability.SUITABLE_WITH_COERCION)

            } catch (NumberFormatException e) {}

        } else if(value instanceof GString){
            return coerceToDouble(value.toString())

        }
        return ValueCoercionResult.NOT_SUITABLE;
    }

    public static ValueCoercionResult coerceToDoublePrimitive(final Object value){
        if(value == null){
            return ValueCoercionResult.NOT_SUITABLE
        } else {
            return coerceToDouble(value);
        }
    }

    public static ValueCoercionResult coerceToFloat(final Object value){
        if(value == null){
            return new ValueCoercionResult(null, Suitability.SUITABLE)

        } else if(value instanceof Float){
            return new ValueCoercionResult(value, Suitability.SUITABLE);

        } else if(value instanceof Double
                && value <= Float.MAX_VALUE
                && value >= -Float.MAX_VALUE
                && (((double) value) - ((int) value)) == 0.0d){
            //Am putting the conversion of floating point numbers from double to float in the too-hard-basket
            return new ValueCoercionResult(value, Suitability.SUITABLE_WITH_COERCION)

        } else if(value instanceof Integer
                && value <= Float.MAX_VALUE
                && value >= -Float.MAX_VALUE){
            return new ValueCoercionResult((float) value, Suitability.SUITABLE_WITH_COERCION);

        } else if(value instanceof Short){
            return new ValueCoercionResult((float) value, Suitability.SUITABLE_WITH_COERCION);

        } else if(value instanceof Long
                && value <= Float.MAX_VALUE
                && value >= -Float.MAX_VALUE){
            return new ValueCoercionResult((float) value, Suitability.SUITABLE_WITH_COERCION);

        } else if(value instanceof BigDecimal ){
            final BigDecimal bd = (BigDecimal) value;
            if(bd.compareTo(-Float.MAX_VALUE) >= 0 && bd.compareTo(Float.MAX_VALUE) <= 1){
                float floatValue = bd.floatValue()
                if(floatValue != Float.NEGATIVE_INFINITY && floatValue != Float.POSITIVE_INFINITY) {
                    return new ValueCoercionResult(floatValue, Suitability.SUITABLE_WITH_COERCION);
                }
            }

        } else if(value instanceof String){
            try {
                final int num = Float.parseFloat((String) value)
                return new ValueCoercionResult(num, Suitability.SUITABLE_WITH_COERCION)

            } catch (NumberFormatException e) {}

        } else if(value instanceof GString){
            return coerceToFloat(value.toString())

        }
        return ValueCoercionResult.NOT_SUITABLE;
    }

    public static ValueCoercionResult coerceToFloatPrimitive(final Object value){
        if(value == null){
            return ValueCoercionResult.NOT_SUITABLE
        } else {
            return coerceToFloat(value);
        }
    }

    public static ValueCoercionResult coerceToInteger(final Object value){
        if(value == null){
            return new ValueCoercionResult(null, Suitability.SUITABLE)

        } else if(value instanceof Integer){
            return new ValueCoercionResult(value, Suitability.SUITABLE);

        } else if(value instanceof Short){
            return new ValueCoercionResult((int) value, Suitability.SUITABLE_WITH_COERCION);

        } else if(value instanceof Double
                && value <= Integer.MAX_VALUE
                && value >= Integer.MIN_VALUE
                && (((double) value) - ((int) value)) == 0.0d){
            return new ValueCoercionResult((int) value, Suitability.SUITABLE_WITH_COERCION);

        } else if(value instanceof Float
                && (((float) value) - ((int) value)) == 0.0d){
            return new ValueCoercionResult((int) value, Suitability.SUITABLE_WITH_COERCION);

        } else if(value instanceof Long
                && value <= Integer.MAX_VALUE
                && value >= Integer.MIN_VALUE){
            return new ValueCoercionResult((int) value, Suitability.SUITABLE_WITH_COERCION);

        } else if(value instanceof BigDecimal) {
            final BigDecimal bd = (BigDecimal) value;
            if ((bd.signum() == 0 || bd.scale() <= 0 || bd.stripTrailingZeros().scale() <= 0)
                    && bd.compareTo(Integer.MIN_VALUE) >= 0 && bd.compareTo(Integer.MAX_VALUE) <= 1) {
                return new ValueCoercionResult(bd.intValue(), Suitability.SUITABLE_WITH_COERCION);
            }

        } else if(value instanceof String){
            try {
                final int num = Integer.parseInt((String) value)
                return new ValueCoercionResult(num, Suitability.SUITABLE_WITH_COERCION)

            } catch (NumberFormatException e) {}

        } else if(value instanceof GString){
            return coerceToInteger(value.toString())
        }

        return ValueCoercionResult.NOT_SUITABLE;
    }

    public static ValueCoercionResult coerceToIntPrimitive(final Object value){
        if(value == null){
            return ValueCoercionResult.NOT_SUITABLE
        } else {
            return coerceToInteger(value);
        }
    }

    public static ValueCoercionResult coerceToLong(final Object value){
        if(value == null){
            return new ValueCoercionResult(null, Suitability.SUITABLE)

        } else if(value instanceof Long){
            return new ValueCoercionResult(value, Suitability.SUITABLE);

        } else if(value instanceof Integer){
            return new ValueCoercionResult((long) value, Suitability.SUITABLE_WITH_COERCION);

        } else if(value instanceof Short){
            return new ValueCoercionResult((long) value, Suitability.SUITABLE_WITH_COERCION);

        } else if(value instanceof Double
                && (((double) value) - ((long) value)) == 0.0d){
            return new ValueCoercionResult((long) value, Suitability.SUITABLE_WITH_COERCION);

        } else if(value instanceof Float
                && (((float) value) - ((long) value)) == 0.0d){
            return new ValueCoercionResult((long) value, Suitability.SUITABLE_WITH_COERCION);

        } else if(value instanceof BigDecimal) {
            final BigDecimal bd = (BigDecimal) value;
            if ((bd.signum() == 0 || bd.scale() <= 0 || bd.stripTrailingZeros().scale() <= 0)) {
                return new ValueCoercionResult(bd.longValueExact(), Suitability.SUITABLE_WITH_COERCION);
            }

        } else if(value instanceof String){
            try {
                final int num = Long.parseLong((String) value)
                return new ValueCoercionResult(num, Suitability.SUITABLE_WITH_COERCION)

            } catch (NumberFormatException e) {}

        } else if(value instanceof GString){
            return coerceToLong(value.toString())
        }

        return ValueCoercionResult.NOT_SUITABLE;
    }

    public static ValueCoercionResult coerceToLongPrimitive(final Object value){
        if(value == null){
            return ValueCoercionResult.NOT_SUITABLE
        } else {
            return coerceToLong(value);
        }
    }

    public static ValueCoercionResult coerceToShort(final Object value){
        if(value == null){
            return new ValueCoercionResult(null, Suitability.SUITABLE)

        } else if(value instanceof Short){
            return new ValueCoercionResult(value, Suitability.SUITABLE);

        } else if(value instanceof Integer
            && value <= Short.MAX_VALUE
            && value >= Short.MIN_VALUE){
            return new ValueCoercionResult((short) value, Suitability.SUITABLE_WITH_COERCION);

        } else if(value instanceof Short){
            return new ValueCoercionResult((short) value, Suitability.SUITABLE_WITH_COERCION);

        } else if(value instanceof Double
                && value <= Short.MAX_VALUE
                && value >= Short.MIN_VALUE
                && (((double) value) - ((short) value)) == 0.0d){
            return new ValueCoercionResult((short) value, Suitability.SUITABLE_WITH_COERCION);

        } else if(value instanceof Float
                && value <= Short.MAX_VALUE
                && value >= Short.MIN_VALUE
                && (((float) value) - ((short) value)) == 0.0d){
            return new ValueCoercionResult((short) value, Suitability.SUITABLE_WITH_COERCION);

        } else if(value instanceof Long
                && value <= Short.MAX_VALUE
                && value >= Short.MIN_VALUE){
            return new ValueCoercionResult((short) value, Suitability.SUITABLE_WITH_COERCION);

        } else if(value instanceof BigDecimal) {
            final BigDecimal bd = (BigDecimal) value;
            if ((bd.signum() == 0 || bd.scale() <= 0 || bd.stripTrailingZeros().scale() <= 0)
                    && bd.compareTo(Short.MIN_VALUE) >= 0 && bd.compareTo(Short.MAX_VALUE) <= 1) {
                return new ValueCoercionResult(bd.shortValueExact(), Suitability.SUITABLE_WITH_COERCION);
            }

        } else if(value instanceof String){
            try {
                final short num = Short.parseShort((String) value)
                return new ValueCoercionResult(num, Suitability.SUITABLE_WITH_COERCION)

            } catch (NumberFormatException e) {}

        } else if(value instanceof GString){
            return coerceToShort(value.toString())
        }

        return ValueCoercionResult.NOT_SUITABLE;
    }

    public static ValueCoercionResult coerceToShortPrimitive(final Object value){
        if(value == null){
            return ValueCoercionResult.NOT_SUITABLE
        } else {
            return coerceToShort(value);
        }
    }

    public static ValueCoercionResult coerceToBigDecimal(final Object value){
        if(value == null){
            return new ValueCoercionResult(null, Suitability.SUITABLE)

        } else if(value instanceof BigDecimal){
            return new ValueCoercionResult(value, Suitability.SUITABLE);

        } else if(value instanceof Double){
            return new ValueCoercionResult(BigDecimal.valueOf(value), Suitability.SUITABLE_WITH_COERCION);

        } else if(value instanceof Integer){
            return new ValueCoercionResult(BigDecimal.valueOf((long) value), Suitability.SUITABLE_WITH_COERCION);

        } else if(value instanceof Short){
            return new ValueCoercionResult(BigDecimal.valueOf((long) value), Suitability.SUITABLE_WITH_COERCION);

        } else if(value instanceof Float
                && (((float) value) - ((int) value)) == 0.0d){
            //Am putting the conversion of floating point numbers from float to double in the too-hard-basket
            return new ValueCoercionResult((double) value, Suitability.SUITABLE_WITH_COERCION);

        } else if(value instanceof Long){
            return new ValueCoercionResult((long) value, Suitability.SUITABLE_WITH_COERCION);

        } else if(value instanceof String){
            try {
                return new ValueCoercionResult(new BigDecimal((String) value), Suitability.SUITABLE_WITH_COERCION)

            } catch (NumberFormatException e) {}

        } else if(value instanceof GString){
            return coerceToBigDecimal(value.toString())

        }
        return ValueCoercionResult.NOT_SUITABLE;
    }

    public static ValueCoercionResult coerceToBoolean(final Object value){
        if(value == null){
            return new ValueCoercionResult(null, Suitability.SUITABLE)

        } else if(value instanceof Boolean){
            return new ValueCoercionResult(value, Suitability.SUITABLE);


        } else if(value instanceof String){
            if(value.equalsIgnoreCase("true")
                    || value.equalsIgnoreCase("T")
                    || value.equalsIgnoreCase("yes")
                    || value.equalsIgnoreCase("Y")){
                return new ValueCoercionResult(true, Suitability.SUITABLE_WITH_COERCION);
            } else if(value.equalsIgnoreCase("false")
                    || value.equalsIgnoreCase("F")
                    || value.equalsIgnoreCase("no")
                    || value.equalsIgnoreCase("N")){
                return new ValueCoercionResult(false, Suitability.SUITABLE_WITH_COERCION);
            }

        } else if(value instanceof GString){
            return coerceToBoolean(value.toString())
        }

        return ValueCoercionResult.NOT_SUITABLE;
    }

    public static ValueCoercionResult coerceToBooleanPrimitive(final Object value){
        if(value == null){
            return ValueCoercionResult.NOT_SUITABLE
        } else {
            return coerceToBoolean(value);
        }
    }

    public static ValueCoercionResult coerceToString(final Object value){
        if(value == null){
            return new ValueCoercionResult(null, Suitability.SUITABLE)

        } else if(value instanceof String){
            return new ValueCoercionResult(value, Suitability.SUITABLE);


        } else if(value instanceof GString) {
            return new ValueCoercionResult(value.toString(), Suitability.SUITABLE_WITH_EXPECTED_COERCION);

        } else if(value instanceof StringBuilder) {
            return new ValueCoercionResult(value.toString(), Suitability.SUITABLE_WITH_COERCION);

        }
        return ValueCoercionResult.NOT_SUITABLE;
    }

    public static ValueCoercionResult coerceToGString(final Object value){
        if(value == null){
            return new ValueCoercionResult(null, Suitability.SUITABLE)

        } else if(value instanceof GString){
            return new ValueCoercionResult(value, Suitability.SUITABLE);

        } else if(value instanceof String){
            return new ValueCoercionResult("$value", Suitability.SUITABLE_WITH_EXPECTED_COERCION);

        } else if(value instanceof StringBuilder){
            return new ValueCoercionResult("${value}", Suitability.SUITABLE_WITH_COERCION);

        }

        return ValueCoercionResult.NOT_SUITABLE;
    }
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy