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 ab1a6d9c JEXL-398: implemented trailing comma and ellipsis for array/set/map literals; - Tests, changes and release notes; - A few incidental tweaks here and there (Closure, error reporting); ab1a6d9c is described below commit ab1a6d9c3c32fd9c5e49def7fe8f1763dce09d7d Author: Henri Biestro <hbies...@cloudera.com> AuthorDate: Thu Jul 6 19:13:56 2023 +0200 JEXL-398: implemented trailing comma and ellipsis for array/set/map literals; - Tests, changes and release notes; - A few incidental tweaks here and there (Closure, error reporting); --- BUILDING.txt | 5 +- RELEASE-NOTES.txt | 17 ++++++ src/changes/changes.xml | 10 +++- .../org/apache/commons/jexl3/JexlArithmetic.java | 32 +++++++++-- .../commons/jexl3/internal/ArrayBuilder.java | 19 +++++-- .../org/apache/commons/jexl3/internal/Closure.java | 4 +- .../apache/commons/jexl3/internal/Interpreter.java | 60 ++++++++------------ .../commons/jexl3/internal/InterpreterBase.java | 4 +- .../apache/commons/jexl3/internal/MapBuilder.java | 12 +++- .../apache/commons/jexl3/internal/SetBuilder.java | 12 +++- .../commons/jexl3/parser/ASTArrayLiteral.java | 30 +--------- .../apache/commons/jexl3/parser/ASTMapLiteral.java | 32 +---------- .../apache/commons/jexl3/parser/ASTSetLiteral.java | 29 +--------- .../commons/jexl3/parser/ExtensibleNode.java | 64 ++++++++++++++++++++++ .../org/apache/commons/jexl3/parser/Parser.jjt | 12 +++- .../org/apache/commons/jexl3/ArrayLiteralTest.java | 40 ++++++++++---- .../commons/jexl3/CollectionLiteralTest.java | 24 ++++---- .../org/apache/commons/jexl3/Issues300Test.java | 33 +++++++++++ .../org/apache/commons/jexl3/MapLiteralTest.java | 31 +++++++---- .../org/apache/commons/jexl3/SetLiteralTest.java | 37 +++++++++---- .../org/apache/commons/jexl3/internal/Util.java | 2 +- .../commons/jexl3/introspection/SandboxTest.java | 2 +- .../commons/jexl3/jexl342/OptionalArithmetic.java | 12 ++-- 23 files changed, 322 insertions(+), 201 deletions(-) diff --git a/BUILDING.txt b/BUILDING.txt index 926eacad..3b67478e 100644 --- a/BUILDING.txt +++ b/BUILDING.txt @@ -1,4 +1,4 @@ -JEXL 3.0 uses Maven 3 as the build tool, and requires Java 6 (or later). +JEXL 3.0 uses Maven 3 as the build tool, and requires Java 8 (or later). The following goals may be useful: * mvn clean - clean up @@ -10,5 +10,4 @@ The following goals may be useful: Note that the Maven build process uses JavaCC to generate some sources. These are created under target/generated sources and automatically compiled. The generated class files are needed to compile some of the ordinary Java source files. -This can cause problems for some IDEs, which will need to be configured accordingly. - \ No newline at end of file +This can cause problems for some IDEs, which will need to be configured accordingly. \ No newline at end of file diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 29450e27..a75a5b81 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -18,6 +18,23 @@ Its goal is to expose scripting features usable by technical operatives or consu https://commons.apache.org/jexl/ +======================================================================================================================== +Release 3.2.1 +======================================================================================================================== + +Version 3.3.1 is a maintenance release. + +Compatibility with previous releases +==================================== +Version 3.3.1 is source and binary compatible with 3.3. + +New Features in 3.3.1: +==================== +* JEXL-398: Allow 'trailing commas' or ellipsis while defining array, map and set literals + +Bugs Fixed in 3.3.1: +=================== + ======================================================================================================================== Release 3.3 ======================================================================================================================== diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 251375a1..187a6ed3 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -28,22 +28,26 @@ </properties> <body> <release version="3.3.1" date="20YY-MM-DD"> + <!-- ADD --> + <action dev="henrib" type="add" due-to="Xu Pengcheng"> + Allow 'trailing commas' or ellipsis while defining array, map and set literals + </action> <!-- FIX --> <action dev="ggregory" type="fix" due-to="step-security-bot, Gary Gregory"> [StepSecurity] ci: Harden GitHub Actions #180. </action> <!-- UPDATE --> - <action dev="hbiestro" type="update" due-to="dependabot"> + <action dev="henrib" type="update" due-to="dependabot"> Bumps ossf/scorecard-action from 2.1.3 to 2.2.0. </action> - <action dev="hbiestro" type="update" due-to="dependabot"> + <action dev="henrib" type="update" due-to="dependabot"> Bumps github/codeql-action from 2.1.22 to 2.20.1. </action> <action dev="ggregory" type="update" due-to="Gary Gregory"> Bump commons-parent from 57 to 58. </action> </release> - <release version="3.3" date="2020-03-20"> + <release version="3.3" date="2023-03-20"> <!-- ADD --> <action dev="henrib" type="add" issue="JEXL-392"> Enable namespace declaration based on scripts diff --git a/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java b/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java index 30ff02dc..8c808a02 100644 --- a/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java +++ b/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java @@ -281,11 +281,19 @@ public class JexlArithmetic { * Called by the interpreter when evaluating a literal array. * * @param size the number of elements in the array + * @param extended whether the map is extended or not * @return the array builder */ - public ArrayBuilder arrayBuilder(final int size) { + public ArrayBuilder arrayBuilder(final int size, final boolean extended) { return new org.apache.commons.jexl3.internal.ArrayBuilder(size); } + @Deprecated + /** + * @deprecated since 3.3.1 + */ + public ArrayBuilder arrayBuilder(final int size) { + return arrayBuilder(size, false); + } /** * Helper interface used when creating a set literal. @@ -311,11 +319,19 @@ public class JexlArithmetic { * Called by the interpreter when evaluating a literal set. * * @param size the number of elements in the set + * @param extended whether the set is extended or not * @return the array builder */ - public SetBuilder setBuilder(final int size) { + public SetBuilder setBuilder(final int size, boolean extended) { return new org.apache.commons.jexl3.internal.SetBuilder(size); } + @Deprecated + /** + * @deprecated since 3.3.1 + */ + public SetBuilder setBuilder(final int size) { + return setBuilder(size, false); + } /** * Helper interface used when creating a map literal. @@ -342,12 +358,20 @@ public class JexlArithmetic { * Called by the interpreter when evaluating a literal map. * * @param size the number of elements in the map + * @param extended whether the map is extended or not * @return the map builder */ - public MapBuilder mapBuilder(final int size) { + public MapBuilder mapBuilder(final int size, boolean extended) { return new org.apache.commons.jexl3.internal.MapBuilder(size); } - + @Deprecated + /** + * @deprecated since 3.3.1 + */ + public MapBuilder mapBuilder(final int size) { + return mapBuilder(size, false); + } + /** * Creates a literal range. * <p>The default implementation only accepts integers and longs.</p> diff --git a/src/main/java/org/apache/commons/jexl3/internal/ArrayBuilder.java b/src/main/java/org/apache/commons/jexl3/internal/ArrayBuilder.java index 76188fd4..4a905e77 100644 --- a/src/main/java/org/apache/commons/jexl3/internal/ArrayBuilder.java +++ b/src/main/java/org/apache/commons/jexl3/internal/ArrayBuilder.java @@ -46,7 +46,7 @@ public class ArrayBuilder implements JexlArithmetic.ArrayBuilder { } /** - * Gets the primitive type of a given class (when it exists). + * Gets the primitive type of given class (when it exists). * @param parm a class * @return the primitive type or null it the argument is not unboxable */ @@ -65,13 +65,24 @@ public class ArrayBuilder implements JexlArithmetic.ArrayBuilder { protected final Object[] untyped; /** Number of added items. */ protected int added = 0; + /** Extended? */ + protected final boolean extended; /** * Creates a new builder. * @param size the exact array size */ public ArrayBuilder(final int size) { - untyped = new Object[size]; + this(size, false); + } + /** + * Creates a new builder. + * @param size the exact array size + * @param extended whether the array is extended + */ + public ArrayBuilder(final int size, final boolean extended) { + this.untyped = new Object[size]; + this.extended = extended; } @Override @@ -111,11 +122,11 @@ public class ArrayBuilder implements JexlArithmetic.ArrayBuilder { } @Override - public Object create(final boolean extended) { + public Object create(final boolean e) { if (untyped == null) { return new Object[0]; } - if (extended) { + if (extended || e) { final List<Object> list = new ArrayList<>(added); list.addAll(Arrays.asList(untyped).subList(0, added)); return list; diff --git a/src/main/java/org/apache/commons/jexl3/internal/Closure.java b/src/main/java/org/apache/commons/jexl3/internal/Closure.java index 0c836b94..72cdd8bc 100644 --- a/src/main/java/org/apache/commons/jexl3/internal/Closure.java +++ b/src/main/java/org/apache/commons/jexl3/internal/Closure.java @@ -137,7 +137,7 @@ public class Closure extends Script { public Object execute(final JexlContext context, final Object... args) { final Frame local = frame != null? frame.assign(args) : null; final Interpreter interpreter = createInterpreter(context, local, options); - return interpreter.runClosure(this, null); + return interpreter.runClosure(this); } @Override @@ -146,7 +146,7 @@ public class Closure extends Script { return new Callable(createInterpreter(context, local, options)) { @Override public Object interpret() { - return interpreter.runClosure(Closure.this, null); + return interpreter.runClosure(Closure.this); } }; } 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 817d1f1e..6bc25b60 100644 --- a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java +++ b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java @@ -164,28 +164,6 @@ public class Interpreter extends InterpreterBase { return null; } - /** - * Gets an attribute of an object. - * - * @param object to retrieve value from - * @param attribute the attribute of the object, e.g. an index (1, 0, 2) or key for a map - * @return the attribute value - */ - public Object getAttribute(final Object object, final Object attribute) { - return getAttribute(object, attribute, null); - } - - /** - * Sets an attribute of an object. - * - * @param object to set the value to - * @param attribute the attribute of the object, e.g. an index (1, 0, 2) or key for a map - * @param value the value to assign to the object's attribute - */ - public void setAttribute(final Object object, final Object attribute, final Object value) { - setAttribute(object, attribute, value, null); - } - @Override protected Object visit(final ASTAddNode node, final Object data) { final Object left = node.jjtGetChild(0).jjtAccept(this, data); @@ -923,7 +901,7 @@ public class Interpreter extends InterpreterBase { @Override protected Object visit(final ASTArrayLiteral node, final Object data) { final int childCount = node.jjtGetNumChildren(); - final JexlArithmetic.ArrayBuilder ab = arithmetic.arrayBuilder(childCount); + final JexlArithmetic.ArrayBuilder ab = arithmetic.arrayBuilder(childCount, node.isExtended()); boolean extended = false; for (int i = 0; i < childCount; i++) { cancelCheck(node); @@ -946,11 +924,14 @@ public class Interpreter extends InterpreterBase { @Override protected Object visit(final ASTSetLiteral node, final Object data) { final int childCount = node.jjtGetNumChildren(); - final JexlArithmetic.SetBuilder mb = arithmetic.setBuilder(childCount); + final JexlArithmetic.SetBuilder mb = arithmetic.setBuilder(childCount, node.isExtended()); for (int i = 0; i < childCount; i++) { cancelCheck(node); - final Object entry = node.jjtGetChild(i).jjtAccept(this, data); - mb.add(entry); + final JexlNode child = node.jjtGetChild(i); + if (!(child instanceof ASTExtendedLiteral)) { + final Object entry = child.jjtAccept(this, data); + mb.add(entry); + } } return mb.create(); } @@ -958,11 +939,14 @@ public class Interpreter extends InterpreterBase { @Override protected Object visit(final ASTMapLiteral node, final Object data) { final int childCount = node.jjtGetNumChildren(); - final JexlArithmetic.MapBuilder mb = arithmetic.mapBuilder(childCount); + final JexlArithmetic.MapBuilder mb = arithmetic.mapBuilder(childCount, node.isExtended()); for (int i = 0; i < childCount; i++) { cancelCheck(node); - final Object[] entry = (Object[]) (node.jjtGetChild(i)).jjtAccept(this, data); - mb.put(entry[0], entry[1]); + final JexlNode child = node.jjtGetChild(i); + if (!(child instanceof ASTExtendedLiteral)) { + final Object[] entry = (Object[]) child.jjtAccept(this, data); + mb.put(entry[0], entry[1]); + } } return mb.create(); } @@ -1052,10 +1036,9 @@ public class Interpreter extends InterpreterBase { /** * Runs a closure. * @param closure the closure - * @param data the usual data * @return the closure return value */ - protected Object runClosure(final Closure closure, final Object data) { + protected Object runClosure(final Closure closure) { final ASTJexlScript script = closure.getScript(); // if empty script, nothing to evaluate final int numChildren = script.jjtGetNumChildren(); @@ -1813,13 +1796,16 @@ public class Interpreter extends InterpreterBase { narrow = true; // continue; } - } catch (final JexlException.Method xmethod) { - // ignore and handle at end; treat as an inner discover that fails - } catch (final JexlException.TryFailed xany) { + } + catch (final JexlException.TryFailed xany) { throw invocationException(node, methodName, xany); - } catch (final JexlException xthru) { - throw xthru; - } catch (final Exception xany) { + } + catch (final JexlException xthru) { + if (xthru.getInfo() != null) { + throw xthru; + } + } + catch (final Exception xany) { throw invocationException(node, methodName, xany); } // we have either evaluated and returned or no method was found diff --git a/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java b/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java index 2c3deb4d..42153ced 100644 --- a/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java +++ b/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java @@ -679,7 +679,7 @@ public abstract class InterpreterBase extends ParserVisitor { * <p>When target == context, we are dealing with a global namespace function call * @param target the pseudo-method owner, first to-be argument * @param narrow whether we should attempt to narrow number arguments - * @param args the other (non null) arguments + * @param args the other (non-null) arguments * @return the arguments array */ protected Object[] functionArguments(final Object target, final boolean narrow, final Object[] args) { @@ -708,7 +708,7 @@ public abstract class InterpreterBase extends ParserVisitor { * Concatenate arguments in call(...). * @param target the pseudo-method owner, first to-be argument * @param narrow whether we should attempt to narrow number arguments - * @param args the other (non null) arguments + * @param args the other (non-null) arguments * @return the arguments array */ protected Object[] callArguments(final Object target, final boolean narrow, final Object[] args) { diff --git a/src/main/java/org/apache/commons/jexl3/internal/MapBuilder.java b/src/main/java/org/apache/commons/jexl3/internal/MapBuilder.java index 57527c3c..27fd12d1 100644 --- a/src/main/java/org/apache/commons/jexl3/internal/MapBuilder.java +++ b/src/main/java/org/apache/commons/jexl3/internal/MapBuilder.java @@ -17,6 +17,7 @@ package org.apache.commons.jexl3.internal; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import org.apache.commons.jexl3.JexlArithmetic; @@ -27,12 +28,21 @@ public class MapBuilder implements JexlArithmetic.MapBuilder { /** The map being created. */ protected final Map<Object, Object> map; + /** + * Creates a new builder. + * @param size the expected map size + * @param extended whether the map is extended + */ + public MapBuilder(final int size, final boolean extended) { + map = extended? new LinkedHashMap<>(size) : new HashMap<>(size); + } + /** * Creates a new builder. * @param size the expected map size */ public MapBuilder(final int size) { - map = new HashMap<>(size); + this(size, false); } @Override diff --git a/src/main/java/org/apache/commons/jexl3/internal/SetBuilder.java b/src/main/java/org/apache/commons/jexl3/internal/SetBuilder.java index 649f53e3..0b894c0d 100644 --- a/src/main/java/org/apache/commons/jexl3/internal/SetBuilder.java +++ b/src/main/java/org/apache/commons/jexl3/internal/SetBuilder.java @@ -18,6 +18,7 @@ package org.apache.commons.jexl3.internal; import org.apache.commons.jexl3.JexlArithmetic; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Set; /** @@ -32,7 +33,16 @@ public class SetBuilder implements JexlArithmetic.SetBuilder { * @param size the expected set size */ public SetBuilder(final int size) { - set = new HashSet<>(size); + this(size, false); + } + + /** + * Creates a new builder. + * @param size the expected set size + * @param extended whether the set is extended + */ + public SetBuilder(final int size, final boolean extended) { + set = extended? new LinkedHashSet<>(size) : new HashSet<>(size); } @Override diff --git a/src/main/java/org/apache/commons/jexl3/parser/ASTArrayLiteral.java b/src/main/java/org/apache/commons/jexl3/parser/ASTArrayLiteral.java index e17b6146..2054c131 100644 --- a/src/main/java/org/apache/commons/jexl3/parser/ASTArrayLiteral.java +++ b/src/main/java/org/apache/commons/jexl3/parser/ASTArrayLiteral.java @@ -16,18 +16,14 @@ */ package org.apache.commons.jexl3.parser; -import org.apache.commons.jexl3.internal.Debugger; - /** * An array literal. */ -public final class ASTArrayLiteral extends JexlNode { +public final class ASTArrayLiteral extends ExtensibleNode { /** * */ private static final long serialVersionUID = 1L; - /** Whether this array is constant or not. */ - private boolean constant = false; ASTArrayLiteral(final int id) { super(id); @@ -37,30 +33,6 @@ public final class ASTArrayLiteral extends JexlNode { super(p, id); } - @Override - public String toString() { - final Debugger dbg = new Debugger(); - return dbg.data(this); - } - - @Override - protected boolean isConstant(final boolean literal) { - return constant; - } - - @Override - public void jjtClose() { - constant = true; - for (int c = 0; c < jjtGetNumChildren() && constant; ++c) { - final JexlNode child = jjtGetChild(c); - if (child instanceof ASTReference) { - constant = child.isConstant(true); - } else if (!child.isConstant()) { - constant = false; - } - } - } - @Override public Object jjtAccept(final ParserVisitor visitor, final Object data) { return visitor.visit(this, data); diff --git a/src/main/java/org/apache/commons/jexl3/parser/ASTMapLiteral.java b/src/main/java/org/apache/commons/jexl3/parser/ASTMapLiteral.java index 696c6cc8..11907b8d 100644 --- a/src/main/java/org/apache/commons/jexl3/parser/ASTMapLiteral.java +++ b/src/main/java/org/apache/commons/jexl3/parser/ASTMapLiteral.java @@ -16,16 +16,11 @@ */ package org.apache.commons.jexl3.parser; -import org.apache.commons.jexl3.internal.Debugger; - -public final class ASTMapLiteral extends JexlNode { +public final class ASTMapLiteral extends ExtensibleNode { /** * */ private static final long serialVersionUID = 1L; - /** Whether this array is constant or not. */ - private boolean constant = false; - ASTMapLiteral(final int id) { super(id); } @@ -34,33 +29,8 @@ public final class ASTMapLiteral extends JexlNode { super(p, id); } - @Override - public String toString() { - final Debugger dbg = new Debugger(); - return dbg.data(this); - } - - @Override - protected boolean isConstant(final boolean literal) { - return constant; - } - - @Override - public void jjtClose() { - constant = true; - for (int c = 0; c < jjtGetNumChildren() && constant; ++c) { - final JexlNode child = jjtGetChild(c); - if (child instanceof ASTMapEntry) { - constant = child.isConstant(true); - } else if (!child.isConstant()) { - constant = false; - } - } - } - @Override public Object jjtAccept(final ParserVisitor visitor, final Object data) { return visitor.visit(this, data); } - } diff --git a/src/main/java/org/apache/commons/jexl3/parser/ASTSetLiteral.java b/src/main/java/org/apache/commons/jexl3/parser/ASTSetLiteral.java index 59177c70..ebdda078 100644 --- a/src/main/java/org/apache/commons/jexl3/parser/ASTSetLiteral.java +++ b/src/main/java/org/apache/commons/jexl3/parser/ASTSetLiteral.java @@ -16,15 +16,11 @@ */ package org.apache.commons.jexl3.parser; -import org.apache.commons.jexl3.internal.Debugger; - -public final class ASTSetLiteral extends JexlNode { +public final class ASTSetLiteral extends ExtensibleNode { /** * */ private static final long serialVersionUID = 1L; - /** Whether this set is constant or not. */ - private boolean constant = false; ASTSetLiteral(final int id) { super(id); @@ -34,31 +30,8 @@ public final class ASTSetLiteral extends JexlNode { super(p, id); } - @Override - public String toString() { - final Debugger dbg = new Debugger(); - return dbg.data(this); - } - - @Override - protected boolean isConstant(final boolean literal) { - return constant; - } - - @Override - public void jjtClose() { - constant = true; - for (int c = 0; c < jjtGetNumChildren() && constant; ++c) { - final JexlNode child = jjtGetChild(c); - if (!child.isConstant()) { - constant = false; - } - } - } - @Override public Object jjtAccept(final ParserVisitor visitor, final Object data) { return visitor.visit(this, data); } - } diff --git a/src/main/java/org/apache/commons/jexl3/parser/ExtensibleNode.java b/src/main/java/org/apache/commons/jexl3/parser/ExtensibleNode.java new file mode 100644 index 00000000..a9ce4b75 --- /dev/null +++ b/src/main/java/org/apache/commons/jexl3/parser/ExtensibleNode.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.jexl3.parser; + +import org.apache.commons.jexl3.internal.Debugger; + +/** + * Base node for array/set/map literals. + * <p>Captures constness and extensibility (...)</p> + */ +public class ExtensibleNode extends JexlNode { + /** Whether this array/set/map is constant or not. */ + protected boolean constant = false; + /** Whether this array/set/map is extended or not. */ + private boolean extended = false; + + public ExtensibleNode(int id) { + super(id); + } + + public ExtensibleNode(Parser p, int id) { + super(p, id); + } + + public void setExtended(boolean e) { + this.extended = e; + } + + public boolean isExtended() { + return extended; + } + + @Override + protected boolean isConstant(final boolean literal) { + return constant; + } + + @Override + public String toString() { + final Debugger dbg = new Debugger(); + return dbg.data(this); + } + + @Override + public void jjtClose() { + constant = super.isConstant(true); + } + +} diff --git a/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt b/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt index cb522f8a..655a0612 100644 --- a/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt +++ b/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt @@ -870,7 +870,8 @@ void ArrayLiteral() : {} ( ExtendedLiteral() | - (Expression() (LOOKAHEAD(2) <COMMA> Expression() )*)? (<COMMA> ExtendedLiteral())? + (Expression() (LOOKAHEAD(2) <COMMA> Expression())* + (<COMMA> (ExtendedLiteral() { jjtThis.setExtended(true); })? )? )? ) <RBRACKET> } @@ -879,7 +880,8 @@ void MapLiteral() : {} { <LCURLY> ( - MapEntry() ( <COMMA> MapEntry() )* + MapEntry() (LOOKAHEAD(2) <COMMA> MapEntry() )* + (<COMMA> (ExtendedLiteral(){ jjtThis.setExtended(true); })? )? | <COLON> ) <RCURLY> @@ -892,7 +894,11 @@ void MapEntry() : {} void SetLiteral() : {} { - <LCURLY> (Expression() ( <COMMA> Expression() )*)? <RCURLY> + <LCURLY> + ( + Expression() (LOOKAHEAD(2) <COMMA> Expression())* + (<COMMA> (ExtendedLiteral() { jjtThis.setExtended(true); } )? )? + )? <RCURLY> } diff --git a/src/test/java/org/apache/commons/jexl3/ArrayLiteralTest.java b/src/test/java/org/apache/commons/jexl3/ArrayLiteralTest.java index 0d8c5f0c..43a001f6 100644 --- a/src/test/java/org/apache/commons/jexl3/ArrayLiteralTest.java +++ b/src/test/java/org/apache/commons/jexl3/ArrayLiteralTest.java @@ -42,16 +42,29 @@ public class ArrayLiteralTest extends JexlTestCase { o = JEXL.createExpression("[...]").evaluate(jc); Assert.assertTrue(o instanceof List<?>); Assert.assertEquals(0, ((List<?>) o).size()); + try { + Object ff = JEXL.createExpression("[ , ]"); + Assert.fail(ff.toString()); + } catch(JexlException.Parsing parsing) { + Assert.assertNotNull(parsing); + } + try { + Object ff = JEXL.createExpression("[ ... , ]"); + Assert.fail(ff.toString()); + } catch(JexlException.Parsing parsing) { + Assert.assertNotNull(parsing); + } } @Test public void testLiteralWithStrings() throws Exception { - final JexlExpression e = JEXL.createExpression("[ 'foo' , 'bar' ]"); - final JexlContext jc = new MapContext(); - - final Object o = e.evaluate(jc); final Object[] check = {"foo", "bar"}; - Assert.assertArrayEquals(check, (Object[]) o); + List<String> sources = Arrays.asList("[ 'foo' , 'bar' ]", "[ 'foo' , 'bar', ]"); + for(String src : sources) { + final JexlExpression e = JEXL.createExpression(src); + final Object o = e.evaluate(null); + Assert.assertArrayEquals(check, (Object[]) o); + } } @Test @@ -63,16 +76,23 @@ public class ArrayLiteralTest extends JexlTestCase { final Object[] check = {"foo", "bar"}; Assert.assertEquals(Arrays.asList(check), o); Assert.assertEquals(2, ((List<?>) o).size()); + try { + JEXL.createExpression("[ 'foo' , 'bar', ... , ]"); + Assert.fail("syntax"); + } catch(JexlException.Parsing parsing) { + Assert.assertNotNull(parsing); + } } @Test public void testLiteralWithOneEntry() throws Exception { - final JexlExpression e = JEXL.createExpression("[ 'foo' ]"); - final JexlContext jc = new MapContext(); - - final Object o = e.evaluate(jc); final Object[] check = {"foo"}; - Assert.assertArrayEquals(check, (Object[]) o); + List<String> sources = Arrays.asList("[ 'foo']", "[ 'foo' , ]"); + for(String src : sources) { + final JexlExpression e = JEXL.createExpression(src); + final Object o = e.evaluate(null); + Assert.assertArrayEquals(check, (Object[]) o); + } } @Test diff --git a/src/test/java/org/apache/commons/jexl3/CollectionLiteralTest.java b/src/test/java/org/apache/commons/jexl3/CollectionLiteralTest.java index 06ecc60f..1898d278 100644 --- a/src/test/java/org/apache/commons/jexl3/CollectionLiteralTest.java +++ b/src/test/java/org/apache/commons/jexl3/CollectionLiteralTest.java @@ -50,21 +50,21 @@ public class CollectionLiteralTest extends JexlTestCase { super(strict); } - @Override public MapBuilder mapBuilder(int size) { - return new CountingMapBuilder(maps, size); + @Override public MapBuilder mapBuilder(int size, boolean extended) { + return new CountingMapBuilder(maps, size, extended); } - @Override public SetBuilder setBuilder(int size) { - return new CountingSetBuilder(sets, size); + @Override public SetBuilder setBuilder(int size, boolean extended) { + return new CountingSetBuilder(sets, size, extended); } - @Override public ArrayBuilder arrayBuilder(int size) { - return new CountingArrayBuilder(arrays, size); + @Override public ArrayBuilder arrayBuilder(int size, boolean extended) { + return new CountingArrayBuilder(arrays, size, extended); } } static class CountingSetBuilder extends SetBuilder { final AtomicInteger count; - public CountingSetBuilder(AtomicInteger ai, int size) { - super(size); + public CountingSetBuilder(AtomicInteger ai, int size, boolean extended) { + super(size, extended); count = ai; } @Override public Set<?> create() { @@ -76,8 +76,8 @@ public class CollectionLiteralTest extends JexlTestCase { static class CountingMapBuilder extends MapBuilder { final AtomicInteger count; - public CountingMapBuilder(AtomicInteger ai, int size) { - super(size); + public CountingMapBuilder(AtomicInteger ai, int size, boolean extended) { + super(size, extended); count = ai; } @Override public Map<Object, Object> create() { @@ -90,8 +90,8 @@ public class CollectionLiteralTest extends JexlTestCase { static class CountingArrayBuilder extends ArrayBuilder { final AtomicInteger count; - public CountingArrayBuilder(AtomicInteger ai, int size) { - super(size); + public CountingArrayBuilder(AtomicInteger ai, int size, boolean extended) { + super(size, extended); count = ai; } diff --git a/src/test/java/org/apache/commons/jexl3/Issues300Test.java b/src/test/java/org/apache/commons/jexl3/Issues300Test.java index b44eb879..b1aa8277 100644 --- a/src/test/java/org/apache/commons/jexl3/Issues300Test.java +++ b/src/test/java/org/apache/commons/jexl3/Issues300Test.java @@ -1320,4 +1320,37 @@ public class Issues300Test { result = (String) script.execute(null, proxy); Assert.assertEquals(control, result); } + + @Test + public void testUnsolvableMethod() throws Exception { + final JexlEngine jexl = new JexlBuilder().create(); + JexlScript script = jexl.createScript( + "var myFunction1 = function(object) {" + + " myNonExistentFunction();" + + "}" + + "var myFunction2 = function(object) {" + + " myFunction1();" + + "}" + + "myFunction2();"); + try { + script.execute(new MapContext()); + Assert.fail("myNonExistentFunction() is not solvable"); + } catch (final JexlException.Method unsolvable) { + Assert.assertEquals("myNonExistentFunction", unsolvable.getMethod()); + } + } + + @Test + public void testIssue398() { + String src = "let m = {\n" + + " \"foo\": 1,\n" + + " \"bar\": 2,\n" + + "}"; + final JexlEngine jexl = new JexlBuilder().create(); + JexlScript script = jexl.createScript(src); + Object result = script.execute(null); + Assert.assertTrue(result instanceof Map); + Map<?,?> map = (Map<?, ?>) result; + Assert.assertEquals(2, map.size()); + } } diff --git a/src/test/java/org/apache/commons/jexl3/MapLiteralTest.java b/src/test/java/org/apache/commons/jexl3/MapLiteralTest.java index 3322b58f..1b5291f2 100644 --- a/src/test/java/org/apache/commons/jexl3/MapLiteralTest.java +++ b/src/test/java/org/apache/commons/jexl3/MapLiteralTest.java @@ -16,8 +16,10 @@ */ package org.apache.commons.jexl3; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.junit.Assert; import org.junit.Test; @@ -34,24 +36,31 @@ public class MapLiteralTest extends JexlTestCase { @Test public void testLiteralWithStrings() throws Exception { - final JexlExpression e = JEXL.createExpression("{ 'foo' : 'bar' }"); - final JexlContext jc = new MapContext(); - - final Object o = e.evaluate(jc); - Assert.assertEquals(Collections.singletonMap("foo", "bar"), o); + List<String> sources = Arrays.asList("{ 'foo' : 'bar' }", "{ 'foo' : 'bar', }"); + for(String src : sources) { + final JexlExpression e = JEXL.createExpression(src); + final Object o = e.evaluate(null); + Assert.assertEquals(Collections.singletonMap("foo", "bar"), o); + } + try { + Object ff = JEXL.createExpression("{ : , }"); + Assert.fail(ff.toString()); + } catch(JexlException.Parsing parsing) { + Assert.assertNotNull(parsing); + } } @Test public void testLiteralWithMultipleEntries() throws Exception { - final JexlExpression e = JEXL.createExpression("{ 'foo' : 'bar', 'eat' : 'food' }"); - final JexlContext jc = new MapContext(); - final Map<String, String> expected = new HashMap<>(); expected.put("foo", "bar"); expected.put("eat", "food"); - - final Object o = e.evaluate(jc); - Assert.assertEquals(expected, o); + List<String> sources = Arrays.asList("{ 'foo' : 'bar', 'eat' : 'food' }", "{ 'foo' : 'bar', 'eat' : 'food', }"); + for(String src : sources) { + final JexlExpression e = JEXL.createExpression("{ 'foo' : 'bar', 'eat' : 'food' }"); + final Object o = e.evaluate(null); + Assert.assertEquals(expected, o); + } } @Test diff --git a/src/test/java/org/apache/commons/jexl3/SetLiteralTest.java b/src/test/java/org/apache/commons/jexl3/SetLiteralTest.java index 09993d34..9216df21 100644 --- a/src/test/java/org/apache/commons/jexl3/SetLiteralTest.java +++ b/src/test/java/org/apache/commons/jexl3/SetLiteralTest.java @@ -19,6 +19,7 @@ package org.apache.commons.jexl3; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Set; import org.junit.Assert; import org.junit.Test; @@ -40,22 +41,34 @@ public class SetLiteralTest extends JexlTestCase { @Test public void testSetLiteralWithStrings() throws Exception { - final JexlExpression e = JEXL.createExpression("{ 'foo' , 'bar' }"); - final JexlContext jc = new MapContext(); - - final Object o = e.evaluate(jc); - final Set<?> check = createSet("foo", "bar"); - Assert.assertEquals(check, o); + List<String> sources = Arrays.asList("{ 'foo', 'bar' }", "{ 'foo', 'bar', ... }", "{ 'foo', 'bar', }"); + for(String src : sources) { + final JexlExpression e = JEXL.createExpression(src); + final JexlContext jc = new MapContext(); + + final Object o = e.evaluate(jc); + final Set<?> check = createSet("foo", "bar"); + Assert.assertEquals(check, o); + } + try { + JEXL.createExpression("{ , }"); + Assert.fail("syntax"); + } catch(JexlException.Parsing parsing) { + Assert.assertNotNull(parsing); + } } @Test public void testLiteralWithOneEntry() throws Exception { - final JexlExpression e = JEXL.createExpression("{ 'foo' }"); - final JexlContext jc = new MapContext(); - - final Object o = e.evaluate(jc); - final Set<?> check = createSet("foo"); - Assert.assertEquals(check, o); + List<String> sources = Arrays.asList("{ 'foo' }", "{ 'foo', }"); + for(String src : sources) { + final JexlExpression e = JEXL.createExpression(src); + final JexlContext jc = new MapContext(); + + final Object o = e.evaluate(jc); + final Set<?> check = createSet("foo"); + Assert.assertEquals(check, o); + } } @Test diff --git a/src/test/java/org/apache/commons/jexl3/internal/Util.java b/src/test/java/org/apache/commons/jexl3/internal/Util.java index f49b02f8..1628aab5 100644 --- a/src/test/java/org/apache/commons/jexl3/internal/Util.java +++ b/src/test/java/org/apache/commons/jexl3/internal/Util.java @@ -53,7 +53,7 @@ public class Util { final JexlFeatures features = entry.getKey().getFeatures(); // recreate expr from string try { - final Script exprdbg = jdbg.createScript(features, null, expressiondbg, null); + final Script exprdbg = jdbg.createScript(features, null, expressiondbg); // make arg cause become the root cause JexlNode root = exprdbg.script; while (root.jjtGetParent() != null) { diff --git a/src/test/java/org/apache/commons/jexl3/introspection/SandboxTest.java b/src/test/java/org/apache/commons/jexl3/introspection/SandboxTest.java index 475062c9..93e6bac4 100644 --- a/src/test/java/org/apache/commons/jexl3/introspection/SandboxTest.java +++ b/src/test/java/org/apache/commons/jexl3/introspection/SandboxTest.java @@ -626,7 +626,7 @@ public class SandboxTest extends JexlTestCase { super(astrict); } @Override - public MapBuilder mapBuilder(final int size) { + public MapBuilder mapBuilder(final int size, boolean extended) { return mb; } Map<?,?> getLastMap() { diff --git a/src/test/java/org/apache/commons/jexl3/jexl342/OptionalArithmetic.java b/src/test/java/org/apache/commons/jexl3/jexl342/OptionalArithmetic.java index a3856cdb..d579e4e9 100644 --- a/src/test/java/org/apache/commons/jexl3/jexl342/OptionalArithmetic.java +++ b/src/test/java/org/apache/commons/jexl3/jexl342/OptionalArithmetic.java @@ -234,8 +234,8 @@ public class OptionalArithmetic extends JexlArithmetic { } @Override - public ArrayBuilder arrayBuilder(final int size) { - return new org.apache.commons.jexl3.internal.ArrayBuilder(size) { + public ArrayBuilder arrayBuilder(final int size, final boolean extended) { + return new org.apache.commons.jexl3.internal.ArrayBuilder(size, extended) { @Override public void add(Object value) { super.add(star(value)); @@ -244,8 +244,8 @@ public class OptionalArithmetic extends JexlArithmetic { } @Override - public SetBuilder setBuilder(final int size) { - return new org.apache.commons.jexl3.internal.SetBuilder(size) { + public SetBuilder setBuilder(final int size, final boolean extended) { + return new org.apache.commons.jexl3.internal.SetBuilder(size, extended) { @Override public void add(Object value) { super.add(star(value)); @@ -254,8 +254,8 @@ public class OptionalArithmetic extends JexlArithmetic { } @Override - public MapBuilder mapBuilder(final int size) { - return new org.apache.commons.jexl3.internal.MapBuilder(size) { + public MapBuilder mapBuilder(final int size, final boolean extended) { + return new org.apache.commons.jexl3.internal.MapBuilder(size, extended) { @Override public void put(Object key, Object value) { super.put(key, star(value));