You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@groovy.apache.org by "Tom Tobin (JIRA)" <ji...@apache.org> on 2015/05/29 03:38:17 UTC
[jira] [Updated] (GROOVY-7437) Support comparison between numbers
and strings
[ 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 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}
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:
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;
}
> 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 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}
--
This message was sent by Atlassian JIRA
(v6.3.4#6332)