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)