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 2ad56eaf JEXL-440 : clean up grammar; - various nits for next release;
2ad56eaf is described below
commit 2ad56eaf931f3c8c8a14a365d2088b8ea4f61ed3
Author: Henrib <[email protected]>
AuthorDate: Wed Nov 5 14:03:54 2025 -0800
JEXL-440 : clean up grammar;
- various nits for next release;
---
pom.xml | 2 -
.../org/apache/commons/jexl3/JexlFeatures.java | 5 ++
.../apache/commons/jexl3/internal/Debugger.java | 38 +++++++---
.../apache/commons/jexl3/internal/Interpreter.java | 9 +++
.../commons/jexl3/introspection/JexlUberspect.java | 3 +-
.../commons/jexl3/parser/ASTCaseStatement.java | 12 +--
.../commons/jexl3/parser/ASTSwitchExpression.java | 1 +
.../commons/jexl3/parser/ASTSwitchStatement.java | 40 +++++-----
.../apache/commons/jexl3/parser/JexlParser.java | 14 +++-
.../org/apache/commons/jexl3/parser/Parser.jjt | 80 ++++++++++----------
.../org/apache/commons/jexl3/Issues400Test.java | 74 ++++++++----------
.../java/org/apache/commons/jexl3/SwitchTest.java | 87 +++++++++++++++++++++-
12 files changed, 232 insertions(+), 133 deletions(-)
diff --git a/pom.xml b/pom.xml
index 33c6f350..645f1ffa 100644
--- a/pom.xml
+++ b/pom.xml
@@ -296,8 +296,6 @@
<onlyModified>true</onlyModified>
<accessModifier>protected</accessModifier>
<ignoreMissingClasses>true</ignoreMissingClasses>
-
<breakBuildOnBinaryIncompatibleModifications>false</breakBuildOnBinaryIncompatibleModifications>
-
<breakBuildIfCausedByExclusion>false</breakBuildIfCausedByExclusion>
</parameter>
</configuration>
</plugin>
diff --git a/src/main/java/org/apache/commons/jexl3/JexlFeatures.java
b/src/main/java/org/apache/commons/jexl3/JexlFeatures.java
index 9fb0516f..19b78f05 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlFeatures.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlFeatures.java
@@ -127,6 +127,11 @@ public final class JexlFeatures {
public static final int REF_CAPTURE = 24;
/** Ambiguous or strict statement allowed. */
public static final int AMBIGUOUS_STATEMENT = 25;
+ /** Bad naming, use AMBIGUOUS_STATEMENT.
+ * @deprecated 3.6
+ */
+ @Deprecated
+ public static final int STRICT_STATEMENT = 25;
/**
* All features.
* Ensure this is updated if additional features are added.
diff --git a/src/main/java/org/apache/commons/jexl3/internal/Debugger.java
b/src/main/java/org/apache/commons/jexl3/internal/Debugger.java
index adb7683a..c592e5d5 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Debugger.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Debugger.java
@@ -52,7 +52,7 @@ public class Debugger extends ParserVisitor implements
JexlInfo.Detail {
*/
private static boolean isStatement(final JexlNode child) {
if (child instanceof ASTCaseStatement) {
- return isStatement(child.jjtGetChild(0));
+ return child.jjtGetNumChildren() > 0 &&
isStatement(child.jjtGetChild(0));
}
return child instanceof ASTJexlScript
|| child instanceof ASTBlock
@@ -718,8 +718,23 @@ public class Debugger extends ParserVisitor implements
JexlInfo.Detail {
}
@Override
- protected Object visit(final ASTCaseStatement node, final Object data) {
- final List<Object> values = node.getValues();
+ protected Object visit(final ASTSwitchExpression node, Object data) {
+ return visit((ASTSwitchStatement) node, data);
+ }
+
+ @Override
+ protected Object visit(final ASTCaseStatement node, Object data) {
+ JexlNode parent = node.jjtGetParent();
+ boolean isStatement = parent instanceof ASTSwitchStatement &&
((ASTSwitchStatement) parent).isStatement();
+ if (isStatement) {
+ return visitCaseStatement(node, data);
+ } else {
+ return visitCaseExpression(node, data);
+ }
+ }
+
+ private Object visitCaseStatement(ASTCaseStatement node, Object data) {
+ List<Object> values = node.getValues();
if (values.isEmpty()) {
// default case
builder.append("default : ");
@@ -731,23 +746,24 @@ public class Debugger extends ParserVisitor implements
JexlInfo.Detail {
builder.append(" : ");
}
}
- accept(node.jjtGetChild(0), data);
+ if (node.jjtGetNumChildren() > 0) {
+ accept(node.jjtGetChild(0), data);
+ }
return data;
}
@Override
- protected Object visit(final ASTSwitchExpression node, final Object data) {
- return visit((ASTSwitchStatement) node, data);
+ protected Object visit(ASTCaseExpression node, Object data) {
+ return visitCaseExpression(node, data);
}
- @Override
- protected Object visit(final ASTCaseExpression node, final Object data) {
- final List<Object> values = node.getValues();
+ private Object visitCaseExpression(final ASTCaseStatement node, Object
data) {
+ List<Object> values = node.getValues();
if (values.isEmpty()) {
// default case
builder.append("default -> ");
} else {
- builder.append("case -> ");
+ builder.append("case ");
// regular case
boolean first = true;
for (final Object value : values) {
@@ -758,7 +774,7 @@ public class Debugger extends ParserVisitor implements
JexlInfo.Detail {
}
acceptValue(builder, value, true);
}
- builder.append(" : ");
+ builder.append(" -> ");
}
accept(node.jjtGetChild(0), data);
return data;
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 a86411b0..fbe46cc7 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
@@ -1865,6 +1865,12 @@ public class Interpreter extends InterpreterBase {
? node.jjtGetChild(0).jjtAccept(this, data)
: null;
cancelCheck(node);
+ JexlNode parent = node.jjtGetParent();
+ // if return is last child of script, no need to throw
+ if (parent instanceof ASTJexlScript &&
+ parent.jjtGetChild(parent.jjtGetNumChildren() - 1) == node) {
+ return val;
+ }
throw new JexlException.Return(node, null, val);
}
@@ -2031,6 +2037,9 @@ public class Interpreter extends InterpreterBase {
Object value = node.jjtGetChild(0).jjtAccept(this, data);
final int index = node.switchIndex(value);
if (index > 0) {
+ if (!node.isStatement()) {
+ return node.jjtGetChild(index).jjtAccept(this, data);
+ }
for (int i = index; i < count; ++i) {
try {
// evaluate the switch body
diff --git
a/src/main/java/org/apache/commons/jexl3/introspection/JexlUberspect.java
b/src/main/java/org/apache/commons/jexl3/introspection/JexlUberspect.java
index 6ed6ce8d..6895db73 100644
--- a/src/main/java/org/apache/commons/jexl3/introspection/JexlUberspect.java
+++ b/src/main/java/org/apache/commons/jexl3/introspection/JexlUberspect.java
@@ -80,7 +80,7 @@ public interface JexlUberspect {
/**
* A marker interface that solves a simple class name into a fully
qualified one.
* <p>The base implementation uses imports.</p>
- * @since 3.3
+ * @since 3.6
*/
interface ClassNameResolver {
/**
@@ -107,6 +107,7 @@ public interface JexlUberspect {
/**
* The factory type for creating constant resolvers.
+ * @since 3.6
*/
interface ConstantResolverFactory {
/**
diff --git
a/src/main/java/org/apache/commons/jexl3/parser/ASTCaseStatement.java
b/src/main/java/org/apache/commons/jexl3/parser/ASTCaseStatement.java
index 1a87f007..8f8d360d 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/ASTCaseStatement.java
+++ b/src/main/java/org/apache/commons/jexl3/parser/ASTCaseStatement.java
@@ -22,8 +22,6 @@ import java.util.Collections;
import java.util.List;
public class ASTCaseStatement extends JexlNode {
- /** Pointless serial UID */
- private static final long serialVersionUID = 1L;
/** The values of the case statement. */
protected List<Object> values = Collections.emptyList();
@@ -37,15 +35,7 @@ public class ASTCaseStatement extends JexlNode {
return visitor.visit(this, data);
}
- public void setValue(final Object value) {
- if (value == null) {
- this.values = Collections.emptyList();
- } else {
- this.values = Collections.singletonList(value);
- }
- }
-
- public void setValues(final List<Object> values) {
+ void setValues(final List<Object> values) {
if (values == null) {
this.values = Collections.emptyList();
} else if (values.size() == 1) {
diff --git
a/src/main/java/org/apache/commons/jexl3/parser/ASTSwitchExpression.java
b/src/main/java/org/apache/commons/jexl3/parser/ASTSwitchExpression.java
index c7413b89..288ca677 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/ASTSwitchExpression.java
+++ b/src/main/java/org/apache/commons/jexl3/parser/ASTSwitchExpression.java
@@ -20,6 +20,7 @@ package org.apache.commons.jexl3.parser;
public class ASTSwitchExpression extends ASTSwitchStatement {
public ASTSwitchExpression(final int id) {
super(id);
+ isStatement = false;
}
@Override
diff --git
a/src/main/java/org/apache/commons/jexl3/parser/ASTSwitchStatement.java
b/src/main/java/org/apache/commons/jexl3/parser/ASTSwitchStatement.java
index a42b55f3..03d32399 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/ASTSwitchStatement.java
+++ b/src/main/java/org/apache/commons/jexl3/parser/ASTSwitchStatement.java
@@ -23,9 +23,10 @@ import java.util.List;
import java.util.Map;
public class ASTSwitchStatement extends JexlNode {
- /** Pointless serial UID */
- private static final long serialVersionUID = 1L;
-
+ /**
+ * Whether this switch is a statement (true) or an expression (false).
+ */
+ protected boolean isStatement = true;
/**
* The map of cases, where the key is the case value and the value is the
switch index.
*/
@@ -50,8 +51,7 @@ public class ASTSwitchStatement extends JexlNode {
*/
public List<Object>[] getCasesList() {
@SuppressWarnings("unchecked")
- final
- List<Object>[] list = (List<Object>[]) new List[jjtGetNumChildren() -1];
+ final List<Object>[] list = new List[jjtGetNumChildren() -1];
for (final Map.Entry<Object, Integer> entry : cases.entrySet()) {
final int index = entry.getValue();
if (index < 0 || index >= list.length) {
@@ -66,13 +66,8 @@ public class ASTSwitchStatement extends JexlNode {
return list;
}
- @SuppressWarnings("unchecked")
- public void setCases(final Map cases) {
- this.cases = cases == null ? Collections.emptyMap() : (Map<Object,
Integer>) cases;
- }
-
- Map<Object, Integer> getCases() {
- return cases;
+ public boolean isStatement() {
+ return isStatement;
}
public int switchIndex(final Object value) {
@@ -92,29 +87,30 @@ public class ASTSwitchStatement extends JexlNode {
* <p>It detects duplicates cases and default.</p>
*/
public static class Helper {
- private int nswitch = 1; // switch index, starts at 1 since the first
child is the switch expression
+ private int switchIndex = 1; // switch index, starts at 1 since the first
child is the switch expression
private boolean defaultDefined = false;
private final Map<Object, Integer> dispatch = new LinkedHashMap<>();
- void defineCase(final JexlParser.SwitchSet constants) throws
ParseException {
- if (constants.isEmpty()) {
+ void defineCase(final JexlParser.SwitchSet switchSet) throws
ParseException {
+ if (switchSet.isEmpty()) {
if (defaultDefined) {
throw new ParseException("default clause is already defined");
+ } else {
+ defaultDefined = true;
+ dispatch.put(JexlParser.DFLT, switchIndex);
}
- defaultDefined = true;
- dispatch.put(JexlParser.DFLT, nswitch);
} else {
- for (final Object constant : constants) {
- if (dispatch.put(constant == null ? JexlParser.NIL : constant,
nswitch) != null) {
+ for (final Object constant : switchSet) {
+ if (dispatch.put(constant == null ? JexlParser.NIL : constant,
switchIndex) != null) {
throw new ParseException("duplicate case in switch statement for
value: " + constant);
}
}
- constants.clear();
+ switchSet.clear();
}
- nswitch += 1;
+ switchIndex += 1;
}
- void defineSwitch(final ASTSwitchStatement statement) {
+ void defineSwitch(ASTSwitchStatement statement) {
statement.cases = dispatch;
}
}
diff --git a/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java
b/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java
index f3674d07..9f2c85e1 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java
+++ b/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java
@@ -367,16 +367,14 @@ public abstract class JexlParser extends StringParser
implements JexlScriptParse
}
protected class SwitchSet implements Iterable<Object> {
-
private final Set<Object> values = new LinkedHashSet<>();
/**
* Adds a collection of values to the set.
- *
- * @param values the values to add.
+ * @param values the values to add
*/
void addAll(final Collection<Object> values) {
- for (final Object value : values) {
+ for (Object value : values) {
add(value);
}
}
@@ -1079,10 +1077,18 @@ public abstract class JexlParser extends StringParser
implements JexlScriptParse
blockReference.set(unit);
}
+ /**
+ * Escape any outer (parent) loops.
+ * <p>A lambda definition embedded in a for-block escapes that block;
+ * break/continue are not valid within that lambda.</p>
+ */
protected void pushLoop() {
loopCounts.push(loopCount.getAndSet(0));
}
+ /**
+ * Restores the previous loop count.
+ */
protected void popLoop() {
if (!loopCounts.isEmpty()) {
loopCount.set(loopCounts.pop());
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 282e449c..59eee701 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
+++ b/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
@@ -362,7 +362,7 @@ TOKEN_MGR_DECLS : {
> : DEFAULT
}
-<DEFAULT> TOKEN :
+<*> TOKEN :
{
< REGEX_LITERAL:
"~" "/" (~["/","\n","\r","\t","\f","\b","\u2028","\u2029"] | "\\" "/" )*
"/"
@@ -381,7 +381,7 @@ ASTJexlScript JexlScript(Scope frame, JexlFeatures
features) : {
{
pushUnit(jjtThis);
}
- (LOOKAHEAD(<IMPORT>) Import())* ( LOOKAHEAD(<PRAGMA>) Pragma() | {
controlPragmaAnywhere(); } Statement() )* <EOF>
+ ( Import())* ( Pragma() | { controlPragmaAnywhere(); } Statement() )*
<EOF>
{
popUnit(jjtThis);
return jjtThis.script();
@@ -408,37 +408,37 @@ void Annotation() #Annotation :
Token t;
}
{
- t=<ANNOTATION> { jjtThis.setName(t.image); } (LOOKAHEAD(<LPAREN>)
Arguments() )?
+ t=<ANNOTATION> { jjtThis.setName(t.image); } (LOOKAHEAD(2) Arguments() )?
}
void AnnotatedStatement() #AnnotatedStatement : {}
{
- (LOOKAHEAD(<ANNOTATION>) Annotation())+ (LOOKAHEAD(1) Block() |
Statement())
+ (LOOKAHEAD(2) Annotation())+ Statement()
}
void Statement() #void : {}
{
- LOOKAHEAD(<LET>|<CONST>|<VAR>) Var()
+ LOOKAHEAD(3) Var()
|
- LOOKAHEAD(<FUNCTION> <IDENTIFIER>) FunctionStatement()
+ LOOKAHEAD(3) FunctionStatement()
|
- StatementNoVar()
+ StatementSink()
}
-void StatementNoVar() #void : {}
+void StatementSink() #void : {}
{
<SEMICOL>
- | LOOKAHEAD(<ANNOTATION>) AnnotatedStatement()
- | LOOKAHEAD(<IF>) IfStatement()
- | LOOKAHEAD(<FOR>) ForeachStatement()
- | LOOKAHEAD(<WHILE>) WhileStatement()
- | LOOKAHEAD(<DO>) DoWhileStatement()
- | LOOKAHEAD(<RETURN>) ReturnStatement()
- | LOOKAHEAD(<CONTINUE>) Continue()
- | LOOKAHEAD(<BREAK>) Break()
- | LOOKAHEAD(<THROW>) ThrowStatement()
- | LOOKAHEAD(<TRY>) TryStatement()
- | LOOKAHEAD(<SWITCH>) SwitchStatement()
+ | AnnotatedStatement()
+ | IfStatement()
+ | ForeachStatement()
+ | WhileStatement()
+ | DoWhileStatement()
+ | ReturnStatement()
+ | Continue()
+ | Break()
+ | ThrowStatement()
+ | TryStatement()
+ | LOOKAHEAD(3) SwitchStatement()
| LOOKAHEAD(LambdaLookahead()) Lambda()
| LOOKAHEAD(Expression()) ExpressionStatement()
| Block()
@@ -447,7 +447,7 @@ void StatementNoVar() #void : {}
void Block() #Block : {}
{
- <LCURLY> { pushUnit(jjtThis); } ( LOOKAHEAD(<PRAGMA>) Pragma() |
Statement() )* { popUnit(jjtThis); } <RCURLY>
+ <LCURLY> { pushUnit(jjtThis); } ( Pragma() | Statement() )* {
popUnit(jjtThis); } <RCURLY>
}
void FunctionStatement() #JexlLambda : {}
@@ -457,22 +457,22 @@ void FunctionStatement() #JexlLambda : {}
void ExpressionStatement() #void : {}
{
- Expression() (LOOKAHEAD(Expression(), { isAmbiguousStatement(SEMICOL) } )
Expression() #Ambiguous(1))* (LOOKAHEAD(1) <SEMICOL>)*
+ Expression() (LOOKAHEAD(2, { isAmbiguousStatement(SEMICOL) } )
Expression() #Ambiguous(1))* (LOOKAHEAD(1) <SEMICOL>)?
}
void IfStatement() : {}
{
- <IF> <LPAREN> Expression() <RPAREN> (LOOKAHEAD(1) Block() |
StatementNoVar())
- ( LOOKAHEAD(2) <ELSE> <IF> <LPAREN> Expression() <RPAREN> (LOOKAHEAD(1)
Block() | StatementNoVar()) )*
- ( LOOKAHEAD(1) <ELSE> (LOOKAHEAD(1) Block() | StatementNoVar()) )?
+ <IF> <LPAREN> Expression() <RPAREN> (LOOKAHEAD(1) Block() |
StatementSink())
+ ( LOOKAHEAD(2) <ELSE> <IF> <LPAREN> Expression() <RPAREN> (LOOKAHEAD(1)
Block() | StatementSink()) )*
+ ( LOOKAHEAD(1) <ELSE> (LOOKAHEAD(1) Block() | StatementSink()) )?
}
void TryStatement() : {}
{
- <TRY> (LOOKAHEAD(1) TryResources() | Block())
- (LOOKAHEAD(1) <CATCH> { pushUnit(jjtThis); } <LPAREN> InlineVar()
<RPAREN> Block() { jjtThis.catchClause(); popUnit(jjtThis);})?
- (LOOKAHEAD(1) <FINALLY> Block() { jjtThis.finallyClause(); })?
+ <TRY> ( TryResources() | Block())
+ ( <CATCH> { pushUnit(jjtThis); } <LPAREN> InlineVar() <RPAREN> Block() {
jjtThis.catchClause(); popUnit(jjtThis);})?
+ ( <FINALLY> Block() { jjtThis.finallyClause(); } )?
}
void TryResources() : {}
@@ -496,12 +496,12 @@ LOOKAHEAD(2) Var() | Identifier(true)
void WhileStatement() : {}
{
- <WHILE> <LPAREN> Expression() <RPAREN> { loopCount.incrementAndGet(); }
(LOOKAHEAD(1) Block() | StatementNoVar()) { loopCount.decrementAndGet(); }
+ <WHILE> <LPAREN> Expression() <RPAREN> { loopCount.incrementAndGet(); }
(LOOKAHEAD(1) Block() | StatementSink()) { loopCount.decrementAndGet(); }
}
void DoWhileStatement() : {}
{
- <DO> { loopCount.incrementAndGet(); } (LOOKAHEAD(1) Block() |
StatementNoVar()) <WHILE> <LPAREN> Expression() <RPAREN> {
loopCount.decrementAndGet(); }
+ <DO> { loopCount.incrementAndGet(); } (LOOKAHEAD(1) Block() |
StatementSink()) <WHILE> <LPAREN> Expression() <RPAREN> {
loopCount.decrementAndGet(); }
}
void ReturnStatement() : {
@@ -551,7 +551,7 @@ void ForeachStatement() : {
{
loopCount.incrementAndGet();
}
- (LOOKAHEAD(1) Block() | StatementNoVar() )
+ (LOOKAHEAD(1) Block() | StatementSink() )
{
loopCount.decrementAndGet();
jjtThis.setLoopForm(loopForm);
@@ -617,13 +617,19 @@ void SwitchStatement() #SwitchStatement :
}
{
<SWITCH> <LPAREN> Expression() <RPAREN> <LCURLY>
- ( SwitchStatementCase(cases) { helper.defineCase(cases); } )*
+ ( LOOKAHEAD(SwitchStatementLookahead()) ( SwitchStatementCase(cases) {
helper.defineCase(cases); } )*
+ |
+ { jjtThis.isStatement = false; pushLoop(); } (
SwitchExpressionCase(cases) { helper.defineCase(cases); } )*) { popLoop(); }
<RCURLY>
{
helper.defineSwitch(jjtThis);
}
}
+void SwitchStatementLookahead() #void : {}
+{
+ <CASE> constLiteral() <COLON> | <CASE_DEFAULT> <COLON>
+}
void SwitchStatementCase(SwitchSet cases) #CaseStatement :
{
@@ -636,7 +642,7 @@ void SwitchStatementCase(SwitchSet cases) #CaseStatement :
{ constants = Collections.singletonList(constant);
jjtThis.setValues(constants); cases.addAll(constants); } )+
|
<CASE_DEFAULT> <COLON> )
- ( LOOKAHEAD(<LCURLY>) Block() | (StatementNoVar())+ )?
+ ( LOOKAHEAD(<LCURLY>) Block() | (StatementSink() )+ )?
{
loopCount.decrementAndGet();
}
@@ -663,12 +669,10 @@ void SwitchExpressionCase(SwitchSet cases)
#CaseExpression :
List<Object> constants = new ArrayList<Object>(1);
}
{
- ( LOOKAHEAD(2)
- <CASE> ConstLiterals(constants) <LAMBDA> { cases.addAll(constants);
jjtThis.setValues(constants); }
- |
- <CASE_DEFAULT> <LAMBDA>
- )
- ( LOOKAHEAD(3) Block() | Expression() (<SEMICOL>)?)
+ ( LOOKAHEAD(2) <CASE> ConstLiterals(constants) <LAMBDA> {
cases.addAll(constants); jjtThis.setValues(constants); }
+ |
+ <CASE_DEFAULT> <LAMBDA> )
+ ( LOOKAHEAD(3) Block() | Expression() (<SEMICOL>)? )
}
void ConstLiterals(List<Object> constants) #void :
diff --git a/src/test/java/org/apache/commons/jexl3/Issues400Test.java
b/src/test/java/org/apache/commons/jexl3/Issues400Test.java
index 2c415aa4..0c2a89c8 100644
--- a/src/test/java/org/apache/commons/jexl3/Issues400Test.java
+++ b/src/test/java/org/apache/commons/jexl3/Issues400Test.java
@@ -112,7 +112,7 @@ public class Issues400Test {
@Test
void test402() {
final JexlContext jc = new MapContext();
- // @formatter:off
+ // @formatter:off
final String[] sources = {
"if (true) { return }",
"if (true) { 3; return }",
@@ -164,26 +164,26 @@ public class Issues400Test {
final JexlEngine jexl = new
JexlBuilder().cache(64).strict(true).safe(false).create();
Map<String, Object> a = Collections.singletonMap("b", 42);
// access is constant
- for (final String src : new String[] { "a.b", "a?.b", "a['b']",
"a?['b']", "a?.`b`" }) {
+ for (final String src : new String[]{"a.b", "a?.b", "a['b']",
"a?['b']", "a?.`b`"}) {
run404(jexl, src, a);
run404(jexl, src + ";", a);
}
// access is variable
- for (final String src : new String[] { "a[b]", "a?[b]", "a?.`${b}`" })
{
+ for (final String src : new String[]{"a[b]", "a?[b]", "a?.`${b}`"}) {
run404(jexl, src, a, "b");
run404(jexl, src + ";", a, "b");
}
// add a 3rd access
final Map<String, Object> b = Collections.singletonMap("c", 42);
a = Collections.singletonMap("b", b);
- for (final String src : new String[] { "a[b].c", "a?[b]?['c']",
"a?.`${b}`.c" }) {
+ for (final String src : new String[]{"a[b].c", "a?[b]?['c']",
"a?.`${b}`.c"}) {
run404(jexl, src, a, "b");
}
}
@Test
void test404b() {
- // @formatter:off
+ // @formatter:off
final JexlEngine jexl = new JexlBuilder()
.cache(64)
.strict(true)
@@ -213,13 +213,13 @@ public class Issues400Test {
// can still do ternary, note the space between ? and [
script = jexl.createScript("a? ['B']:['C']", "a");
result = script.execute(null, a);
- assertArrayEquals(new String[] { "B" }, (String[]) result);
+ assertArrayEquals(new String[]{"B"}, (String[]) result);
script = jexl.createScript("a?['b'] ?: ['C']", "a");
result = script.execute(null, a);
assertEquals(b, result);
script = jexl.createScript("a?['B'] ?: ['C']", "a");
result = script.execute(null, a);
- assertArrayEquals(new String[] { "C" }, (String[]) result);
+ assertArrayEquals(new String[]{"C"}, (String[]) result);
}
@Test
@@ -252,7 +252,7 @@ public class Issues400Test {
final JexlScript script0 = jexl.createScript(src0, "x");
final String src1 = "join(x, '*')";
final JexlScript script1 = jexl.createScript(src1, "x");
- for (final Object x : Arrays.asList(Arrays.asList(1, 2, 3, 4), new
int[] { 1, 2, 3, 4 })) {
+ for (final Object x : Arrays.asList(Arrays.asList(1, 2, 3, 4), new
int[]{1, 2, 3, 4})) {
Object result = script0.execute(context, x);
assertEquals("1*2*3*4", result, src0);
result = script1.execute(context, x);
@@ -335,8 +335,7 @@ public class Issues400Test {
void test413d() {
final JexlBuilder builder = new JexlBuilder().features(new
JexlFeatures().constCapture(true));
final JexlEngine jexl = builder.create();
- final JexlException.Parsing xparse =
assertThrows(JexlException.Parsing.class, () -> jexl.createScript("var c = 42;
var f = y -> c += y; f(z)", "z"),
- "c should be const");
+ final JexlException.Parsing xparse =
assertThrows(JexlException.Parsing.class, () -> jexl.createScript("var c = 42;
var f = y -> c += y; f(z)", "z"), "c should be const");
assertTrue(xparse.getMessage().contains("const"));
}
@@ -465,18 +464,14 @@ public class Issues400Test {
void test429a() {
final MapContext ctxt = new MapContext();
final JexlFeatures features = JexlFeatures.createDefault();
- final JexlEngine jexl = new JexlBuilder()
- .features(features)
- .safe(false).strict(true).silent(false).create();
+ final JexlEngine jexl = new
JexlBuilder().features(features).safe(false).strict(true).silent(false).create();
final JexlScript f = jexl.createScript("x -> x");
ctxt.set("f", f);
- String src = "#pragma jexl.namespace.b "+Ns429.class.getName() +"\n"
- +"b ? b : f(2);";
+ String src = "#pragma jexl.namespace.b " + Ns429.class.getName() +
"\n" + "b ? b : f(2);";
JexlScript script = jexl.createScript(src, "b");
assertEquals(1, (int) script.execute(ctxt, 1));
- src = "#pragma jexl.namespace.b "+Ns429.class.getName() +"\n"
- +"b ? b:f(2) : 1;";
+ src = "#pragma jexl.namespace.b " + Ns429.class.getName() + "\n" + "b
? b:f(2) : 1;";
script = jexl.createScript(src, "b");
assertEquals(20042, (int) script.execute(ctxt, 1));
}
@@ -487,18 +482,14 @@ public class Issues400Test {
ctxt.set("b", 1);
final JexlFeatures features = JexlFeatures.createDefault();
features.namespaceIdentifier(true);
- final JexlEngine jexl = new JexlBuilder()
- .features(features)
- .safe(false).strict(true).silent(false).create();
+ final JexlEngine jexl = new
JexlBuilder().features(features).safe(false).strict(true).silent(false).create();
final JexlScript f = jexl.createScript("x -> x");
ctxt.set("f", f);
- String src = "#pragma jexl.namespace.b "+Ns429.class.getName() +"\n"
- +"b ? b : f(2);";
+ String src = "#pragma jexl.namespace.b " + Ns429.class.getName() +
"\n" + "b ? b : f(2);";
JexlScript script = jexl.createScript(src);
assertEquals(1, (int) script.execute(ctxt));
- src = "#pragma jexl.namespace.b "+Ns429.class.getName() +"\n"
- +"b ? b:f(2) : 1;";
+ src = "#pragma jexl.namespace.b " + Ns429.class.getName() + "\n" + "b
? b:f(2) : 1;";
script = jexl.createScript(src);
assertEquals(20042, (int) script.execute(ctxt));
}
@@ -534,7 +525,7 @@ public class Issues400Test {
try {
final JexlScript script = jexl.createScript(src);
fail("xx is already defined in scope");
- } catch(final JexlException.Parsing parsing) {
+ } catch (final JexlException.Parsing parsing) {
assertTrue(parsing.getDetail().contains("xx"));
}
}
@@ -567,6 +558,7 @@ public class Issues400Test {
public Arithmetic435(final boolean strict) {
super(strict);
}
+
public Object empty(final String type) {
if ("list".equals(type)) {
return Collections.emptyList();
@@ -588,13 +580,13 @@ public class Issues400Test {
@Test
void test436a() {
- final String[] srcs = {"let i = null; ++i", "let i; ++i;", "let i;
i--;", "let i; i++;"};
+ final String[] srcs = {"let i = null; ++i", "let i; ++i;", "let i;
i--;", "let i; i++;"};
run436(null, srcs);
}
@Test
void test436b() {
- final String[] srcs = {"var i = null; ++i", "var i; ++i;", "var i;
i--;", "var i; i++;"};
+ final String[] srcs = {"var i = null; ++i", "var i; ++i;", "var i;
i--;", "var i; i++;"};
run436(null, srcs);
}
@@ -602,13 +594,13 @@ public class Issues400Test {
void test436c() {
final JexlContext ctxt = new MapContext();
ctxt.set("i", null);
- final String[] srcs = {"++i", "++i;", "i--;", "i++;"};
+ final String[] srcs = {"++i", "++i;", "i--;", "i++;"};
run436(null, srcs);
}
void run436(final JexlContext ctxt, final String[] srcs) {
final JexlEngine jexl = new JexlBuilder().create();
- for(final String src : srcs) {
+ for (final String src : srcs) {
final JexlScript script = jexl.createScript(src);
assertThrows(JexlException.Operator.class, () ->
script.execute(ctxt));
}
@@ -653,8 +645,11 @@ public class Issues400Test {
assertEquals(2, values.size());
}
- /** The set of characters that may be followed by a '='.*/
+ /**
+ * The set of characters that may be followed by a '='.
+ */
static final char[] EQ_FRIEND;
+
static {
final char[] eq = {'!', ':', '<', '>', '^', '|', '&', '+', '-', '/',
'*', '~', '='};
Arrays.sort(eq);
@@ -663,6 +658,7 @@ public class Issues400Test {
/**
* Transcodes a SQL-inspired expression to a JEXL expression.
+ *
* @param expr the expression to transcode
* @return the resulting expression
*/
@@ -735,9 +731,9 @@ public class Issues400Test {
@Test
void testSQLTranspose() {
- final String[] e = { "a<>b", "a = 2", "a.b.c <> '1<>0'" };
- final String[] j = { "a!=b", "a == 2", "a.b.c != '1<>0'" };
- for(int i = 0; i < e.length; ++i) {
+ final String[] e = {"a<>b", "a = 2", "a.b.c <> '1<>0'"};
+ final String[] j = {"a!=b", "a == 2", "a.b.c != '1<>0'"};
+ for (int i = 0; i < e.length; ++i) {
final String je = transcodeSQLExpr(e[i]);
Assertions.assertEquals(j[i], je);
}
@@ -745,7 +741,7 @@ public class Issues400Test {
@Test
void testSQLNoChange() {
- final String[] e = { "a <= 2", "a >= 2", "a := 2", "a + 3 << 4 > 5",
};
+ final String[] e = {"a <= 2", "a >= 2", "a := 2", "a + 3 << 4 > 5",};
for (final String element : e) {
final String je = transcodeSQLExpr(element);
Assertions.assertEquals(element, je);
@@ -754,12 +750,7 @@ public class Issues400Test {
@Test
void test438() {// no local, no lambda, no loops, no-side effects
- final JexlFeatures f = new JexlFeatures()
- .localVar(false)
- .lambda(false)
- .loops(false)
- .sideEffect(false)
- .sideEffectGlobal(false);
+ final JexlFeatures f = new
JexlFeatures().localVar(false).lambda(false).loops(false).sideEffect(false).sideEffectGlobal(false);
final JexlBuilder builder = new
JexlBuilder().parserFactory(SQLParser::new).cache(32).features(f);
final JexlEngine sqle = builder.create();
Assertions.assertTrue((boolean) sqle.createScript("a <> 25",
"a").execute(null, 24));
@@ -811,7 +802,7 @@ public class Issues400Test {
final Object result = script.execute(null, "a", "b");
Assertions.assertEquals("a\n?= ba\n?== b", result);
- final String TEST447 = "src/test/scripts/test447.jexl";
+ final String TEST447 = "src/test/scripts/test447.jexl";
final File src447 = new File(TEST447);
final JexlScript script447 = jexl.createScript(src447);
final Object result447 = script447.execute(null);
@@ -822,5 +813,6 @@ public class Issues400Test {
Assertions.assertTrue(item);
}
}
+
}
diff --git a/src/test/java/org/apache/commons/jexl3/SwitchTest.java
b/src/test/java/org/apache/commons/jexl3/SwitchTest.java
index a66f1cc8..9b220319 100644
--- a/src/test/java/org/apache/commons/jexl3/SwitchTest.java
+++ b/src/test/java/org/apache/commons/jexl3/SwitchTest.java
@@ -21,9 +21,13 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
+import org.apache.commons.jexl3.internal.Debugger;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
+import java.util.Arrays;
+import java.util.List;
+
/**
* Tests switch/case statement and expression.
*/
@@ -47,13 +51,13 @@ public class SwitchTest extends JexlTestCase {
void testBrokenSwitchExpression0() {
final JexlEngine jexl = new
JexlBuilder().safe(false).strict(true).create();
try {
- final JexlScript e = jexl.createScript("var j = switch(i) { case 1 ->
return 2; }; j", "i");
+ jexl.createScript("var j = switch(i) { case 1 -> return 2; }; j", "i");
fail("should not be able to create script with return in switch
expression");
} catch (final JexlException.Parsing xparse) {
assertTrue(xparse.getMessage().contains("return"));
}
try {
- final JexlScript e = jexl.createScript("var j = switch(i) { case 1 ->
break; }; j", "i");
+ jexl.createScript("var j = switch(i) { case 1 -> break; }; j", "i");
fail("should not be able to create script with break in switch
expression");
} catch (final JexlException.Parsing xparse) {
assertTrue(xparse.getMessage().contains("break"));
@@ -104,7 +108,13 @@ public class SwitchTest extends JexlTestCase {
void test440b() {
final JexlEngine jexl = new
JexlBuilder().safe(false).strict(true).create();
final String src =
- "switch (x) { case 10 : return 3; case 20 : case 21 : return 4;
case 32: break; default : return x + 4; } 169";
+ "switch (x) {\n" +
+ " case 10 : return 3\n;" +
+ " case 20 : case 21 : return 4;\n" +
+ " case 32: break; \n" +
+ " default : return x + 4;\n" +
+ " }\n" +
+ " 169";
final JexlScript script = jexl.createScript(src, "x");
assertNotNull(script);
final String dbgStr = script.getParsedText();
@@ -171,4 +181,75 @@ public class SwitchTest extends JexlTestCase {
assertTrue(xjexl.getMessage().contains("switch"));
}
}
+
+ @Test
+ void testSwitchStatement1() {
+ // a one statement script
+ String src = "switch (x) {\n"
+ + " case 1: return 'one';\n"
+ + " case 2: return 'two';\n"
+ + " case 3: return 'three';\n"
+ + " default: return 'many';\n"
+ + "}";
+ runSwitch(src);
+ String src2 = "if (true) { " + src + " }";
+ runSwitch(src2);
+ }
+
+ @Test
+ void testSwitchExpression1() {
+ // a one statement script that uses the expression syntax
+ String src = "switch (x) {\n"
+ + " case 1 -> 'one';\n"
+ + " case 2 -> 'two';\n"
+ + " case 3 -> 'three';\n"
+ + " default -> 'many';\n"
+ + "}";
+ runSwitch(src);
+ String src2 = "if (true) { " + src + " }";
+ runSwitch(src2);
+ }
+
+ void runSwitch(final String src) {
+ JexlEngine jexl = new JexlBuilder().create();
+ JexlScript script = jexl.createScript(src, "x");
+ Object result;
+ result = script.execute(null, 1);
+ Assertions.assertEquals("one", result);
+ result = script.execute(null, 2);
+ Assertions.assertEquals("two", result);
+ result = script.execute(null, 3);
+ Assertions.assertEquals("three", result);
+ result = script.execute(null, 4);
+ Assertions.assertEquals("many", result);
+ result = script.execute(null, 42);
+ Assertions.assertEquals("many", result);
+ final Debugger debugger = new Debugger();
+ assertTrue(debugger.debug(script));
+ final String dbgStr = debugger.toString();
+ assertTrue(JexlTestCase.equalsIgnoreWhiteSpace(src, dbgStr));
+ }
+
+ @Test
+ void testSwitchExpressionFail() {
+ List<String> err = Arrays.asList("break", "continue");
+ for (String keyword : err) {
+ switchExpressionFailKeyword(keyword);
+ }
+ }
+ private void switchExpressionFailKeyword(String keyword) {
+ // a one statement script that uses the expression syntax
+ String src = "switch (x) {\n"
+ + " case 1 -> 'one';\n"
+ + " case 2 -> " + keyword + ";\n"
+ + " default -> 'many';\n"
+ + "}";
+ JexlEngine jexl = new JexlBuilder().create();
+ try {
+ jexl.createScript(src, "x");
+ fail("should not be able to create script with " + keyword + " in switch
expression");
+ } catch (final JexlException.Parsing xparse) {
+ assertTrue(xparse.getMessage().contains(keyword));
+ }
+ }
}