Skip to content

Commit

Permalink
Ensure SpEL ternary and Elvis expressions are enclosed in parentheses…
Browse files Browse the repository at this point in the history
… in toStringAST()

Prior to this commit, ternary and Elvis expressions enclosed in
parentheses (to account for operator precedence) were properly parsed
and evaluated; however, the corresponding toStringAST() implementations
did not enclose the results in parentheses. Consequently, the string
representation of the ASTs did not reflect the original semantics of
such expressions.

For example, given "(4 % 2 == 0 ? 1 : 0) * 10" as the expression to
parse and evaluate, the result of toStringAST() was previously
"(((4 % 2) == 0) ? 1 : 0 * 10)" instead of
"((((4 % 2) == 0) ? 1 : 0) * 10)", implying that 0 should be multiplied
by 10 instead of multiplying the result of the ternary expression by 10.

This commit addresses this by ensuring that SpEL ternary and Elvis
expressions are enclosed in parentheses in toStringAST().

Closes gh-29463
  • Loading branch information
sbrannen committed Nov 11, 2022
1 parent b42b785 commit 27f3fee
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 7 deletions.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -32,6 +32,7 @@
*
* @author Andy Clement
* @author Juergen Hoeller
* @author Sam Brannen
* @since 3.0
*/
public class Elvis extends SpelNodeImpl {
Expand Down Expand Up @@ -64,7 +65,7 @@ public TypedValue getValueInternal(ExpressionState state) throws EvaluationExcep

@Override
public String toStringAST() {
return getChild(0).toStringAST() + " ?: " + getChild(1).toStringAST();
return "(" + getChild(0).toStringAST() + " ?: " + getChild(1).toStringAST() + ")";
}

@Override
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -32,6 +32,7 @@
*
* @author Andy Clement
* @author Juergen Hoeller
* @author Sam Brannen
* @since 3.0
*/
public class Ternary extends SpelNodeImpl {
Expand Down Expand Up @@ -62,7 +63,7 @@ public TypedValue getValueInternal(ExpressionState state) throws EvaluationExcep

@Override
public String toStringAST() {
return getChild(0).toStringAST() + " ? " + getChild(1).toStringAST() + " : " + getChild(2).toStringAST();
return "(" + getChild(0).toStringAST() + " ? " + getChild(1).toStringAST() + " : " + getChild(2).toStringAST() + ")";
}

private void computeExitTypeDescriptor() {
Expand Down
Expand Up @@ -116,6 +116,10 @@ void createObjectsOnAttemptToReferenceNull() {
void elvisOperator() {
evaluate("'Andy'?:'Dave'", "Andy", String.class);
evaluate("null?:'Dave'", "Dave", String.class);
evaluate("3?:1", 3, Integer.class);
evaluate("(2*3)?:1*10", 6, Integer.class);
evaluate("null?:2*10", 20, Integer.class);
evaluate("(null?:1)*10", 10, Integer.class);
}

@Test
Expand Down Expand Up @@ -624,6 +628,24 @@ void ternaryOperator05() {
evaluate("2>4?(3>2?true:false):(5<3?true:false)", false, Boolean.class);
}

@Test
void ternaryOperator06() {
evaluate("3?:#var=5", 3, Integer.class);
evaluate("null?:#var=5", 5, Integer.class);
evaluate("2>4?(3>2?true:false):(5<3?true:false)", false, Boolean.class);
}

@Test
void ternaryExpressionWithImplicitGrouping() {
evaluate("4 % 2 == 0 ? 2 : 3 * 10", 2, Integer.class);
evaluate("4 % 2 == 1 ? 2 : 3 * 10", 30, Integer.class);
}

@Test
void ternaryExpressionWithExplicitGrouping() {
evaluate("((4 % 2 == 0) ? 2 : 1) * 10", 20, Integer.class);
}

@Test
void ternaryOperatorWithNullValue() {
assertThatExceptionOfType(EvaluationException.class).isThrownBy(
Expand Down
Expand Up @@ -162,7 +162,9 @@ void relOperatorsGE02() {

@Test
void elvis() {
parseCheck("3?:1", "3 ?: 1");
parseCheck("3?:1", "(3 ?: 1)");
parseCheck("(2*3)?:1*10", "((2 * 3) ?: (1 * 10))");
parseCheck("((2*3)?:1)*10", "(((2 * 3) ?: 1) * 10)");
}

// void relOperatorsIn01() {
Expand Down Expand Up @@ -404,13 +406,17 @@ void assignmentToVariables01() {

@Test
void ternaryOperator01() {
parseCheck("1>2?3:4", "(1 > 2) ? 3 : 4");
parseCheck("1>2?3:4", "((1 > 2) ? 3 : 4)");
parseCheck("(a ? 1 : 0) * 10", "((a ? 1 : 0) * 10)");
parseCheck("(a?1:0)*10", "((a ? 1 : 0) * 10)");
parseCheck("(4 % 2 == 0 ? 1 : 0) * 10", "((((4 % 2) == 0) ? 1 : 0) * 10)");
parseCheck("((4 % 2 == 0) ? 1 : 0) * 10", "((((4 % 2) == 0) ? 1 : 0) * 10)");
}

@Test
void ternaryOperator02() {
parseCheck("{1}.#isEven(#this) == 'y'?'it is even':'it is odd'",
"({1}.#isEven(#this) == 'y') ? 'it is even' : 'it is odd'");
"(({1}.#isEven(#this) == 'y') ? 'it is even' : 'it is odd')");
}

//
Expand Down

0 comments on commit 27f3fee

Please sign in to comment.