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/05 22:21:17 UTC
[commons-jexl] branch master updated: JEXL-366: compare will try and convert a string to number when one argument is a number
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 33bc10c1 JEXL-366: compare will try and convert a string to number when one argument is a number
33bc10c1 is described below
commit 33bc10c109403639aca7ef22dd7a883468fd6e30
Author: henrib <he...@apache.org>
AuthorDate: Fri May 6 00:19:17 2022 +0200
JEXL-366: compare will try and convert a string to number when one argument is a number
---
.../org/apache/commons/jexl3/JexlArithmetic.java | 46 ++++++++---
.../org/apache/commons/jexl3/ArithmeticTest.java | 92 ++++++++++++++++++----
2 files changed, 112 insertions(+), 26 deletions(-)
diff --git a/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java b/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java
index b34d3678..54afcf93 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java
@@ -497,7 +497,16 @@ public class JexlArithmetic {
}
/**
- * Given a Number, return back the value using the smallest type the result
+ * The last method called before returning a result from a script execution.
+ * @param returned the returned value
+ * @return the controlled returned value
+ */
+ public Object controlReturn(Object returned) {
+ return returned;
+ }
+
+ /**
+ * Given a Number, return the value using the smallest type the result
* will fit into.
* <p>This works hand in hand with parameter 'widening' in java
* method calls, e.g. a call to substring(int,int) with an int and a long
@@ -1347,6 +1356,21 @@ 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.
*
@@ -1390,15 +1414,19 @@ public class JexlArithmetic {
return 0;
}
if (isNumberable(left) || isNumberable(right)) {
- final long lhs = toLong(left);
- final long rhs = toLong(right);
- if (lhs < rhs) {
- return -1;
- }
- if (lhs > rhs) {
- return +1;
+ 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) {
+ // ignore it, continue in sequence
}
- return 0;
}
if (left instanceof String || right instanceof String) {
return toString(left).compareTo(toString(right));
diff --git a/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java b/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java
index 8b8cacc5..3eb611fb 100644
--- a/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java
+++ b/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java
@@ -270,6 +270,7 @@ public class ArithmeticTest extends JexlTestCase {
asserter.failExpression("left > y.right", ".*null.*");
asserter.failExpression("left >= y.right", ".*null.*");
}
+
@Test
public void testNullOperands() throws Exception {
asserter.setVariable("left", null);
@@ -781,20 +782,20 @@ public class ArithmeticTest extends JexlTestCase {
}
@Test
- public void testNullArgs() throws Exception {
+ public void testNullArgs() {
JexlEngine jexl = new JexlBuilder().arithmetic(new JexlArithmetic(true) {
@Override public boolean isStrict(JexlOperator op) {
return JexlOperator.ADD == op? false: super.isStrict(op);
}
}).create();
JexlScript script = jexl.createScript("'1.2' + x ", "x");
- Object result = script.execute(null, null);
+ Object result = script.execute(null);
Assert.assertEquals("1.2", result);
}
@Test
- public void testOption() throws Exception {
- final Map<String, Object> vars = new HashMap<String, Object>();
+ public void testOption() {
+ final Map<String, Object> vars = new HashMap<>();
final JexlEvalContext context = new JexlEvalContext(vars);
final JexlOptions options = context.getEngineOptions();
options.setStrictArithmetic(true);
@@ -811,7 +812,7 @@ public class ArithmeticTest extends JexlTestCase {
}
@Test
- public void testIsFloatingPointPattern() throws Exception {
+ public void testIsFloatingPointPattern() {
final JexlArithmetic ja = new JexlArithmetic(true);
Assert.assertFalse(ja.isFloatingPointNumber("floating point"));
@@ -918,7 +919,7 @@ public class ArithmeticTest extends JexlTestCase {
}
}
- // an arithmetic that know how to subtract strings
+ // an arithmetic that knows how to deal with vars
public static class ArithmeticPlus extends JexlArithmetic {
public ArithmeticPlus(final boolean strict) {
super(strict);
@@ -1041,7 +1042,7 @@ public class ArithmeticTest extends JexlTestCase {
}
@Test
- public void testArithmeticPlusNoCache() throws Exception {
+ public void testArithmeticPlusNoCache() {
final JexlEngine jexl = new JexlBuilder().cache(0).arithmetic(new ArithmeticPlus(false)).create();
final JexlContext jc = new EmptyTestContext();
runOverload(jexl, jc);
@@ -1258,7 +1259,7 @@ public class ArithmeticTest extends JexlTestCase {
}
@Test
- public void testJexl173() throws Exception {
+ public void testJexl173() {
final JexlEngine jexl = new JexlBuilder().create();
final JexlContext jc = new MapContext();
final Callable173 c173 = new Callable173();
@@ -1522,7 +1523,7 @@ public class ArithmeticTest extends JexlTestCase {
}
@Test
- public void testEmptyLong() throws Exception {
+ public void testEmptyLong() {
Object x;
x = JEXL.createScript("new('java.lang.Long', 4294967296)").execute(null);
Assert.assertEquals(4294967296L, ((Long) x).longValue());
@@ -1539,7 +1540,7 @@ public class ArithmeticTest extends JexlTestCase {
}
@Test
- public void testEmptyFloat() throws Exception {
+ public void testEmptyFloat() {
Object x;
x = JEXL.createScript("4294967296.f").execute(null);
Assert.assertEquals(4294967296.0f, (Float) x, EPSILON);
@@ -1555,7 +1556,7 @@ public class ArithmeticTest extends JexlTestCase {
}
@Test
- public void testEmptyDouble() throws Exception {
+ public void testEmptyDouble() {
Object x;
x = JEXL.createScript("4294967296.d").execute(null);
Assert.assertEquals(4294967296.0d, (Double) x, EPSILON);
@@ -1584,7 +1585,7 @@ public class ArithmeticTest extends JexlTestCase {
}
@Test
- public void testCoerceInteger() throws Exception {
+ public void testCoerceInteger() {
final JexlArithmetic ja = JEXL.getArithmetic();
final JexlEvalContext ctxt = new JexlEvalContext();
final JexlOptions options = ctxt.getEngineOptions();
@@ -1603,7 +1604,7 @@ public class ArithmeticTest extends JexlTestCase {
}
@Test
- public void testCoerceLong() throws Exception {
+ public void testCoerceLong() {
final JexlArithmetic ja = JEXL.getArithmetic();
final JexlEvalContext ctxt = new JexlEvalContext();
final JexlOptions options = ctxt.getEngineOptions();
@@ -1622,7 +1623,7 @@ public class ArithmeticTest extends JexlTestCase {
}
@Test
- public void testCoerceDouble() throws Exception {
+ public void testCoerceDouble() {
final JexlArithmetic ja = JEXL.getArithmetic();
final JexlEvalContext ctxt = new JexlEvalContext();
final JexlOptions options = ctxt.getEngineOptions();
@@ -1641,7 +1642,7 @@ public class ArithmeticTest extends JexlTestCase {
}
@Test
- public void testCoerceBigInteger() throws Exception {
+ public void testCoerceBigInteger() {
final JexlArithmetic ja = JEXL.getArithmetic();
final JexlEvalContext ctxt = new JexlEvalContext();
final JexlOptions options = ctxt.getEngineOptions();
@@ -1660,7 +1661,7 @@ public class ArithmeticTest extends JexlTestCase {
}
@Test
- public void testCoerceBigDecimal() throws Exception {
+ public void testCoerceBigDecimal() {
final JexlArithmetic ja = JEXL.getArithmetic();
final JexlEvalContext ctxt = new JexlEvalContext();
final JexlOptions options = ctxt.getEngineOptions();
@@ -1679,7 +1680,7 @@ public class ArithmeticTest extends JexlTestCase {
}
@Test
- public void testAtomicBoolean() throws Exception {
+ public void testAtomicBoolean() {
// in a condition
JexlScript e = JEXL.createScript("if (x) 1 else 2;", "x");
final JexlContext jc = new MapContext();
@@ -1748,4 +1749,61 @@ public class ArithmeticTest extends JexlTestCase {
o = e.execute(jc, ab);
Assert.assertTrue((Boolean) o);
}
+
+ @Test
+ public void testCompare() {
+ // JEXL doesn't support more than one operator in the same expression, for example: 1 == 1 == 1
+ final Object[] EXPRESSIONS = {
+ // Basic compare
+ "1 == 1", true,
+ "1 != 1", false,
+ "1 != 2", true,
+ "1 > 2", false,
+ "1 >= 2", false,
+ "1 < 2", true,
+ "1 <= 2", true,
+ // Int <-> Float Coercion
+ "1.0 == 1", true,
+ "1 == 1.0", true,
+ "1.1 != 1", true,
+ "1.1 < 2", true,
+ // Big Decimal <-> Big Integer Coercion
+ "1.0b == 1h", true,
+ "1h == 1.0b", true,
+ "1.1b != 1h", true,
+ "1.1b < 2h", true,
+ // Mix all type of numbers
+ "1l == 1.0", true, // long and int
+ "1.0d == 1.0f", true, // double and float
+ "1l == 1.0b", true,
+ "1l == 1h", true,
+ "1.0d == 1.0b", true,
+ "1.0f == 1.0b", true,
+ "1.0d == 1h", true,
+ "1.0f == 1h", true,
+ // numbers and strings
+ "'1' == 1", true,
+ "'1' == 1l", true,
+ "'1' == 1h", true,
+ "'' == 0", true, // empty string is coerced to zero (ECMA compliance)
+ "'1.0' == 1", true,
+ "'1.0' == 1.0f", true,
+ "'1.0' == 1.0d", true,
+ "'1.0' == 1.0b", true,
+ "'1.01' == 1.01", true,
+ "1.0 >= '1'", true,
+ "1.0 > '1'", false,
+ };
+ final JexlEngine jexl = new JexlBuilder().create();
+ final JexlContext jc = new EmptyTestContext();
+ JexlExpression expression;
+
+ for (int e = 0; e < EXPRESSIONS.length; e += 2) {
+ final String stext = (String) EXPRESSIONS[e];
+ final Object expected = EXPRESSIONS[e + 1];
+ expression = jexl.createExpression(stext);
+ final Object result = expression.evaluate(jc);
+ Assert.assertEquals("failed on " + stext, expected, result);
+ }
+ }
}