org.tools4j.groovytables.ValueCoerser.groovy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of groovy-tables Show documentation
Show all versions of groovy-tables Show documentation
A groovy API which allows you to create lists of objects using a table like grammar.
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;
}
}