diff --git a/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java b/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java
index 76d1a7d70..ee09ebb7b 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java
@@ -1715,6 +1715,38 @@ private long parseLong(final String arg) throws ArithmeticException {
throw new CoercionException("Long coercion: ("+ arg +")");
}
+ /**
+ * Parse an identifier which must be of the form:
+ * 0|([1-9][0-9]*)
+ * @param id the identifier
+ * @return an integer or null
+ */
+ public static Integer parseIdentifier(final Object id) {
+ if (id instanceof Number) {
+ return ((Number) id).intValue();
+ }
+ // hand coded because the was no way to fail on leading '0's using NumberFormat
+ if (id instanceof CharSequence) {
+ final CharSequence str = (CharSequence) id;
+ final int length = str.length();
+ // can not be empty string and can not be longer than Integer.MAX_VALUE representation
+ if (length > 0 && length <= 10) {
+ int val = 0;
+ for (int i = 0; i < length; ++i) {
+ final char c = str.charAt(i);
+ // leading 0s but no just 0, numeric only
+ if ((c == '0' && val == 0 && length > 1) || (c < '0' || c > '9')) {
+ return null;
+ }
+ val *= 10;
+ val += c - '0';
+ }
+ return val;
+ }
+ }
+ return null;
+ }
+
/**
* Positivize value (unary plus for numbers).
*
C/C++/C#/Java perform integral promotion of the operand, ie
diff --git a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
index 3479c92a8..6096ff863 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
@@ -493,7 +493,7 @@ private Object evalJxltHandle(NODE
if (options.isStrictInterpolation()) {
return inter;
}
- final Integer id = ASTIdentifierAccess.parseIdentifier(inter);
+ final Integer id = JexlArithmetic.parseIdentifier(inter);
return id != null ? id : eval;
}
}
diff --git a/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java b/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java
index bdbfb373b..7608e7384 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java
@@ -331,7 +331,7 @@ public final Method[] getMethods(final Class> c, final String methodName) {
public JexlPropertyGet getPropertyGet(
final List resolvers, final Object obj, final Object identifier
) {
- final Class> claz = obj.getClass();
+ final Class> clazz = obj.getClass();
final String property = AbstractExecutor.castString(identifier);
final Introspector is = base();
final List r = resolvers == null ? strategy.apply(null, obj) : resolvers;
@@ -341,33 +341,33 @@ public JexlPropertyGet getPropertyGet(
switch ((JexlResolver) resolver) {
case PROPERTY:
// first try for a getFoo() type of property (also getfoo() )
- executor = PropertyGetExecutor.discover(is, claz, property);
+ executor = PropertyGetExecutor.discover(is, clazz, property);
if (executor == null) {
- executor = BooleanGetExecutor.discover(is, claz, property);
+ executor = BooleanGetExecutor.discover(is, clazz, property);
}
break;
case MAP:
// let's see if we are a map...
- executor = MapGetExecutor.discover(is, claz, identifier);
+ executor = MapGetExecutor.discover(is, clazz, identifier);
break;
case LIST:
// let's see if this is a list or array
final Integer index = AbstractExecutor.castInteger(identifier);
if (index != null) {
- executor = ListGetExecutor.discover(is, claz, index);
+ executor = ListGetExecutor.discover(is, clazz, index);
}
break;
case DUCK:
// if that didn't work, look for get(foo)
- executor = DuckGetExecutor.discover(is, claz, identifier);
+ executor = DuckGetExecutor.discover(is, clazz, identifier);
if (executor == null && property != null && property != identifier) {
// look for get("foo") if we did not try yet (just above)
- executor = DuckGetExecutor.discover(is, claz, property);
+ executor = DuckGetExecutor.discover(is, clazz, property);
}
break;
case FIELD:
// a field may be? (can not be a number)
- executor = FieldGetExecutor.discover(is, claz, property);
+ executor = FieldGetExecutor.discover(is, clazz, property);
// static class fields (enums included)
if (obj instanceof Class>) {
executor = FieldGetExecutor.discover(is, (Class>) obj, property);
@@ -399,7 +399,7 @@ public JexlPropertyGet getPropertyGet(final Object obj, final Object identifier)
public JexlPropertySet getPropertySet(
final List resolvers, final Object obj, final Object identifier, final Object arg
) {
- final Class> claz = obj.getClass();
+ final Class> clazz = obj.getClass();
final String property = AbstractExecutor.castString(identifier);
final Introspector is = base();
final List actual = resolvers == null ? strategy.apply(null, obj) : resolvers;
@@ -409,30 +409,30 @@ public JexlPropertySet getPropertySet(
switch ((JexlResolver) resolver) {
case PROPERTY:
// first try for a setFoo() type of property (also setfoo() )
- executor = PropertySetExecutor.discover(is, claz, property, arg);
+ executor = PropertySetExecutor.discover(is, clazz, property, arg);
break;
case MAP:
// let's see if we are a map...
- executor = MapSetExecutor.discover(is, claz, identifier, arg);
+ executor = MapSetExecutor.discover(is, clazz, identifier, arg);
break;
case LIST:
// let's see if we can convert the identifier to an int,
// if obj is an array or a list, we can still do something
final Integer index = AbstractExecutor.castInteger(identifier);
if (index != null) {
- executor = ListSetExecutor.discover(is, claz, identifier, arg);
+ executor = ListSetExecutor.discover(is, clazz, identifier, arg);
}
break;
case DUCK:
// if that didn't work, look for set(foo)
- executor = DuckSetExecutor.discover(is, claz, identifier, arg);
+ executor = DuckSetExecutor.discover(is, clazz, identifier, arg);
if (executor == null && property != null && property != identifier) {
- executor = DuckSetExecutor.discover(is, claz, property, arg);
+ executor = DuckSetExecutor.discover(is, clazz, property, arg);
}
break;
case FIELD:
// a field may be?
- executor = FieldSetExecutor.discover(is, claz, property, arg);
+ executor = FieldSetExecutor.discover(is, clazz, property, arg);
break;
case CONTAINER:
default:
diff --git a/src/main/java/org/apache/commons/jexl3/parser/ASTIdentifierAccess.java b/src/main/java/org/apache/commons/jexl3/parser/ASTIdentifierAccess.java
index 7943996c4..893471ee4 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/ASTIdentifierAccess.java
+++ b/src/main/java/org/apache/commons/jexl3/parser/ASTIdentifierAccess.java
@@ -16,6 +16,8 @@
*/
package org.apache.commons.jexl3.parser;
+import org.apache.commons.jexl3.JexlArithmetic;
+
/**
* Identifiers, variables and registers.
*/
@@ -23,38 +25,6 @@ public class ASTIdentifierAccess extends JexlNode {
/**
*/
private static final long serialVersionUID = 1L;
- /**
- * Parse an identifier which must be of the form:
- * 0|([1-9][0-9]*)
- * @param id the identifier
- * @return an integer or null
- */
- public static Integer parseIdentifier(final String id) {
- // hand coded because the was no way to fail on leading '0's using NumberFormat
- if (id != null) {
- final int length = id.length();
- int val = 0;
- for (int i = 0; i < length; ++i) {
- final char c = id.charAt(i);
- // leading 0s but no just 0, NaN
- if (c == '0') {
- if (length == 1) {
- return 0;
- }
- if (val == 0) {
- return null;
- }
- } // any non numeric, NaN
- else if (c < '0' || c > '9') {
- return null;
- }
- val *= 10;
- val += c - '0';
- }
- return val;
- }
- return null;
- }
private String name;
private Integer identifier;
@@ -99,7 +69,7 @@ public Object jjtAccept(final ParserVisitor visitor, final Object data) {
void setIdentifier(final String id) {
name = id;
- identifier = parseIdentifier(id);
+ identifier = JexlArithmetic.parseIdentifier(id);
}
@Override
diff --git a/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java b/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java
index 6d5b90598..484041fc2 100644
--- a/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java
+++ b/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java
@@ -31,6 +31,7 @@
import java.math.MathContext;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
@@ -2254,4 +2255,65 @@ public void testXmlArithmetic() throws Exception {
assertTrue(getJavaVersion() > 11);
}
}
+
+ @Test void testShortCircuitAnd() {
+ String src = "(x, y, z) -> x && y && z";
+ final JexlBuilder builder = new JexlBuilder();
+ final JexlEngine jexl = builder.create();
+ JexlScript script;
+ Object result;
+ script = jexl.createScript(src);
+ result = script.execute(null, true, "foo", 42);
+ assertEquals(42, result);
+ result = script.execute(null, true, "", 42);
+ assertEquals("", result);
+ }
+
+ @Test void testShortCircuitOr() {
+ OptContext optc = new OptContext();
+ String src = "(x, y, z) -> x || y || z";
+ final JexlBuilder builder = new JexlBuilder();
+ final JexlEngine jexl = builder.create();
+ JexlOptions options = builder.options();
+ optc.setOptions(options);
+ JexlScript script;
+ Object result;
+ script = jexl.createScript(src);
+ result = script.execute(optc, 0, "", 42);
+ assertEquals(42, result);
+ result = script.execute(optc, true, 42, null);
+ assertEquals(true, result);
+
+ options.setBooleanLogical(true);
+ result = script.execute(optc, 0, "", Double.NaN);
+ assertEquals(false, result);
+ result = script.execute(optc, 0, "", Collections.emptySet());
+ assertEquals(true, result);
+
+ }
+
+ @Test void testLogicalValue() {
+ String src = "function sanitize(const n) { n == 0 ? NaN : n }; sanitize(x) && 420 / x";
+ final JexlEngine jexl = new JexlBuilder().create();
+ JexlScript script;
+ Object result;
+ script = jexl.createScript(src, "x");
+ result = script.execute(null, 10);
+ assertEquals(42, result);
+ result = script.execute(null, 0);
+ assertTrue(Double.isNaN(((Number) result).doubleValue()));
+ }
+
+ public static class OptContext extends MapContext implements JexlContext.OptionsHandle {
+ private JexlOptions options;
+
+ @Override
+ public JexlOptions getEngineOptions() {
+ return options;
+ }
+
+ void setOptions(JexlOptions options) {
+ this.options = options;
+ }
+ }
}
diff --git a/src/test/java/org/apache/commons/jexl3/BuilderTest.java b/src/test/java/org/apache/commons/jexl3/BuilderTest.java
index 465b584e6..9f0bd222f 100644
--- a/src/test/java/org/apache/commons/jexl3/BuilderTest.java
+++ b/src/test/java/org/apache/commons/jexl3/BuilderTest.java
@@ -37,7 +37,7 @@ private static JexlBuilder builder() {
}
@Test
- public void testFlags() {
+ void testFlags() {
assertTrue(builder().antish(true).antish());
assertFalse(builder().antish(false).antish());
assertTrue(builder().cancellable(true).cancellable());
@@ -54,10 +54,14 @@ public void testFlags() {
assertFalse(builder().silent(false).silent());
assertTrue(builder().strict(true).strict());
assertFalse(builder().strict(false).strict());
+ assertTrue(builder().booleanLogical(true).options().isBooleanLogical());
+ assertFalse(builder().booleanLogical(false).options().isBooleanLogical());
+ assertTrue(builder().strictInterpolation(true).options().isStrictInterpolation());
+ assertFalse(builder().strictInterpolation(false).options().isStrictInterpolation());
}
@Test
- public void testOther() {
+ void testOther() {
final ClassLoader cls = getClass().getClassLoader().getParent();
assertEquals(cls, builder().loader(cls).loader());
final Charset cs = StandardCharsets.UTF_16;
@@ -71,7 +75,7 @@ public void testOther() {
}
@Test
- public void testValues() {
+ void testValues() {
assertEquals(1, builder().collectMode(1).collectMode());
assertEquals(0, builder().collectMode(0).collectMode());
assertEquals(32, builder().cacheThreshold(32).cacheThreshold());
diff --git a/src/test/java/org/apache/commons/jexl3/Issues400Test.java b/src/test/java/org/apache/commons/jexl3/Issues400Test.java
index 8ec230e40..df1417533 100644
--- a/src/test/java/org/apache/commons/jexl3/Issues400Test.java
+++ b/src/test/java/org/apache/commons/jexl3/Issues400Test.java
@@ -482,132 +482,4 @@ void testSortArray() {
assertEquals(9, m[1].get("type"));
}
- @Test void test425() {
- final JexlBuilder builder = new JexlBuilder().strictInterpolation(true);
- final JexlEngine jexl = builder.create();
- JexlScript script;
- Object result;
- script = jexl.createScript("let x = 42; let y = `${x}`; y");
- result = script.execute(null);
- assertTrue(result instanceof String);
- assertEquals("42", result);
- }
-
- @Test void test426a() {
- String src = "let x = 10;\n" +
- "let foo = () -> {\n" +
- "x += 2;\n" +
- "}\n" +
- "x = 40;\n" +
- "foo();\n" +
- "x";
- final JexlBuilder builder = new JexlBuilder().features(new JexlFeatures().constCapture(false).referenceCapture(true));
- final JexlEngine jexl = builder.create();
- JexlScript script;
- Object result;
- script = jexl.createScript(src);
- result = script.execute(null);
- assertEquals(42, result);
- }
-
- @Test void test426b() {
- String src = "let x = 10; let f = () -> { x + 2 }; x = 40; f()";
- final JexlBuilder builder = new JexlBuilder().features(new JexlFeatures().constCapture(true).referenceCapture(true));
- final JexlEngine jexl = builder.create();
- JexlScript script;
- Object result;
- script = jexl.createScript(src);
- result = script.execute(null);
- assertEquals(42, result);
- }
-
- @Test void test426c() {
- String src = "let x = 10; let f = () -> { x + 2 }; x = 40; f";
- final JexlBuilder builder = new JexlBuilder().features(new JexlFeatures().constCapture(true).referenceCapture(true));
- final JexlEngine jexl = builder.create();
- JexlScript script;
- Object result;
- script = jexl.createScript(src);
- result = script.execute(null);
- assertTrue(result instanceof JexlScript);
- script = jexl.createScript("f()", "f");
- result = script.execute(null, result);
- assertEquals(42, result);
- }
-
- @Test void test426d() {
- String src = "let x = 10; let f = () -> { let x = 142; x }; x = 40; f";
- final JexlBuilder builder = new JexlBuilder().features(new JexlFeatures().referenceCapture(true));
- final JexlEngine jexl = builder.create();
- JexlScript script;
- Object result;
- script = jexl.createScript(src);
- result = script.execute(null);
- assertTrue(result instanceof JexlScript);
- script = jexl.createScript("f()", "f");
- result = script.execute(null, result);
- assertEquals(142, result);
- }
-
-
- @Test void test427a() {
- String src = "(x, y, z) -> x && y && z";
- final JexlBuilder builder = new JexlBuilder().features(new JexlFeatures().constCapture(true));
- final JexlEngine jexl = builder.create();
- JexlScript script;
- Object result;
- script = jexl.createScript(src);
- result = script.execute(null, true, "foo", 42);
- assertEquals(42, result);
- result = script.execute(null, true, "", 42);
- assertEquals("", result);
- }
-
- @Test void test427b() {
- OptContext optc = new OptContext();
- String src = "(x, y, z) -> x || y || z";
- final JexlBuilder builder = new JexlBuilder().features(new JexlFeatures().constCapture(true));
- final JexlEngine jexl = builder.create();
- JexlOptions options = builder.options();
- optc.setOptions(options);
- JexlScript script;
- Object result;
- script = jexl.createScript(src);
- result = script.execute(optc, 0, "", 42);
- assertEquals(42, result);
- result = script.execute(optc, true, 42, null);
- assertEquals(true, result);
-
- options.setBooleanLogical(true);
- result = script.execute(optc, 0, "", Double.NaN);
- assertEquals(false, result);
- result = script.execute(optc, 0, "", Collections.emptySet());
- assertEquals(true, result);
-
- }
-
- @Test void test427c() {
- String src = "function sanitize(const n) { n == 0 ? NaN : n }; sanitize(x) && 420 / x";
- final JexlEngine jexl = new JexlBuilder().create();
- JexlScript script;
- Object result;
- script = jexl.createScript(src, "x");
- result = script.execute(null, 10);
- assertEquals(42, result);
- result = script.execute(null, 0);
- assertTrue(Double.isNaN(((Number) result).doubleValue()));
- }
-
- public static class OptContext extends MapContext implements JexlContext.OptionsHandle {
- private JexlOptions options;
-
- @Override
- public JexlOptions getEngineOptions() {
- return options;
- }
-
- void setOptions(JexlOptions options) {
- this.options = options;
- }
- }
}
diff --git a/src/test/java/org/apache/commons/jexl3/JXLTTest.java b/src/test/java/org/apache/commons/jexl3/JXLTTest.java
index 889f6a8b9..39937ee6c 100644
--- a/src/test/java/org/apache/commons/jexl3/JXLTTest.java
+++ b/src/test/java/org/apache/commons/jexl3/JXLTTest.java
@@ -30,9 +30,12 @@
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Method;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import org.apache.commons.jexl3.internal.Debugger;
@@ -44,6 +47,7 @@
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
@@ -138,14 +142,14 @@ public String toString() {
}
};
- public static List engines() {
- final JexlFeatures f = new JexlFeatures();
- f.lexical(true).lexicalShade(true);
- return Arrays.asList(
- new JexlBuilder().silent(false).lexical(true).lexicalShade(true).cache(128).strict(true),
- new JexlBuilder().features(f).silent(false).cache(128).strict(true),
- new JexlBuilder().silent(false).cache(128).strict(true));
- }
+ public static List engines() {
+ final JexlFeatures f = new JexlFeatures();
+ f.lexical(true).lexicalShade(true);
+ return Arrays.asList(
+ new JexlBuilder().silent(false).lexical(true).lexicalShade(true).cache(128).strict(true),
+ new JexlBuilder().features(f).silent(false).cache(128).strict(true),
+ new JexlBuilder().silent(false).cache(128).strict(true));
+ }
private static String refactor(final TemplateDebugger td, final JxltEngine.Template ts) {
final boolean dbg = td.debug(ts);
@@ -210,7 +214,7 @@ private boolean isLexicalShade() {
@BeforeEach
@Override
- public void setUp() throws Exception {
+ public void setUp() {
// ensure jul logging is only error
java.util.logging.Logger.getLogger(org.apache.commons.jexl3.JexlEngine.class.getName()).setLevel(java.util.logging.Level.SEVERE);
context = new JexlEvalContext(vars);
@@ -225,7 +229,7 @@ public void tearDown() throws Exception {
@ParameterizedTest
@MethodSource("engines")
- public void test311a(final JexlBuilder builder) throws Exception {
+ void test311a(final JexlBuilder builder) {
init(builder);
final JexlContext ctx = null;
// @formatter:off
@@ -243,7 +247,7 @@ public void test311a(final JexlBuilder builder) throws Exception {
@ParameterizedTest
@MethodSource("engines")
- public void test311b(final JexlBuilder builder) throws Exception {
+ void test311b(final JexlBuilder builder) {
init(builder);
final JexlContext ctx311 = new Context311();
// @formatter:off
@@ -261,7 +265,7 @@ public void test311b(final JexlBuilder builder) throws Exception {
@ParameterizedTest
@MethodSource("engines")
- public void test311c(final JexlBuilder builder) throws Exception {
+ void test311c(final JexlBuilder builder) {
init(builder);
final Context311 ctx311 = new Context311();
ctx311.newOptions().setLexical(true);
@@ -280,7 +284,7 @@ public void test311c(final JexlBuilder builder) throws Exception {
@ParameterizedTest
@MethodSource("engines")
- public void test311d(final JexlBuilder builder) throws Exception {
+ void test311d(final JexlBuilder builder) {
init(builder);
final Context311 ctx311 = new Context311();
ctx311.newOptions().setLexical(true);
@@ -299,7 +303,7 @@ public void test311d(final JexlBuilder builder) throws Exception {
@ParameterizedTest
@MethodSource("engines")
- public void test311e(final JexlBuilder builder) throws Exception {
+ void test311e(final JexlBuilder builder) {
init(builder);
final Context311 ctx311 = new Context311();
ctx311.newOptions().setLexical(true);
@@ -316,7 +320,7 @@ public void test311e(final JexlBuilder builder) throws Exception {
@ParameterizedTest
@MethodSource("engines")
- public void test311f(final JexlBuilder builder) throws Exception {
+ void test311f(final JexlBuilder builder) {
init(builder);
final Context311 ctx311 = new Context311();
ctx311.newOptions().setLexical(true);
@@ -333,7 +337,7 @@ public void test311f(final JexlBuilder builder) throws Exception {
@ParameterizedTest
@MethodSource("engines")
- public void test311g(final JexlBuilder builder) throws Exception {
+ void test311g(final JexlBuilder builder) {
init(builder);
final Context311 ctx311 = new Context311();
ctx311.newOptions().setLexical(true);
@@ -350,7 +354,7 @@ public void test311g(final JexlBuilder builder) throws Exception {
@ParameterizedTest
@MethodSource("engines")
- public void test311h(final JexlBuilder builder) throws Exception {
+ void test311h(final JexlBuilder builder) {
init(builder);
final Context311 ctx311 = new Context311();
ctx311.newOptions().setLexical(true);
@@ -362,7 +366,7 @@ public void test311h(final JexlBuilder builder) throws Exception {
@ParameterizedTest
@MethodSource("engines")
- public void test311i(final JexlBuilder builder) throws Exception {
+ void test311i(final JexlBuilder builder) {
init(builder);
final JexlContext ctx311 = new Context311();
// @formatter:off
@@ -380,7 +384,7 @@ public void test311i(final JexlBuilder builder) throws Exception {
@ParameterizedTest
@MethodSource("engines")
- public void test315(final JexlBuilder builder) throws Exception {
+ void test315(final JexlBuilder builder) {
init(builder);
String s315;
StringWriter strw;
@@ -411,7 +415,7 @@ public void test315(final JexlBuilder builder) throws Exception {
@ParameterizedTest
@MethodSource("engines")
- public void test42(final JexlBuilder builder) throws Exception {
+ void test42(final JexlBuilder builder) {
init(builder);
// @formatter:off
final String test42
@@ -449,9 +453,141 @@ public void test42(final JexlBuilder builder) throws Exception {
assertEquals(test42, refactored);
}
+
+ @Test
+ void testParseIdentifier() {
+ assertNull(JexlArithmetic.parseIdentifier(null));
+ assertNull(JexlArithmetic.parseIdentifier(""));
+ assertNull(JexlArithmetic.parseIdentifier("za"));
+ assertNull(JexlArithmetic.parseIdentifier("a"));
+ assertNull(JexlArithmetic.parseIdentifier("00"));
+ assertNull(JexlArithmetic.parseIdentifier("01"));
+ assertNull(JexlArithmetic.parseIdentifier("001"));
+ assertNull(JexlArithmetic.parseIdentifier("12345678901"));
+
+ assertEquals(0, JexlArithmetic.parseIdentifier("0"));
+ assertEquals(10, JexlArithmetic.parseIdentifier("10"));
+ assertEquals(100, JexlArithmetic.parseIdentifier("100"));
+ assertEquals(42, JexlArithmetic.parseIdentifier("42"));
+ assertEquals(42000, JexlArithmetic.parseIdentifier("42000"));
+
+ assertEquals(42, JexlArithmetic.parseIdentifier(42));
+ }
+
+ /**
+ * A remediation to strict interpolation that consistently attempts to coerce to integer
+ * index for lists and keys for maps. (see JEXL-425)
+ */
+ public static class Arithmetic425 extends JexlArithmetic {
+ public Arithmetic425(boolean astrict) {
+ super(astrict);
+ }
+
+ public Object propertyGet(List list, String property) {
+ Integer id = JexlArithmetic.parseIdentifier(property);
+ return id == null? JexlEngine.TRY_FAILED : list.get(id);
+ }
+
+ public Object propertySet(List list, String property, Object value) {
+ Integer id = JexlArithmetic.parseIdentifier(property);
+ if (id != null) {
+ return list.set(id, value);
+ }
+ return JexlEngine.TRY_FAILED;
+ }
+
+ public Object propertyGet(Map list, String property) {
+ // determine if keys are integers by performing a check on first one
+ if (list.keySet().stream().findFirst().orElse(null) instanceof Number) {
+ Integer id = JexlArithmetic.parseIdentifier(property);
+ if (id != null) {
+ return list.get(id);
+ }
+ }
+ return JexlEngine.TRY_FAILED;
+ }
+
+ public Object propertySet(Map list, String property, Object value) {
+ // determine if keys are integers by performing a check on first one
+ if (list.keySet().stream().findFirst().orElse(null) instanceof Number) {
+ Integer id = JexlArithmetic.parseIdentifier(property);
+ if (id != null) {
+ list.put(id, value);
+ return value;
+ }
+ }
+ return JexlEngine.TRY_FAILED;
+ }
+ }
+
+ @Test void test425a() {
+ final String S42 = "fourty-two";
+ final JexlBuilder builder = new JexlBuilder().strictInterpolation(true);
+ final JexlEngine jexl = builder.create();
+ JexlScript script;
+ Object result;
+ script = jexl.createScript("let x = 42; let y = `${x}`; y");
+ result = script.execute(null);
+ assertTrue(result instanceof String);
+ assertEquals("42", result);
+ Map