You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by he...@apache.org on 2022/05/06 11:27:12 UTC
[commons-jexl] branch master updated: JEXL-366: homogenize conversion from string to numbers;
This is an automated email from the ASF dual-hosted git repository.
henrib pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-jexl.git
The following commit(s) were added to refs/heads/master by this push:
new 42eefe6f JEXL-366: homogenize conversion from string to numbers;
42eefe6f is described below
commit 42eefe6f8f2d1166ad2998643e672a0bec3fb68c
Author: henrib <he...@apache.org>
AuthorDate: Fri May 6 13:27:06 2022 +0200
JEXL-366: homogenize conversion from string to numbers;
---
.../org/apache/commons/jexl3/JexlArithmetic.java | 162 ++++++++++++---------
.../org/apache/commons/jexl3/ArithmeticTest.java | 8 +
2 files changed, 102 insertions(+), 68 deletions(-)
diff --git a/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java b/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java
index 54afcf93..75e77d16 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java
@@ -31,6 +31,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import static java.lang.StrictMath.floor;
+
/**
* Perform arithmetic, implements JexlOperator methods.
*
@@ -765,7 +767,7 @@ public class JexlArithmetic {
final BigInteger r = toBigInteger(right);
final BigInteger result = l.add(r);
return narrowBigInteger(left, right, result);
- } catch (final java.lang.NumberFormatException nfe) {
+ } catch (final ArithmeticException nfe) {
if (left == null || right == null) {
controlNullOperand();
}
@@ -1044,7 +1046,7 @@ public class JexlArithmetic {
/**
* Positivize value (unary plus for numbers).
* <p>C/C++/C#/Java perform integral promotion of the operand, ie
- * cast to int if type can represented as int without loss of precision.
+ * cast to int if type can be represented as int without loss of precision.
* @see #isPositivizeStable()
* @param val the value to positivize
* @return the positive value
@@ -1356,21 +1358,6 @@ public class JexlArithmetic {
return l >>> r;
}
- /**
- * Converts an arg to a long for comparison purpose.
- * @param arg the arg
- * @return a long
- * @throws NumberFormatException if the
- */
- private long comparableLong(Object arg) throws NumberFormatException {
- if (arg instanceof String) {
- String s = (String) arg;
- return s.isEmpty()? 0 :(long) Double.parseDouble((String) arg);
- } else {
- return toLong(arg);
- }
- }
-
/**
* Performs a comparison.
*
@@ -1388,9 +1375,13 @@ public class JexlArithmetic {
return l.compareTo(r);
}
if (left instanceof BigInteger || right instanceof BigInteger) {
- final BigInteger l = toBigInteger(left);
- final BigInteger r = toBigInteger(right);
- return l.compareTo(r);
+ try {
+ final BigInteger l = toBigInteger(left);
+ final BigInteger r = toBigInteger(right);
+ return l.compareTo(r);
+ } catch(ArithmeticException xconvert) {
+ // ignore it, continue in sequence
+ }
}
if (isFloatingPoint(left) || isFloatingPoint(right)) {
final double lhs = toDouble(left);
@@ -1405,26 +1396,14 @@ public class JexlArithmetic {
// lhs is not NaN
return +1;
}
- if (lhs < rhs) {
- return -1;
- }
- if (lhs > rhs) {
- return +1;
- }
- return 0;
+ return Double.compare(lhs, rhs);
}
if (isNumberable(left) || isNumberable(right)) {
try {
- final long lhs = comparableLong(left);
- final long rhs = comparableLong(right);
- if (lhs < rhs) {
- return -1;
- }
- if (lhs > rhs) {
- return +1;
- }
- return 0;
- } catch(NumberFormatException xformat) {
+ final long lhs = toLong(left);
+ final long rhs = toLong(right);
+ return Long.compare(lhs, rhs);
+ } catch(ArithmeticException xconvert) {
// ignore it, continue in sequence
}
}
@@ -1571,20 +1550,14 @@ public class JexlArithmetic {
return 0;
}
if (val instanceof Double) {
- final Double dval = (Double) val;
- if (Double.isNaN(dval)) {
- return 0;
- }
- return dval.intValue();
+ final double dval = (Double) val;
+ return Double.isNaN(dval)? 0 : (int) dval;
}
if (val instanceof Number) {
return ((Number) val).intValue();
}
if (val instanceof String) {
- if ("".equals(val)) {
- return 0;
- }
- return Integer.parseInt((String) val);
+ return parseInteger((String) val);
}
if (val instanceof Boolean) {
return ((Boolean) val) ? 1 : 0;
@@ -1615,20 +1588,14 @@ public class JexlArithmetic {
return 0L;
}
if (val instanceof Double) {
- final Double dval = (Double) val;
- if (Double.isNaN(dval)) {
- return 0L;
- }
- return dval.longValue();
+ final double dval = (Double) val;
+ return Double.isNaN(dval)? 0L : (long) dval;
}
if (val instanceof Number) {
return ((Number) val).longValue();
}
if (val instanceof String) {
- if ("".equals(val)) {
- return 0L;
- }
- return Long.parseLong((String) val);
+ return parseLong((String) val);
}
if (val instanceof Boolean) {
return ((Boolean) val) ? 1L : 0L;
@@ -1639,11 +1606,80 @@ public class JexlArithmetic {
if (val instanceof Character) {
return ((Character) val);
}
-
throw new ArithmeticException("Long coercion: "
+ val.getClass().getName() + ":(" + val + ")");
}
+
+ /**
+ * Convert a string to a double.
+ * <>Empty string is considered as NaN.</>
+ * @param arg the arg
+ * @return a double
+ * @throws ArithmeticException if the string can not be coerced into a double
+ */
+ private double parseDouble(String arg) throws ArithmeticException {
+ try {
+ return arg.isEmpty()? Double.NaN : Double.parseDouble((String) arg);
+ } catch(NumberFormatException xformat) {
+ throw new ArithmeticException("Double coercion: ("+ arg +")");
+ }
+ }
+
+ /**
+ * Converts a string to a long.
+ * <p>This ensure the represented number is a natural (not a real).</p>
+ * @param arg the arg
+ * @return a long
+ * @throws ArithmeticException if the string can not be coerced into a long
+ */
+ private long parseLong(String arg) throws ArithmeticException {
+ final double d = parseDouble(arg);
+ if (Double.isNaN(d)) {
+ return 0L;
+ }
+ final double f = floor(d);
+ if (d == f) {
+ return (long) d;
+ }
+ throw new ArithmeticException("Long coercion: ("+ arg +")");
+ }
+
+ /**
+ * Converts a string to an int.
+ * <p>This ensure the represented number is a natural (not a real).</p>
+ * @param arg the arg
+ * @return an int
+ * @throws ArithmeticException if the string can not be coerced into a long
+ */
+ private int parseInteger(String arg) throws ArithmeticException {
+ final long l = parseLong(arg);
+ final int i = (int) l;
+ if ((long) i == l) {
+ return i;
+ }
+ throw new ArithmeticException("Int coercion: ("+ arg +")");
+ }
+
+ /**
+ * Converts a string to a big integer.
+ * <>Empty string is considered as 0.</>
+ * @param arg the arg
+ * @return a big integer
+ * @throws ArithmeticException if the string can not be coerced into a big integer
+ */
+ private BigInteger parseBigInteger(String arg) throws ArithmeticException {
+ if (arg.isEmpty()) {
+ return BigInteger.ZERO;
+ }
+ try {
+ return new BigInteger(arg);
+ } catch(NumberFormatException xformat) {
+ // ignore, try harder
+ }
+ return BigInteger.valueOf(parseLong(arg));
+ }
+
/**
* Coerce to a BigInteger.
* <p>Double.NaN, null and empty string coerce to zero.</p>
@@ -1681,17 +1717,12 @@ public class JexlArithmetic {
return BigInteger.valueOf(((AtomicBoolean) val).get() ? 1L : 0L);
}
if (val instanceof String) {
- final String string = (String) val;
- if ("".equals(string)) {
- return BigInteger.ZERO;
- }
- return new BigInteger(string);
+ return parseBigInteger((String) val);
}
if (val instanceof Character) {
final int i = ((Character) val);
return BigInteger.valueOf(i);
}
-
throw new ArithmeticException("BigInteger coercion: "
+ val.getClass().getName() + ":(" + val + ")");
}
@@ -1772,12 +1803,7 @@ public class JexlArithmetic {
return ((AtomicBoolean) val).get() ? 1. : 0.;
}
if (val instanceof String) {
- final String string = (String) val;
- if ("".equals(string)) {
- return Double.NaN;
- }
- // the spec seems to be iffy about this. Going to give it a wack anyway
- return Double.parseDouble(string);
+ return parseDouble((String) val);
}
if (val instanceof Character) {
final int i = ((Character) val);
diff --git a/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java b/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java
index 3eb611fb..8c1ca580 100644
--- a/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java
+++ b/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java
@@ -1791,6 +1791,14 @@ public class ArithmeticTest extends JexlTestCase {
"'1.0' == 1.0d", true,
"'1.0' == 1.0b", true,
"'1.01' == 1.01", true,
+ "'1.01' == 1", false,
+ "'1.01' == 1b", false,
+ "'1.01' == 1h", false,
+ "'1.00001' == 1b", false,
+ "'1.00001' == 1h", false,
+ "'1.00000001' == 1", false,
+ "'1.00000001' == 1b", false,
+ "'1.00000001' == 1h", false,
"1.0 >= '1'", true,
"1.0 > '1'", false,
};