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 8abefd3634ce CAMEL-24920: simple - the ternary takes a compound
condition (#26764)
8abefd3634ce is described below
commit 8abefd3634cecaacb353b34d10f5a447ed479c89
Author: Claus Ibsen <[email protected]>
AuthorDate: Wed Sep 23 13:32:39 2026 +0200
CAMEL-24920: simple - the ternary takes a compound condition (#26764)
${a > 0 && b < 10 ? 'x' : 'y'} was refused, so a ternary could only test a
single comparison. The condition of a ternary inside ${ } is turned into a
predicate by wrapping its functions: the wrapping found the first comparison
operator and left the rest untouched, so everything after the first && kept a
bare function name. It now splits the condition on && and || outside quotes and
wraps each comparison. A quoted operator is still text, and the word forms
and/or are still refused.
---
.../camel/catalog/docs/simple-operators.adoc | 7 ++++
.../modules/languages/pages/simple-operators.adoc | 7 ++++
.../language/simple/ast/SimpleFunctionStart.java | 41 ++++++++++++++++++++++
.../camel/language/simple/SimpleOperatorTest.java | 31 ++++++++++++++++
4 files changed, 86 insertions(+)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/simple-operators.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/simple-operators.adoc
index e5354e79f7db..7ce0e007b06b 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/simple-operators.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/simple-operators.adoc
@@ -128,6 +128,13 @@ The syntax for the ternary operator is:
${leftValue OP rightValue ? trueValue : falseValue}
----
+The condition may be several comparisons joined by `&&` or `||`:
+
+[source,text]
+----
+${header.age > 18 && header.country == 'DK' ? 'yes' : 'no'}
+----
+
The whole ternary must be inside a single `${ }`. Outside `${ }` a Simple
expression is a template,
so a `?` and a `:` there are literal text and the operator is not evaluated.
For example:
diff --git
a/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-operators.adoc
b/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-operators.adoc
index e5354e79f7db..7ce0e007b06b 100644
---
a/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-operators.adoc
+++
b/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-operators.adoc
@@ -128,6 +128,13 @@ The syntax for the ternary operator is:
${leftValue OP rightValue ? trueValue : falseValue}
----
+The condition may be several comparisons joined by `&&` or `||`:
+
+[source,text]
+----
+${header.age > 18 && header.country == 'DK' ? 'yes' : 'no'}
+----
+
The whole ternary must be inside a single `${ }`. Outside `${ }` a Simple
expression is a template,
so a `?` and a `:` there are literal text and the operator is not evaluated.
For example:
diff --git
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionStart.java
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionStart.java
index 523718ec70a7..f1052ca34d4c 100644
---
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionStart.java
+++
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionStart.java
@@ -333,6 +333,47 @@ public class SimpleFunctionStart extends BaseSimpleNode
implements BlockStart {
return conditionText;
}
+ // the condition may be several comparisons joined by a logical
operator, and each of them needs its
+ // functions wrapped, not only the first (CAMEL-24920)
+ StringBuilder answer = new StringBuilder();
+ int from = 0;
+ for (int at = logicalOperator(conditionText, 0); at >= 0; at =
logicalOperator(conditionText, from)) {
+ // the operator matched with its trailing space, so the next space
is at most two characters away
+ int end = conditionText.indexOf(' ', at + 1);
+ answer.append(wrapComparison(conditionText.substring(from,
at).trim()));
+ answer.append(' ').append(conditionText, at, end).append(' ');
+ from = end + 1;
+ }
+ answer.append(wrapComparison(conditionText.substring(from).trim()));
+ return answer.toString();
+ }
+
+ /**
+ * The index of the next logical operator ({@code &&} or {@code ||})
outside quotes, or -1. Simple has no word
+ * forms: {@code and} and {@code or} are refused by the parser with a
message that says so.
+ */
+ private static int logicalOperator(String text, int from) {
+ boolean single = false;
+ boolean dubble = false;
+ for (int i = from; i < text.length(); i++) {
+ char c = text.charAt(i);
+ if (c == '\'' && !dubble) {
+ single = !single;
+ } else if (c == '"' && !single) {
+ dubble = !dubble;
+ } else if (!single && !dubble && c == ' ') {
+ for (String op : new String[] { "&& ", "|| " }) {
+ if (text.startsWith(op, i + 1)) {
+ return i + 1;
+ }
+ }
+ }
+ }
+ return -1;
+ }
+
+ /** Wraps the left hand side of one comparison with ${} when it is a
function reference. */
+ private String wrapComparison(String conditionText) {
// Find the operator in the condition
String[] operators = {
" >= ", " <= ", " > ", " < ", " == ", " != ", " =~ ", " !=~ ",
diff --git
a/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleOperatorTest.java
b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleOperatorTest.java
index a054c868f158..a20eb732c9a2 100644
---
a/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleOperatorTest.java
+++
b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleOperatorTest.java
@@ -942,6 +942,37 @@ public class SimpleOperatorTest extends
LanguageTestSupport {
assertExpression("${body != true ? 'A' : 'B'}", "A");
}
+ @Test
+ public void testTernaryWithCompoundCondition() {
+ // CAMEL-24920: the condition of a ternary may be more than one
comparison
+ exchange.getIn().setBody(5);
+ assertExpression("${body > 0 && body < 10 ? 'in' : 'out'}", "in");
+ assertExpression("${body > 0 || body > 100 ? 'in' : 'out'}", "in");
+ assertExpression("${body > 0 && body < 10 && body != 7 ? 'in' :
'out'}", "in");
+
+ exchange.getIn().setBody(50);
+ assertExpression("${body > 0 && body < 10 ? 'in' : 'out'}", "out");
+ assertExpression("${body > 0 || body > 100 ? 'in' : 'out'}", "in");
+
+ exchange.getIn().setBody("Hello");
+ assertExpression("${body != null && body contains 'ell' ? 'yes' :
'no'}", "yes");
+ assertExpression("${body != null && body contains 'xxx' ? 'yes' :
'no'}", "no");
+ }
+
+ @Test
+ public void testTernaryWithCompoundConditionCornerCases() {
+ exchange.getIn().setBody(5);
+ // a quoted value that holds the operator text is not a logical
operator
+ assertExpression("${body > 0 && body < 10 ? 'in && out' : 'no'}", "in
&& out");
+ exchange.getIn().setBody("a && b");
+ assertExpression("${body contains 'a && b' ? 'yes' : 'no'}", "yes");
+ // simple has no word forms, and says so, as it does outside a ternary
+ exchange.getIn().setBody(5);
+ Exception e = assertThrows(Exception.class,
+ () ->
context.resolveLanguage("simple").createExpression("${body > 0 and body < 10 ?
'in' : 'out'}"));
+ assertTrue(e.getMessage().contains("use && for and"), e.getMessage());
+ }
+
@Test
public void testTernaryValueForms() {
// a value form the ternary accepts must be used as that value and not
looked up as a function,