This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 73854db8f2bd CAMEL-24376: Allow boolean-zen operands in Simple logical
expressions
73854db8f2bd is described below
commit 73854db8f2bddf6777263ad7da4ee849e5a23c7b
Author: Omar Atie <[email protected]>
AuthorDate: Wed Aug 12 00:39:19 2026 -0700
CAMEL-24376: Allow boolean-zen operands in Simple logical expressions
Fix SimpleIllegalSyntaxException when boolean-zen shorthand (standalone
${header.foo} or ${exchangeProperty.foo}) is used as an operand of ||
or &&. Accept SimpleFunctionStart as a valid logical operand and wrap it
with matchesValue() in csimple codegen for correct boolean evaluation.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
---
.../camel/language/csimple/CSimpleHelper.java | 4 +
.../language/simple/ast/LogicalExpression.java | 30 +++++-
.../csimple/CSimplePredicateParserTest.java | 9 ++
.../simple/SimplePredicateParserLogicalTest.java | 108 ++++++++++++++++-----
4 files changed, 125 insertions(+), 26 deletions(-)
diff --git
a/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimpleHelper.java
b/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimpleHelper.java
index 21fc2018c579..877841580665 100644
---
a/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimpleHelper.java
+++
b/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimpleHelper.java
@@ -1310,6 +1310,10 @@ public final class CSimpleHelper {
return null;
}
+ public static boolean matchesValue(Exchange exchange, Object value) {
+ return ObjectHelper.evaluateValuePredicate(value);
+ }
+
public static boolean isNot(Exchange exchange, Object value) {
if (value == null) {
return true;
diff --git
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/LogicalExpression.java
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/LogicalExpression.java
index 535dce4fbfa8..bb7f69a016a4 100644
---
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/LogicalExpression.java
+++
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/LogicalExpression.java
@@ -48,7 +48,7 @@ public class LogicalExpression extends BaseSimpleNode {
}
public boolean acceptLeftNode(SimpleNode lef) {
- if (!(lef instanceof BinaryExpression) && !(lef instanceof
LogicalExpression)) {
+ if (!isValidPredicateOperand(lef)) {
return false;
}
this.left = lef;
@@ -56,13 +56,24 @@ public class LogicalExpression extends BaseSimpleNode {
}
public boolean acceptRightNode(SimpleNode right) {
- if (!(right instanceof BinaryExpression) && !(right instanceof
LogicalExpression)) {
+ if (!isValidPredicateOperand(right)) {
return false;
}
this.right = right;
return true;
}
+ /**
+ * Predicate operands for logical AND/OR include binary/logical
expressions as well as boolean-zen shorthand
+ * (standalone functions such as {@code ${header.active}} or {@code
${exchangeProperty.flag}}) that are evaluated
+ * via {@link org.apache.camel.support.ExpressionToPredicateAdapter}.
+ */
+ private static boolean isValidPredicateOperand(SimpleNode node) {
+ return node instanceof BinaryExpression
+ || node instanceof LogicalExpression
+ || node instanceof SimpleFunctionStart;
+ }
+
public LogicalOperatorType getOperator() {
return operator;
}
@@ -137,8 +148,8 @@ public class LogicalExpression extends BaseSimpleNode {
ObjectHelper.notNull(left, "left node", this);
ObjectHelper.notNull(right, "right node", this);
- final String leftExp = left.createCode(camelContext, expression);
- final String rightExp = right.createCode(camelContext, expression);
+ final String leftExp = predicateOperandCode(left, camelContext,
expression);
+ final String rightExp = predicateOperandCode(right, camelContext,
expression);
if (operator == LogicalOperatorType.AND) {
return leftExp + " && " + rightExp;
@@ -148,4 +159,15 @@ public class LogicalExpression extends BaseSimpleNode {
throw new SimpleParserException("Unknown logical operator " +
operator, token.getIndex());
}
+
+ private static String predicateOperandCode(SimpleNode node, CamelContext
camelContext, String expression)
+ throws SimpleParserException {
+ String code = node.createCode(camelContext, expression);
+ code = code.replace(BaseSimpleParser.CODE_START, "");
+ code = code.replace(BaseSimpleParser.CODE_END, "");
+ if (node instanceof SimpleFunctionStart) {
+ return "matchesValue(exchange, " + code + ")";
+ }
+ return code;
+ }
}
diff --git
a/core/camel-core/src/test/java/org/apache/camel/language/csimple/CSimplePredicateParserTest.java
b/core/camel-core/src/test/java/org/apache/camel/language/csimple/CSimplePredicateParserTest.java
index 4e9d3e1ac204..56e4f8f1fa20 100644
---
a/core/camel-core/src/test/java/org/apache/camel/language/csimple/CSimplePredicateParserTest.java
+++
b/core/camel-core/src/test/java/org/apache/camel/language/csimple/CSimplePredicateParserTest.java
@@ -103,4 +103,13 @@ public class CSimplePredicateParserTest {
"isNotEqualTo(exchange, exchangePropertyAs(exchange, \"foo\",
com.foo.User.class).getName(), \"bar\")", code);
}
+ @Test
+ public void testParseBooleanZenLogicalOr() {
+ CSimplePredicateParser parser = new CSimplePredicateParser();
+ String code = parser.parsePredicate("${header.token} == null ||
${exchangeProperty.forceNewSessionToken}");
+ Assertions.assertEquals(
+ "isEqualTo(exchange, header(message, \"token\"), null) ||
matchesValue(exchange, exchangeProperty(exchange, \"forceNewSessionToken\"))",
+ code);
+ }
+
}
diff --git
a/core/camel-core/src/test/java/org/apache/camel/language/simple/SimplePredicateParserLogicalTest.java
b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimplePredicateParserLogicalTest.java
index bd62f2cb1089..f7e630c301cc 100644
---
a/core/camel-core/src/test/java/org/apache/camel/language/simple/SimplePredicateParserLogicalTest.java
+++
b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimplePredicateParserLogicalTest.java
@@ -21,18 +21,17 @@ import org.apache.camel.Predicate;
import org.apache.camel.language.simple.types.SimpleIllegalSyntaxException;
import org.junit.jupiter.api.Test;
-import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* Regression tests for prepareLogicalExpressions in SimplePredicateParser.
Covers the bug where the right-hand token
* was reported as the left-hand token in the "does not support right hand
side token" error message.
*/
-public class SimplePredicateParserLogicalTest extends ExchangeTestSupport {
+class SimplePredicateParserLogicalTest extends ExchangeTestSupport {
@Test
- public void testAndWithFunctionRightHandSide() {
+ void testAndWithFunctionRightHandSide() {
exchange.getIn().setBody("hello");
exchange.getIn().setHeader("active", true);
@@ -41,11 +40,78 @@ public class SimplePredicateParserLogicalTest extends
ExchangeTestSupport {
Predicate predicate = parser.parsePredicate();
predicate.init(context);
- assertTrue(predicate.matches(exchange));
+ assertThat(predicate.matches(exchange)).isTrue();
}
@Test
- public void testAndWithLiteralRightHandSide() {
+ void testOrWithBooleanZenRightHandSide() {
+ // CAMEL-24376: boolean-zen shorthand must work as the right operand
of ||
+ exchange.getIn().setHeader("token", null);
+ exchange.setProperty("forceNewSessionToken", true);
+
+ SimplePredicateParser parser = new SimplePredicateParser(
+ context, "${header.token} == null ||
${exchangeProperty.forceNewSessionToken}", true, null);
+ Predicate predicate = parser.parsePredicate();
+ predicate.init(context);
+
+ assertThat(predicate.matches(exchange)).isTrue();
+ }
+
+ @Test
+ void testOrWithBooleanZenLeftHandSide() {
+ exchange.getIn().setHeader("active", true);
+ exchange.getIn().setBody("other");
+
+ SimplePredicateParser parser = new SimplePredicateParser(
+ context, "${header.active} || ${body} == 'hello'", true, null);
+ Predicate predicate = parser.parsePredicate();
+ predicate.init(context);
+
+ assertThat(predicate.matches(exchange)).isTrue();
+ }
+
+ @Test
+ void testAndWithBooleanZenOperands() {
+ exchange.getIn().setHeader("enabled", true);
+ exchange.setProperty("ready", true);
+
+ SimplePredicateParser parser = new SimplePredicateParser(
+ context, "${header.enabled} && ${exchangeProperty.ready}",
true, null);
+ Predicate predicate = parser.parsePredicate();
+ predicate.init(context);
+
+ assertThat(predicate.matches(exchange)).isTrue();
+
+ exchange.setProperty("ready", false);
+ assertThat(predicate.matches(exchange)).isFalse();
+ }
+
+ @Test
+ void testOrWithBooleanZenFalseWhenBothOperandsFalse() {
+ exchange.getIn().setHeader("token", "abc");
+ exchange.setProperty("forceNewSessionToken", false);
+
+ SimplePredicateParser parser = new SimplePredicateParser(
+ context, "${header.token} == null ||
${exchangeProperty.forceNewSessionToken}", true, null);
+ Predicate predicate = parser.parsePredicate();
+ predicate.init(context);
+
+ assertThat(predicate.matches(exchange)).isFalse();
+ }
+
+ @Test
+ void testStandaloneBooleanZenStillWorks() {
+ exchange.getIn().setHeader("foo", "yes");
+
+ SimplePredicateParser parser = new SimplePredicateParser(context,
"${header.foo}", true, null);
+ Predicate predicate = parser.parsePredicate();
+ predicate.init(context);
+
+ assertThat(predicate.matches(exchange)).isTrue();
+ }
+
+ @Test
+ void testAndWithLiteralRightHandSide() {
exchange.getIn().setBody("foo");
SimplePredicateParser parser = new SimplePredicateParser(
@@ -53,11 +119,11 @@ public class SimplePredicateParserLogicalTest extends
ExchangeTestSupport {
Predicate predicate = parser.parsePredicate();
predicate.init(context);
- assertTrue(predicate.matches(exchange));
+ assertThat(predicate.matches(exchange)).isTrue();
}
@Test
- public void testOrWithFunctionRightHandSide() {
+ void testOrWithFunctionRightHandSide() {
exchange.getIn().setBody("hello");
exchange.getIn().setHeader("score", 5);
@@ -66,11 +132,11 @@ public class SimplePredicateParserLogicalTest extends
ExchangeTestSupport {
Predicate predicate = parser.parsePredicate();
predicate.init(context);
- assertTrue(predicate.matches(exchange));
+ assertThat(predicate.matches(exchange)).isTrue();
}
@Test
- public void testOrAllFalse() {
+ void testOrAllFalse() {
exchange.getIn().setBody("hello");
exchange.getIn().setHeader("score", 1);
@@ -79,11 +145,11 @@ public class SimplePredicateParserLogicalTest extends
ExchangeTestSupport {
Predicate predicate = parser.parsePredicate();
predicate.init(context);
- assertFalse(predicate.matches(exchange));
+ assertThat(predicate.matches(exchange)).isFalse();
}
@Test
- public void testAndWithNumericRightHandSide() {
+ void testAndWithNumericRightHandSide() {
exchange.getIn().setBody(42);
SimplePredicateParser parser = new SimplePredicateParser(
@@ -91,11 +157,11 @@ public class SimplePredicateParserLogicalTest extends
ExchangeTestSupport {
Predicate predicate = parser.parsePredicate();
predicate.init(context);
- assertTrue(predicate.matches(exchange));
+ assertThat(predicate.matches(exchange)).isTrue();
}
@Test
- public void testAndWithNullRightHandSide() {
+ void testAndWithNullRightHandSide() {
exchange.getIn().setBody("present");
exchange.getIn().setHeader("tag", "x");
@@ -104,11 +170,11 @@ public class SimplePredicateParserLogicalTest extends
ExchangeTestSupport {
Predicate predicate = parser.parsePredicate();
predicate.init(context);
- assertTrue(predicate.matches(exchange));
+ assertThat(predicate.matches(exchange)).isTrue();
}
@Test
- public void testChainedAndOrLogicalOperators() {
+ void testChainedAndOrLogicalOperators() {
exchange.getIn().setBody("alpha");
exchange.getIn().setHeader("flag", true);
@@ -117,20 +183,18 @@ public class SimplePredicateParserLogicalTest extends
ExchangeTestSupport {
Predicate predicate = parser.parsePredicate();
predicate.init(context);
- assertTrue(predicate.matches(exchange));
+ assertThat(predicate.matches(exchange)).isTrue();
}
@Test
- public void testInvalidRightHandSideReportsRightToken() {
+ void testInvalidRightHandSideReportsRightToken() {
// "&&" followed by a bare numeric (not a binary expression) is
invalid syntax.
// The error message must say "right hand side token 42" (the actual
offending token),
// not "right hand side token ==" (which would indicate the left-hand
node was reported).
SimplePredicateParser parser = new SimplePredicateParser(
context, "${body} == 'foo' && 42", true, null);
SimpleIllegalSyntaxException ex =
assertThrows(SimpleIllegalSyntaxException.class, parser::parsePredicate);
- assertTrue(ex.getMessage().contains("right hand side token 42"),
- "Error message should say 'right hand side token 42', but was:
" + ex.getMessage());
- assertFalse(ex.getMessage().contains("right hand side token =="),
- "Error message must not say 'right hand side token ==', but
was: " + ex.getMessage());
+ assertThat(ex.getMessage()).contains("right hand side token 42");
+ assertThat(ex.getMessage()).doesNotContain("right hand side token ==");
}
}