[ https://issues.apache.org/jira/browse/GROOVY-7437?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
Tom Tobin updated GROOVY-7437: ------------------------------ Description: Would like logical comparison of numbers and strings, e.g. 123.45f > "300.0". Same applies to equality and both directions: "123.45" == 123.45d. Successful with other operators (plus, minus..) because operator overloading works. It does not work with comparison operators because DefaultTypeTransformations.java tries to do a smart compare but ignores operator overloading (that's why marking as bug vs enhancement). Simple fix is to add string compares if the string is a number in DefaultTypeTransformations:compareToWithEqualityCheck() (example below). More complete fix would be that plus respect operator overloading of compareTo() and equals(). I would also like to do special handling (e.g. 3rd party data sometimes in form 00012345 and would love for 0.34 > "30%" to work) - could do that if operator overloading worked. in : {code:title=DefaultTypeTransformations.java|borderStyle=solid} private static int compareToWithEqualityCheck(Object left, Object right, boolean equalityCheckOnly) { if (left == right) { return 0; } if (left == null) { return -1; } else if (right == null) { return 1; } if (left instanceof Comparable) { if (left instanceof Number) { if (right instanceof Character || right instanceof Number) { return DefaultGroovyMethods.compareTo((Number) left, castToNumber(right)); } if (isValidCharacterString(right)) { return DefaultGroovyMethods.compareTo((Number) left, ShortTypeHandling.castToChar(right)); } // ********* SNIP ********* if (right instanceof String) { Number rightNumber = convertToNumber(left.getClass(), (String)right); if (rightNumber != null) { return DefaultGroovyMethods.compareTo((Number) left, rightNumber); } } // ********* SNIP ********* } else if (left instanceof Character) { if (isValidCharacterString(right)) { return DefaultGroovyMethods.compareTo((Character)left, ShortTypeHandling.castToChar(right)); } if (right instanceof Number) { return DefaultGroovyMethods.compareTo((Character)left,(Number)right); } } else if (right instanceof Number) { if (isValidCharacterString(left)) { return DefaultGroovyMethods.compareTo(ShortTypeHandling.castToChar(left),(Number) right); } // ********* SNIP ********* if (left instanceof String) { Number number = convertToNumber(right.getClass(), (String)left); if (number != null) { return DefaultGroovyMethods.compareTo(number, (Number) right); } } // ********* SNIP ********* } else if (left instanceof String && right instanceof Character) { return ((String) left).compareTo(right.toString()); } else if (left instanceof String && right instanceof GString) { return ((String) left).compareTo(right.toString()); } if (!equalityCheckOnly || left.getClass().isAssignableFrom(right.getClass()) || (right.getClass() != Object.class && right.getClass().isAssignableFrom(left.getClass())) //GROOVY-4046 || (left instanceof GString && right instanceof String)) { Comparable comparable = (Comparable) left; return comparable.compareTo(right); } } if (equalityCheckOnly) { return -1; // anything other than 0 } throw new GroovyRuntimeException( MessageFormat.format("Cannot compare {0} with value ''{1}'' and {2} with value ''{3}''", left.getClass().getName(), left, right.getClass().getName(), right)); } // ********* SNIP ********* private static Number convertToNumber(Class numberClass, String string) { Number number = null; try { // attempt convert string to number - could be more elegant, like org.apache.commons.lang3.math.NumberUtils.createNumber() if (numberClass.equals(Integer.class)) { number = Integer.parseInt(string); } else if (numberClass.equals(Long.class)) { number = Long.parseLong(string); } else if (numberClass.equals(Float.class)) { number = Float.parseFloat(string); } else if (numberClass.equals(Double.class)) { number = Double.parseDouble(string); } else if (numberClass.equals(BigInteger.class)) { number = new BigInteger(string); } else if (numberClass.equals(BigDecimal.class)) { number = new BigDecimal(string); } } catch (NumberFormatException exception) { return null; } return number; } {code} was: Would like logical comparison of numbers and strings, e.g. 123.45f > "300.0". Same applies to equality and both directions: "123.45" == 123.45d. Successful with other operators (plus, minus..) because operator overloading works. It does not work with comparison operators because DefaultTypeTransformations.java tries to do a smart compare but ignores operator overloading (that's why marking as bug vs enhancement). Simple fix is to add string compares if the string is a number in DefaultTypeTransformations:compareToWithEqualityCheck() (example below). More complete fix would be that plus respect operator overloading of compareTo() and equals(). I would also like to do special handling (e.g. 3rd party data sometimes in form 00012345 and would love for 0.34 > "30%" to work) - could do that if operator overloading worked. in DefaultTypeTransformations.java: {code:title=Bar.java|borderStyle=solid} private static int compareToWithEqualityCheck(Object left, Object right, boolean equalityCheckOnly) { if (left == right) { return 0; } if (left == null) { return -1; } else if (right == null) { return 1; } if (left instanceof Comparable) { if (left instanceof Number) { if (right instanceof Character || right instanceof Number) { return DefaultGroovyMethods.compareTo((Number) left, castToNumber(right)); } if (isValidCharacterString(right)) { return DefaultGroovyMethods.compareTo((Number) left, ShortTypeHandling.castToChar(right)); } // ********* SNIP ********* if (right instanceof String) { Number rightNumber = convertToNumber(left.getClass(), (String)right); if (rightNumber != null) { return DefaultGroovyMethods.compareTo((Number) left, rightNumber); } } // ********* SNIP ********* } else if (left instanceof Character) { if (isValidCharacterString(right)) { return DefaultGroovyMethods.compareTo((Character)left, ShortTypeHandling.castToChar(right)); } if (right instanceof Number) { return DefaultGroovyMethods.compareTo((Character)left,(Number)right); } } else if (right instanceof Number) { if (isValidCharacterString(left)) { return DefaultGroovyMethods.compareTo(ShortTypeHandling.castToChar(left),(Number) right); } // ********* SNIP ********* if (left instanceof String) { Number number = convertToNumber(right.getClass(), (String)left); if (number != null) { return DefaultGroovyMethods.compareTo(number, (Number) right); } } // ********* SNIP ********* } else if (left instanceof String && right instanceof Character) { return ((String) left).compareTo(right.toString()); } else if (left instanceof String && right instanceof GString) { return ((String) left).compareTo(right.toString()); } if (!equalityCheckOnly || left.getClass().isAssignableFrom(right.getClass()) || (right.getClass() != Object.class && right.getClass().isAssignableFrom(left.getClass())) //GROOVY-4046 || (left instanceof GString && right instanceof String)) { Comparable comparable = (Comparable) left; return comparable.compareTo(right); } } if (equalityCheckOnly) { return -1; // anything other than 0 } throw new GroovyRuntimeException( MessageFormat.format("Cannot compare {0} with value ''{1}'' and {2} with value ''{3}''", left.getClass().getName(), left, right.getClass().getName(), right)); } // ********* SNIP ********* private static Number convertToNumber(Class numberClass, String string) { Number number = null; try { // attempt convert string to number - could be more elegant, like org.apache.commons.lang3.math.NumberUtils.createNumber() if (numberClass.equals(Integer.class)) { number = Integer.parseInt(string); } else if (numberClass.equals(Long.class)) { number = Long.parseLong(string); } else if (numberClass.equals(Float.class)) { number = Float.parseFloat(string); } else if (numberClass.equals(Double.class)) { number = Double.parseDouble(string); } else if (numberClass.equals(BigInteger.class)) { number = new BigInteger(string); } else if (numberClass.equals(BigDecimal.class)) { number = new BigDecimal(string); } } catch (NumberFormatException exception) { return null; } return number; } {code} > Support comparison between numbers and strings > ---------------------------------------------- > > Key: GROOVY-7437 > URL: https://issues.apache.org/jira/browse/GROOVY-7437 > Project: Groovy > Issue Type: Bug > Components: groovy-jdk > Affects Versions: 2.4.3 > Environment: Linux/Windows > Reporter: Tom Tobin > Assignee: Guillaume Laforge > Priority: Critical > > Would like logical comparison of numbers and strings, e.g. 123.45f > > "300.0". Same applies to equality and both directions: "123.45" == 123.45d. > Successful with other operators (plus, minus..) because operator overloading > works. It does not work with comparison operators because > DefaultTypeTransformations.java tries to do a smart compare but ignores > operator overloading (that's why marking as bug vs enhancement). > Simple fix is to add string compares if the string is a number in > DefaultTypeTransformations:compareToWithEqualityCheck() (example below). > More complete fix would be that plus respect operator overloading of > compareTo() and equals(). I would also like to do special handling (e.g. > 3rd party data sometimes in form 00012345 and would love for 0.34 > "30%" to > work) - could do that if operator overloading worked. > in : > {code:title=DefaultTypeTransformations.java|borderStyle=solid} > private static int compareToWithEqualityCheck(Object left, Object right, > boolean equalityCheckOnly) { > if (left == right) { > return 0; > } > if (left == null) { > return -1; > } > else if (right == null) { > return 1; > } > if (left instanceof Comparable) { > if (left instanceof Number) { > if (right instanceof Character || right instanceof Number) { > return DefaultGroovyMethods.compareTo((Number) left, > castToNumber(right)); > } > if (isValidCharacterString(right)) { > return DefaultGroovyMethods.compareTo((Number) left, > ShortTypeHandling.castToChar(right)); > } > // ********* SNIP ********* > if (right instanceof String) { > Number rightNumber = convertToNumber(left.getClass(), > (String)right); > if (rightNumber != null) { > return DefaultGroovyMethods.compareTo((Number) left, > rightNumber); > } > } > // ********* SNIP ********* > } > else if (left instanceof Character) { > if (isValidCharacterString(right)) { > return DefaultGroovyMethods.compareTo((Character)left, > ShortTypeHandling.castToChar(right)); > } > if (right instanceof Number) { > return > DefaultGroovyMethods.compareTo((Character)left,(Number)right); > } > } > else if (right instanceof Number) { > if (isValidCharacterString(left)) { > return > DefaultGroovyMethods.compareTo(ShortTypeHandling.castToChar(left),(Number) > right); > } > // ********* SNIP ********* > if (left instanceof String) { > Number number = convertToNumber(right.getClass(), > (String)left); > if (number != null) { > return DefaultGroovyMethods.compareTo(number, > (Number) right); > } > } > // ********* SNIP ********* > } > else if (left instanceof String && right instanceof Character) { > return ((String) left).compareTo(right.toString()); > } > else if (left instanceof String && right instanceof GString) { > return ((String) left).compareTo(right.toString()); > } > if (!equalityCheckOnly || > left.getClass().isAssignableFrom(right.getClass()) > || (right.getClass() != Object.class && > right.getClass().isAssignableFrom(left.getClass())) //GROOVY-4046 > || (left instanceof GString && right instanceof String)) { > Comparable comparable = (Comparable) left; > return comparable.compareTo(right); > } > } > if (equalityCheckOnly) { > return -1; // anything other than 0 > } > throw new GroovyRuntimeException( > MessageFormat.format("Cannot compare {0} with value ''{1}'' > and {2} with value ''{3}''", > left.getClass().getName(), > left, > right.getClass().getName(), > right)); > } > // ********* SNIP ********* > private static Number convertToNumber(Class numberClass, String string) { > Number number = null; > try { > // attempt convert string to number - could be more elegant, like > org.apache.commons.lang3.math.NumberUtils.createNumber() > if (numberClass.equals(Integer.class)) { > number = Integer.parseInt(string); > } else if (numberClass.equals(Long.class)) { > number = Long.parseLong(string); > } else if (numberClass.equals(Float.class)) { > number = Float.parseFloat(string); > } else if (numberClass.equals(Double.class)) { > number = Double.parseDouble(string); > } else if (numberClass.equals(BigInteger.class)) { > number = new BigInteger(string); > } else if (numberClass.equals(BigDecimal.class)) { > number = new BigDecimal(string); > } > } catch (NumberFormatException exception) { > return null; > } > return number; > } > {code} -- This message was sent by Atlassian JIRA (v6.3.4#6332)