This is an automated email from the ASF dual-hosted git repository.

gnodet 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 8089af56cf44 CAMEL-22873: Add ternary operator support to Simple 
language (#20936)
8089af56cf44 is described below

commit 8089af56cf4451edcd96ca46ab0a46c96647271a
Author: Guillaume Nodet <[email protected]>
AuthorDate: Fri Jan 23 10:56:12 2026 +0100

    CAMEL-22873: Add ternary operator support to Simple language (#20936)
    
    Add support for the ternary operator (condition ? trueValue : falseValue) 
in Apache Camel's Simple language, providing a more familiar syntax alongside 
the existing iif function.
---
 .../language/csimple/joor/OriginalSimpleTest.java  |  85 ++++
 .../modules/languages/pages/csimple-language.adoc  |   4 +-
 .../modules/languages/pages/simple-language.adoc   |  22 +
 .../camel/language/csimple/CSimpleHelper.java      |  12 +
 .../camel/language/simple/BaseSimpleParser.java    |  77 ++++
 .../language/simple/SimpleExpressionParser.java    |  41 +-
 .../language/simple/SimplePredicateParser.java     |  33 ++
 .../camel/language/simple/SimpleTokenizer.java     |  22 +-
 .../language/simple/ast/SimpleFunctionStart.java   | 501 ++++++++++++++++++++-
 .../language/simple/ast/TernaryExpression.java     | 156 +++++++
 .../language/simple/types/SimpleTokenType.java     |   7 +
 .../{TokenType.java => TernaryOperatorType.java}   |  45 +-
 .../camel/language/simple/types/TokenType.java     |   1 +
 .../apache/camel/language/simple/SimpleTest.java   |  85 ++++
 14 files changed, 1060 insertions(+), 31 deletions(-)

diff --git 
a/components/camel-csimple-joor/src/test/java/org/apache/camel/language/csimple/joor/OriginalSimpleTest.java
 
b/components/camel-csimple-joor/src/test/java/org/apache/camel/language/csimple/joor/OriginalSimpleTest.java
index 1a76affa12bb..a223b8f9d1a8 100644
--- 
a/components/camel-csimple-joor/src/test/java/org/apache/camel/language/csimple/joor/OriginalSimpleTest.java
+++ 
b/components/camel-csimple-joor/src/test/java/org/apache/camel/language/csimple/joor/OriginalSimpleTest.java
@@ -2845,6 +2845,91 @@ public class OriginalSimpleTest extends 
LanguageTestSupport {
         assertEquals(1, exchange.getVariables().size());
     }
 
+    @Test
+    public void testTernaryOperator() {
+        // Test that the same expression object evaluates correctly with 
different header values
+        exchange.getIn().setHeader("foo", 44);
+        Expression exp = 
context.resolveLanguage("csimple").createExpression("${header.foo > 0 ? 
'positive' : 'negative'}");
+        assertEquals("positive", exp.evaluate(exchange, String.class), "First 
evaluation with foo=44");
+
+        exchange.getIn().setHeader("foo", -123);
+        assertEquals("negative", exp.evaluate(exchange, String.class), "Second 
evaluation with foo=-123");
+
+        // Test a simple ternary with a constant condition
+        Expression expTrue = 
context.resolveLanguage("csimple").createExpression("${true ? 'yes' : 'no'}");
+        assertEquals("yes", expTrue.evaluate(exchange, String.class), 
"Constant true ternary");
+
+        Expression expFalse = 
context.resolveLanguage("csimple").createExpression("${false ? 'yes' : 'no'}");
+        assertEquals("no", expFalse.evaluate(exchange, String.class), 
"Constant false ternary");
+
+        // Test with body
+        exchange.getIn().setBody("Hello World");
+        exchange.getIn().setHeader("foo", 44);
+        assertExpression("${header.foo > 0 ? ${body} : 'Bye World'}", "Hello 
World");
+        exchange.getIn().setHeader("foo", -123);
+        assertExpression("${header.foo > 0 ? ${body} : 'Bye World'}", "Bye 
World");
+        assertExpression("${header.foo > 0 ? ${body} : ${null}}", null);
+
+        // Test with file name
+        exchange.getIn().setHeader("CamelFileName", "testfile.txt");
+        assertExpression("${file:name startsWith 'test' ? 'foo' : 'bar'}", 
"foo");
+        exchange.getIn().setHeader("CamelFileName", "dummy.txt");
+        assertExpression("${file:name startsWith 'test' ? 'foo' : 'bar'}", 
"bar");
+    }
+
+    @Test
+    public void testTernaryOperatorWithNumbers() {
+        exchange.getIn().setHeader("score", 85);
+        assertExpression("${header.score >= 90 ? 'A' : 'B'}", "B");
+        exchange.getIn().setHeader("score", 95);
+        assertExpression("${header.score >= 90 ? 'A' : 'B'}", "A");
+
+        exchange.getIn().setHeader("age", 25);
+        assertExpression("${header.age >= 18 ? 'adult' : 'minor'}", "adult");
+        exchange.getIn().setHeader("age", 15);
+        assertExpression("${header.age >= 18 ? 'adult' : 'minor'}", "minor");
+    }
+
+    @Test
+    public void testTernaryOperatorWithBooleans() {
+        exchange.getIn().setHeader("enabled", true);
+        assertExpression("${header.enabled == true ? 'yes' : 'no'}", "yes");
+        exchange.getIn().setHeader("enabled", false);
+        assertExpression("${header.enabled == true ? 'yes' : 'no'}", "no");
+    }
+
+    @Test
+    public void testTernaryOperatorWithNull() {
+        exchange.getIn().setHeader("value", null);
+        assertExpression("${header.value == null ? 'empty' : 'full'}", 
"empty");
+        exchange.getIn().setHeader("value", "something");
+        assertExpression("${header.value == null ? 'empty' : 'full'}", "full");
+    }
+
+    @Test
+    public void testTernaryOperatorNested() {
+        // Nested ternary operators
+        exchange.getIn().setHeader("score", 95);
+        assertExpression("${header.score >= 90 ? 'A' : ${header.score} >= 80 ? 
'B' : 'C'}", "A");
+        exchange.getIn().setHeader("score", 85);
+        assertExpression("${header.score >= 90 ? 'A' : ${header.score} >= 80 ? 
'B' : 'C'}", "B");
+        exchange.getIn().setHeader("score", 75);
+        assertExpression("${header.score >= 90 ? 'A' : ${header.score} >= 80 ? 
'B' : 'C'}", "C");
+    }
+
+    @Test
+    public void testTernaryOperatorWithStrings() {
+        exchange.getIn().setBody("Hello");
+        assertExpression("${body == 'Hello' ? 'greeting' : 'other'}", 
"greeting");
+        exchange.getIn().setBody("Goodbye");
+        assertExpression("${body == 'Hello' ? 'greeting' : 'other'}", "other");
+
+        exchange.getIn().setHeader("name", "John");
+        assertExpression("${header.name contains 'John' ? 'found' : 'not 
found'}", "found");
+        exchange.getIn().setHeader("name", "Jane");
+        assertExpression("${header.name contains 'John' ? 'found' : 'not 
found'}", "not found");
+    }
+
     @Test
     public void testAbs() {
         exchange.getMessage().setBody("-987");
diff --git 
a/core/camel-core-languages/src/main/docs/modules/languages/pages/csimple-language.adoc
 
b/core/camel-core-languages/src/main/docs/modules/languages/pages/csimple-language.adoc
index 1a1326360161..4cbc0df85c42 100644
--- 
a/core/camel-core-languages/src/main/docs/modules/languages/pages/csimple-language.adoc
+++ 
b/core/camel-core-languages/src/main/docs/modules/languages/pages/csimple-language.adoc
@@ -187,7 +187,9 @@ include::partial$language-options.adoc[]
 Currently, the csimple language does **not** support:
 
 - nested functions (aka functions inside functions)
-- the _null safe_ operator (`?`).
+- the _null safe_ operator (`?.`).
+
+NOTE: The ternary operator (`? :`) IS supported in csimple. The limitation 
above refers only to the null-safe operator used in OGNL expressions (e.g., 
`${body?.address}`).
 
 For example the following scripts cannot compile:
 
diff --git 
a/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-language.adoc
 
b/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-language.adoc
index 6df8b7ea9ded..bcb8c13cb006 100644
--- 
a/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-language.adoc
+++ 
b/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-language.adoc
@@ -413,9 +413,31 @@ And the following other operators can be used:
 [width="100%",cols="50%,50%",options="header",]
 |====
 |Operator |Description
+|`? :` | The ternary operator evaluates a condition and returns a value based 
on the result. If the condition is true, the first value (after `?`) is 
returned; otherwise, the second value (after `:`) is returned. There must be 
spaces around both `?` and `:` operators. This is similar to the ternary 
operator in Java.
 |`?:` | The elvis operator returns the left-hand side if it has an effective 
Boolean value of true, otherwise it returns the right-hand side. This is useful 
for providing fallback values when an expression may evaluate to a value with 
an effective Boolean value of false (such as `null`, `false`, `0`, empty/blank 
string).
 |====
 
+The syntax for the ternary operator is:
+
+[source,text]
+----
+${leftValue} OP rightValue ? trueValue : falseValue
+----
+
+For example the following ternary operator will return `positive` if header 
`foo` is greater than 0, otherwise `negative`:
+
+[source,java]
+----
+simple("${header.foo > 0 ? 'positive' : 'negative'}");
+----
+
+Ternary operators can also be nested to handle multiple conditions:
+
+[source,java]
+----
+simple("${header.score >= 90 ? 'A' : ${header.score >= 80 ? 'B' : 'C'}}");
+----
+
 For example the following elvis operator will return the username header 
unless its null or empty, which
 then the default value of `Guest` is returned.
 
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 d4b7caaea8d4..249addbb81a6 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
@@ -1160,6 +1160,18 @@ public final class CSimpleHelper {
         }
     }
 
+    public static Object ternary(Exchange exchange, Object condition, Object 
trueValue, Object falseValue) {
+        boolean result;
+        if (condition instanceof Boolean b) {
+            result = b;
+        } else {
+            // Try to convert to boolean - treat null, empty, and "false" as 
false
+            result = condition != null && !ObjectHelper.isEmpty(condition)
+                    && !Boolean.FALSE.equals(condition) && 
!"false".equalsIgnoreCase(String.valueOf(condition));
+        }
+        return result ? trueValue : falseValue;
+    }
+
     public static Object setHeader(Exchange exchange, String name, Class<?> 
type, Object value) {
         if (type != null && value != null) {
             value = convertTo(exchange, type, value);
diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/BaseSimpleParser.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/BaseSimpleParser.java
index 359f7a979ccb..1cb610ea7974 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/BaseSimpleParser.java
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/BaseSimpleParser.java
@@ -29,6 +29,7 @@ import org.apache.camel.language.simple.ast.BlockEnd;
 import org.apache.camel.language.simple.ast.BlockStart;
 import org.apache.camel.language.simple.ast.OtherExpression;
 import org.apache.camel.language.simple.ast.SimpleNode;
+import org.apache.camel.language.simple.ast.TernaryExpression;
 import org.apache.camel.language.simple.ast.UnaryExpression;
 import org.apache.camel.language.simple.types.SimpleParserException;
 import org.apache.camel.language.simple.types.SimpleToken;
@@ -270,6 +271,82 @@ public abstract class BaseSimpleParser {
         Collections.reverse(nodes);
     }
 
+    /**
+     * Prepares ternary expressions.
+     * <p/>
+     * This process prepares the ternary expressions in the AST. This is done 
by linking the ternary operator (condition
+     * ? trueValue : falseValue) with all three parts: condition, trueValue, 
and falseValue.
+     * <p/>
+     * The ternary operator consists of two tokens: ? and : We need to find 
the pattern: condition ? trueValue :
+     * falseValue
+     */
+    protected void prepareTernaryExpressions() {
+        List<SimpleNode> answer = new ArrayList<>();
+
+        for (int i = 0; i < nodes.size(); i++) {
+            SimpleNode token = nodes.get(i);
+
+            if (token instanceof TernaryExpression ternary && 
"?".equals(token.getToken().getText())) {
+                // We found the ? operator
+                // Get the condition (left side)
+                if (answer.isEmpty()) {
+                    throw new SimpleParserException(
+                            "Ternary operator ? has no condition", 
token.getToken().getIndex());
+                }
+                SimpleNode condition = answer.remove(answer.size() - 1);
+
+                // Get the true value (right side of ?)
+                if (i >= nodes.size() - 1) {
+                    throw new SimpleParserException(
+                            "Ternary operator ? has no true value", 
token.getToken().getIndex());
+                }
+                SimpleNode trueValue = nodes.get(++i);
+
+                // Find the : operator
+                if (i >= nodes.size() - 1) {
+                    throw new SimpleParserException(
+                            "Ternary operator ? has no : operator", 
token.getToken().getIndex());
+                }
+                SimpleNode colonToken = nodes.get(++i);
+                if (!(colonToken instanceof TernaryExpression) || 
!":".equals(colonToken.getToken().getText())) {
+                    throw new SimpleParserException(
+                            "Ternary operator ? must be followed by :", 
token.getToken().getIndex());
+                }
+
+                // Get the false value (right side of :)
+                if (i >= nodes.size() - 1) {
+                    throw new SimpleParserException(
+                            "Ternary operator : has no false value", 
token.getToken().getIndex());
+                }
+                SimpleNode falseValue = nodes.get(++i);
+
+                // Link all parts to the ternary expression
+                if (!ternary.acceptCondition(condition)) {
+                    throw new SimpleParserException(
+                            "Ternary operator does not support condition token 
" + condition.getToken(),
+                            token.getToken().getIndex());
+                }
+                if (!ternary.acceptTrueValue(trueValue)) {
+                    throw new SimpleParserException(
+                            "Ternary operator does not support true value 
token " + trueValue.getToken(),
+                            token.getToken().getIndex());
+                }
+                if (!ternary.acceptFalseValue(falseValue)) {
+                    throw new SimpleParserException(
+                            "Ternary operator does not support false value 
token " + falseValue.getToken(),
+                            token.getToken().getIndex());
+                }
+
+                answer.add(ternary);
+            } else {
+                answer.add(token);
+            }
+        }
+
+        nodes.clear();
+        nodes.addAll(answer);
+    }
+
     // --------------------------------------------------------------
     // grammar
     // --------------------------------------------------------------
diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionParser.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionParser.java
index 7ef8b8271dc7..f8042976310a 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionParser.java
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionParser.java
@@ -29,6 +29,7 @@ import org.apache.camel.language.simple.ast.OtherExpression;
 import org.apache.camel.language.simple.ast.SimpleFunctionEnd;
 import org.apache.camel.language.simple.ast.SimpleFunctionStart;
 import org.apache.camel.language.simple.ast.SimpleNode;
+import org.apache.camel.language.simple.ast.TernaryExpression;
 import org.apache.camel.language.simple.ast.UnaryExpression;
 import org.apache.camel.language.simple.types.OtherOperatorType;
 import org.apache.camel.language.simple.types.SimpleIllegalSyntaxException;
@@ -99,10 +100,11 @@ public class SimpleExpressionParser extends 
BaseSimpleParser {
         // parse the expression using the following grammar
         nextToken();
         while (!token.getType().isEol()) {
-            // an expression supports just template (eg text), functions, 
unary, or other operator
+            // an expression supports just template (eg text), functions, 
unary, ternary, or other operator
             templateText();
             functionText();
             unaryOperator();
+            ternaryOperator();
             otherOperator();
             nextToken();
         }
@@ -119,6 +121,8 @@ public class SimpleExpressionParser extends 
BaseSimpleParser {
         prepareBlocks();
         // compact and stack unary operators
         prepareUnaryExpressions();
+        // compact and stack ternary expressions
+        prepareTernaryExpressions();
         // compact and stack other expressions
         prepareOtherExpressions();
 
@@ -212,7 +216,7 @@ public class SimpleExpressionParser extends 
BaseSimpleParser {
     }
 
     private SimpleNode createNode(SimpleToken token, AtomicInteger functions) {
-        // expression only support functions and unary operators
+        // expression only support functions, unary operators, ternary 
operators, and other operators
         if (token.getType().isFunctionStart()) {
             // starting a new function
             functions.incrementAndGet();
@@ -226,6 +230,8 @@ public class SimpleExpressionParser extends 
BaseSimpleParser {
             if (!nodes.isEmpty() && nodes.get(nodes.size() - 1) instanceof 
SimpleFunctionEnd) {
                 return new UnaryExpression(token);
             }
+        } else if (token.getType().isTernary()) {
+            return new TernaryExpression(token);
         } else if (token.getType().isOther()) {
             return new OtherExpression(token);
         }
@@ -308,9 +314,9 @@ public class SimpleExpressionParser extends 
BaseSimpleParser {
     // - other operator = operator attached to both the left and right hand 
side nodes
 
     protected void templateText() {
-        // for template, we accept anything but functions / other operator
+        // for template, we accept anything but functions / ternary operator / 
other operator
         while (!token.getType().isFunctionStart() && 
!token.getType().isFunctionEnd() && !token.getType().isEol()
-                && !token.getType().isOther()) {
+                && !token.getType().isTernary() && !token.getType().isOther()) 
{
             nextToken();
         }
     }
@@ -365,6 +371,33 @@ public class SimpleExpressionParser extends 
BaseSimpleParser {
         return false;
     }
 
+    protected boolean ternaryOperator() {
+        if (accept(TokenType.ternaryOperator)) {
+            nextToken();
+            // there should be at least one whitespace after the operator
+            expectAndAcceptMore(TokenType.whiteSpace);
+
+            // then we expect either some quoted text, another function, or a 
numeric, boolean or null value
+            if (singleQuotedLiteralWithFunctionsText()
+                    || doubleQuotedLiteralWithFunctionsText()
+                    || functionText()
+                    || numericValue()
+                    || booleanValue()
+                    || nullValue()) {
+                // then after the right hand side value, there should be a 
whitespace if there is more tokens
+                nextToken();
+                if (!token.getType().isEol()) {
+                    expect(TokenType.whiteSpace);
+                }
+            } else {
+                throw new SimpleParserException(
+                        "Ternary operator does not support token " + token, 
token.getIndex());
+            }
+            return true;
+        }
+        return false;
+    }
+
     protected boolean unaryOperator() {
         if (accept(TokenType.unaryOperator)) {
             nextToken();
diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimplePredicateParser.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimplePredicateParser.java
index a0f674adaa26..a7ad6f8e0d08 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimplePredicateParser.java
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimplePredicateParser.java
@@ -43,6 +43,7 @@ import 
org.apache.camel.language.simple.ast.SimpleFunctionStart;
 import org.apache.camel.language.simple.ast.SimpleNode;
 import org.apache.camel.language.simple.ast.SingleQuoteEnd;
 import org.apache.camel.language.simple.ast.SingleQuoteStart;
+import org.apache.camel.language.simple.ast.TernaryExpression;
 import org.apache.camel.language.simple.ast.UnaryExpression;
 import org.apache.camel.language.simple.types.BinaryOperatorType;
 import org.apache.camel.language.simple.types.LogicalOperatorType;
@@ -122,6 +123,7 @@ public class SimplePredicateParser extends BaseSimpleParser 
{
                     && !functionText()
                     && !unaryOperator()
                     && !binaryOperator()
+                    && !ternaryOperator()
                     && !otherOperator()
                     && !logicalOperator()
                     && !isBooleanValue()
@@ -149,6 +151,8 @@ public class SimplePredicateParser extends BaseSimpleParser 
{
         prepareUnaryExpressions();
         // compact and stack binary expressions
         prepareBinaryExpressions();
+        // compact and stack ternary expressions
+        prepareTernaryExpressions();
         // compact and stack other expressions
         prepareOtherExpressions();
         // compact and stack logical expressions
@@ -340,6 +344,8 @@ public class SimplePredicateParser extends BaseSimpleParser 
{
             return new UnaryExpression(token);
         } else if (token.getType().isBinary()) {
             return new BinaryExpression(token);
+        } else if (token.getType().isTernary()) {
+            return new TernaryExpression(token);
         } else if (token.getType().isOther()) {
             return new OtherExpression(token);
         } else if (token.getType().isLogical()) {
@@ -732,6 +738,33 @@ public class SimplePredicateParser extends 
BaseSimpleParser {
         return false;
     }
 
+    protected boolean ternaryOperator() {
+        if (accept(TokenType.ternaryOperator)) {
+            nextToken();
+            // there should be at least one whitespace after the operator
+            expectAndAcceptMore(TokenType.whiteSpace);
+
+            // then we expect either some quoted text, another function, or a 
numeric, boolean or null value
+            if (singleQuotedLiteralWithFunctionsText()
+                    || doubleQuotedLiteralWithFunctionsText()
+                    || functionText()
+                    || numericValue()
+                    || booleanValue()
+                    || nullValue()) {
+                // then after the right hand side value, there should be a 
whitespace if there is more tokens
+                nextToken();
+                if (!token.getType().isEol()) {
+                    expect(TokenType.whiteSpace);
+                }
+            } else {
+                throw new SimpleParserException(
+                        "Ternary operator does not support token " + token, 
token.getIndex());
+            }
+            return true;
+        }
+        return false;
+    }
+
     protected boolean otherOperator() {
         if (accept(TokenType.otherOperator)) {
             // remember the other operator
diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleTokenizer.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleTokenizer.java
index 0258ce18b8ff..8beb7b343522 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleTokenizer.java
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleTokenizer.java
@@ -27,7 +27,7 @@ import org.apache.camel.util.ObjectHelper;
 public final class SimpleTokenizer {
 
     // keep this number in sync with tokens list
-    private static final int NUMBER_OF_TOKENS = 50;
+    private static final int NUMBER_OF_TOKENS = 52;
 
     private static final SimpleTokenType[] KNOWN_TOKENS = new 
SimpleTokenType[NUMBER_OF_TOKENS];
 
@@ -96,10 +96,14 @@ public final class SimpleTokenizer {
         KNOWN_TOKENS[47] = new SimpleTokenType(TokenType.logicalOperator, 
"&&");
         KNOWN_TOKENS[48] = new SimpleTokenType(TokenType.logicalOperator, 
"||");
 
+        // ternary operators
+        KNOWN_TOKENS[49] = new SimpleTokenType(TokenType.ternaryOperator, "?");
+        KNOWN_TOKENS[50] = new SimpleTokenType(TokenType.ternaryOperator, ":");
+
         //binary operator
         // it is added as the last item because unary -- has the priority
         // if unary not found it is highly possible - operator is run into.
-        KNOWN_TOKENS[49] = new SimpleTokenType(TokenType.minusValue, "-");
+        KNOWN_TOKENS[51] = new SimpleTokenType(TokenType.minusValue, "-");
     }
 
     private SimpleTokenizer() {
@@ -267,6 +271,9 @@ public final class SimpleTokenizer {
         if (token.isOther()) {
             return evalOther(token, text, expression, index);
         }
+        if (token.isTernary()) {
+            return evalTernary(token, text, expression, index);
+        }
 
         return text.startsWith(token.getValue());
     }
@@ -293,6 +300,17 @@ public final class SimpleTokenizer {
         return " ".equals(previousOne) && " ".equals(afterOne) && 
text.substring(0, len).equals(token.getValue());
     }
 
+    private static boolean evalTernary(SimpleTokenType token, String text, 
String expression, int index) {
+        int len = token.getValue().length();
+        // The ternary operator must be used in the format of "exp1 ? exp2 : 
exp3"
+        if (index < 2 || len >= text.length() - 1) {
+            return false;
+        }
+        String previousOne = expression.substring(index - 1, index);
+        String afterOne = text.substring(len, len + 1);
+        return " ".equals(previousOne) && " ".equals(afterOne) && 
text.substring(0, len).equals(token.getValue());
+    }
+
     private static boolean evalUnary(SimpleTokenType token, String text, 
String expression, int index) {
         int endLen = 1;
 
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 1953dbb8ea1a..0442c33288c8 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
@@ -16,11 +16,16 @@
  */
 package org.apache.camel.language.simple.ast;
 
+import java.util.List;
 import java.util.Map;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.Exchange;
 import org.apache.camel.Expression;
+import org.apache.camel.Predicate;
+import org.apache.camel.language.simple.BaseSimpleParser;
+import org.apache.camel.language.simple.SimpleExpressionParser;
+import org.apache.camel.language.simple.SimplePredicateParser;
 import org.apache.camel.language.simple.types.SimpleIllegalSyntaxException;
 import org.apache.camel.language.simple.types.SimpleParserException;
 import org.apache.camel.language.simple.types.SimpleToken;
@@ -61,6 +66,11 @@ public class SimpleFunctionStart extends BaseSimpleNode 
implements BlockStart {
 
     @Override
     public Expression createExpression(CamelContext camelContext, String 
expression) {
+        // Check if the block contains ternary expression nodes - if so, 
process them first
+        if (containsTernaryExpressionNodes()) {
+            return doCreateTernaryExpression(camelContext, expression);
+        }
+
         // a function can either be a simple literal function, or contain 
nested functions
         if (block.getChildren().size() == 1 && block.getChildren().get(0) 
instanceof LiteralNode) {
             return doCreateLiteralExpression(camelContext, expression);
@@ -69,13 +79,329 @@ public class SimpleFunctionStart extends BaseSimpleNode 
implements BlockStart {
         }
     }
 
+    /**
+     * Check if the block contains TernaryExpression nodes (? or : operators)
+     */
+    private boolean containsTernaryExpressionNodes() {
+        for (SimpleNode child : block.getChildren()) {
+            if (child instanceof TernaryExpression) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Create an expression from a block that contains ternary expression 
nodes. This handles the pattern: condition ?
+     * trueValue : falseValue
+     */
+    private Expression doCreateTernaryExpression(CamelContext camelContext, 
String expression) {
+        List<SimpleNode> children = block.getChildren();
+
+        // Find the ? operator
+        int questionIdx = -1;
+        for (int i = 0; i < children.size(); i++) {
+            SimpleNode child = children.get(i);
+            if (child instanceof TernaryExpression && 
"?".equals(child.getToken().getText())) {
+                questionIdx = i;
+                break;
+            }
+        }
+
+        if (questionIdx < 0) {
+            // No ? found, fall back to composite expression
+            return doCreateCompositeExpression(camelContext, expression);
+        }
+
+        // Find the : operator after the ?
+        int colonIdx = -1;
+        for (int i = questionIdx + 1; i < children.size(); i++) {
+            SimpleNode child = children.get(i);
+            if (child instanceof TernaryExpression && 
":".equals(child.getToken().getText())) {
+                colonIdx = i;
+                break;
+            }
+        }
+
+        if (colonIdx < 0) {
+            throw new SimpleParserException(
+                    "Ternary operator ? must be followed by :", 
children.get(questionIdx).getToken().getIndex());
+        }
+
+        // Extract condition, true value, and false value
+        List<SimpleNode> conditionNodes = children.subList(0, questionIdx);
+        List<SimpleNode> trueNodes = children.subList(questionIdx + 1, 
colonIdx);
+        List<SimpleNode> falseNodes = children.subList(colonIdx + 1, 
children.size());
+
+        // Build the condition text
+        String conditionText = buildTextFromNodes(conditionNodes, 
camelContext);
+        String trueText = buildTextFromNodes(trueNodes, camelContext);
+        String falseText = buildTextFromNodes(falseNodes, camelContext);
+
+        // Wrap the condition for predicate parsing
+        String predicateText = wrapFunctionsInCondition(conditionText.trim());
+
+        // Parse the condition as a predicate
+        SimplePredicateParser predicateParser
+                = new SimplePredicateParser(camelContext, predicateText, true, 
skipFileFunctions, null);
+        final Predicate conditionPredicate = predicateParser.parsePredicate();
+
+        // Parse the true and false values as expressions
+        final Expression trueExp = parseValueExpression(camelContext, 
trueText.trim());
+        final Expression falseExp = parseValueExpression(camelContext, 
falseText.trim());
+
+        return new Expression() {
+            @Override
+            public <T> T evaluate(Exchange exchange, Class<T> type) {
+                if (conditionPredicate.matches(exchange)) {
+                    return trueExp.evaluate(exchange, type);
+                } else {
+                    return falseExp.evaluate(exchange, type);
+                }
+            }
+
+            @Override
+            public String toString() {
+                return conditionText + " ? " + trueText + " : " + falseText;
+            }
+        };
+    }
+
+    /**
+     * Build a text string from a list of nodes
+     */
+    private String buildTextFromNodes(List<SimpleNode> nodes, CamelContext 
camelContext) {
+        StringBuilder sb = new StringBuilder();
+        for (SimpleNode node : nodes) {
+            if (node instanceof LiteralNode literal) {
+                sb.append(literal.getText());
+            } else if (node instanceof SingleQuoteStart || node instanceof 
DoubleQuoteStart) {
+                sb.append(node.toString());
+            } else if (node instanceof SimpleFunctionStart) {
+                sb.append(node.toString());
+            } else if (node instanceof TernaryExpression) {
+                // Include the ternary operator (? or :) in the text
+                sb.append(node.getToken().getText());
+            }
+        }
+        return sb.toString();
+    }
+
     private Expression doCreateLiteralExpression(CamelContext camelContext, 
String expression) {
-        SimpleFunctionExpression function = new 
SimpleFunctionExpression(this.getToken(), cacheExpression, skipFileFunctions);
         LiteralNode literal = (LiteralNode) block.getChildren().get(0);
-        function.addText(literal.getText());
+        String text = literal.getText();
+
+        // Check if this is a ternary expression
+        Expression ternaryExp = tryParseTernaryExpression(camelContext, text);
+        if (ternaryExp != null) {
+            return ternaryExp;
+        }
+
+        SimpleFunctionExpression function = new 
SimpleFunctionExpression(this.getToken(), cacheExpression, skipFileFunctions);
+        function.addText(text);
         return function.createExpression(camelContext, expression);
     }
 
+    /**
+     * Try to parse the text as a ternary expression. Returns null if the text 
is not a ternary expression.
+     */
+    private Expression tryParseTernaryExpression(CamelContext camelContext, 
String text) {
+        // Find the ? operator (not inside quotes or nested ${})
+        int questionIdx = findTernaryOperator(text, '?');
+        if (questionIdx < 0) {
+            return null;
+        }
+
+        // Find the : operator after the ?
+        int colonIdx = findTernaryOperator(text.substring(questionIdx + 1), 
':');
+        if (colonIdx < 0) {
+            return null;
+        }
+        colonIdx = questionIdx + 1 + colonIdx;
+
+        // Extract the three parts
+        String conditionText = text.substring(0, questionIdx).trim();
+        String trueText = text.substring(questionIdx + 1, colonIdx).trim();
+        String falseText = text.substring(colonIdx + 1).trim();
+
+        if (conditionText.isEmpty() || trueText.isEmpty() || 
falseText.isEmpty()) {
+            return null;
+        }
+
+        // The condition text is like "header.foo > 0" but the predicate 
parser expects
+        // "${header.foo} > 0". We need to transform the condition to wrap 
function references
+        // with ${}. A simple approach: if there's no ${} in the condition, 
wrap the left side.
+        String predicateText = wrapFunctionsInCondition(conditionText);
+
+        // Parse the condition as a predicate - use null for cache to avoid 
caching issues
+        SimplePredicateParser predicateParser
+                = new SimplePredicateParser(camelContext, predicateText, true, 
skipFileFunctions, null);
+        final Predicate conditionPredicate = predicateParser.parsePredicate();
+
+        // Parse the true and false values as expressions
+        final Expression trueExp = parseValueExpression(camelContext, 
trueText);
+        final Expression falseExp = parseValueExpression(camelContext, 
falseText);
+
+        return new Expression() {
+            @Override
+            public <T> T evaluate(Exchange exchange, Class<T> type) {
+                if (conditionPredicate.matches(exchange)) {
+                    return trueExp.evaluate(exchange, type);
+                } else {
+                    return falseExp.evaluate(exchange, type);
+                }
+            }
+
+            @Override
+            public String toString() {
+                return conditionText + " ? " + trueText + " : " + falseText;
+            }
+        };
+    }
+
+    /**
+     * Parse a value as an expression. Handles quoted literals, functions, 
ternary expressions, and null.
+     */
+    private Expression parseValueExpression(CamelContext camelContext, String 
text) {
+        // Handle quoted strings
+        if ((text.startsWith("'") && text.endsWith("'")) || 
(text.startsWith("\"") && text.endsWith("\""))) {
+            final String value = text.substring(1, text.length() - 1);
+            return new Expression() {
+                @Override
+                public <T> T evaluate(Exchange exchange, Class<T> type) {
+                    return 
exchange.getContext().getTypeConverter().convertTo(type, value);
+                }
+
+                @Override
+                public String toString() {
+                    return value;
+                }
+            };
+        }
+
+        // Handle null
+        if ("null".equals(text) || "${null}".equals(text)) {
+            return new Expression() {
+                @Override
+                public <T> T evaluate(Exchange exchange, Class<T> type) {
+                    return null;
+                }
+
+                @Override
+                public String toString() {
+                    return "null";
+                }
+            };
+        }
+
+        // Check if this is a nested ternary expression (contains ? and :)
+        Expression ternaryExp = tryParseTernaryExpression(camelContext, text);
+        if (ternaryExp != null) {
+            return ternaryExp;
+        }
+
+        // Handle function expressions (may or may not have ${})
+        String expText = text;
+        if (!text.startsWith("${")) {
+            expText = "${" + text + "}";
+        }
+        // use null for cache to avoid caching issues with ternary expressions
+        SimpleExpressionParser parser
+                = new SimpleExpressionParser(camelContext, expText, true, 
skipFileFunctions, null);
+        return parser.parseExpression();
+    }
+
+    /**
+     * Wrap function references in the condition text with ${}. For example: 
"header.foo > 0" becomes "${header.foo} >
+     * 0"
+     */
+    private String wrapFunctionsInCondition(String conditionText) {
+        // If the condition already has ${}, assume it's properly formatted
+        if (conditionText.contains("${")) {
+            return conditionText;
+        }
+
+        // Find the operator in the condition
+        String[] operators = {
+                " >= ", " <= ", " > ", " < ", " == ", " != ", " =~ ", " !=~ ",
+                " contains ", " !contains ", " ~~ ", " !~~ ", " regex ", " 
!regex ",
+                " in ", " !in ", " is ", " !is ", " range ", " !range ",
+                " startsWith ", " !startsWith ", " endsWith ", " !endsWith " };
+
+        for (String op : operators) {
+            int opIdx = conditionText.indexOf(op);
+            if (opIdx > 0) {
+                String leftSide = conditionText.substring(0, opIdx).trim();
+                String rightSide = conditionText.substring(opIdx + 
op.length()).trim();
+
+                // Wrap the left side with ${} if it looks like a function 
reference
+                if (!leftSide.startsWith("${") && !leftSide.startsWith("'") && 
!leftSide.startsWith("\"")
+                        && !isNumeric(leftSide) && 
!"true".equalsIgnoreCase(leftSide)
+                        && !"false".equalsIgnoreCase(leftSide) && 
!"null".equalsIgnoreCase(leftSide)) {
+                    leftSide = "${" + leftSide + "}";
+                }
+
+                return leftSide + op + rightSide;
+            }
+        }
+
+        // No operator found, return as-is
+        return conditionText;
+    }
+
+    private boolean isNumeric(String str) {
+        if (str == null || str.isEmpty()) {
+            return false;
+        }
+        try {
+            Double.parseDouble(str);
+            return true;
+        } catch (NumberFormatException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Find the index of the ternary operator character, skipping nested ${}, 
quotes, etc.
+     */
+    private int findTernaryOperator(String text, char operator) {
+        int depth = 0;
+        boolean inSingleQuote = false;
+        boolean inDoubleQuote = false;
+
+        for (int i = 0; i < text.length(); i++) {
+            char c = text.charAt(i);
+
+            if (!inSingleQuote && !inDoubleQuote) {
+                if (c == '$' && i + 1 < text.length() && text.charAt(i + 1) == 
'{') {
+                    depth++;
+                    i++; // skip the {
+                    continue;
+                }
+                if (c == '}' && depth > 0) {
+                    depth--;
+                    continue;
+                }
+                if (c == '\'' && depth == 0) {
+                    inSingleQuote = true;
+                    continue;
+                }
+                if (c == '"' && depth == 0) {
+                    inDoubleQuote = true;
+                    continue;
+                }
+                if (c == operator && depth == 0) {
+                    return i;
+                }
+            } else if (inSingleQuote && c == '\'') {
+                inSingleQuote = false;
+            } else if (inDoubleQuote && c == '"') {
+                inDoubleQuote = false;
+            }
+        }
+        return -1;
+    }
+
     private Expression doCreateCompositeExpression(CamelContext camelContext, 
String expression) {
         final SimpleToken token = getToken();
         return new Expression() {
@@ -121,6 +447,13 @@ public class SimpleFunctionStart extends BaseSimpleNode 
implements BlockStart {
                 // we have now concat the block as a String which contains the 
function expression
                 // which we then need to evaluate as a function
                 String exp = sb.toString();
+
+                // Check if this is a ternary expression
+                Expression ternaryExp = 
tryParseTernaryExpression(camelContext, exp);
+                if (ternaryExp != null) {
+                    return ternaryExp.evaluate(exchange, type);
+                }
+
                 SimpleFunctionExpression function = new 
SimpleFunctionExpression(token, cacheExpression, skipFileFunctions);
                 function.addText(exp);
                 try {
@@ -140,9 +473,10 @@ public class SimpleFunctionStart extends BaseSimpleNode 
implements BlockStart {
 
     @Override
     public boolean acceptAndAddNode(SimpleNode node) {
-        // only accept literals, quotes or embedded functions
+        // only accept literals, quotes, ternary expressions, or embedded 
functions
         if (node instanceof LiteralNode || node instanceof SimpleFunctionStart
-                || node instanceof SingleQuoteStart || node instanceof 
DoubleQuoteStart) {
+                || node instanceof SingleQuoteStart || node instanceof 
DoubleQuoteStart
+                || node instanceof TernaryExpression) {
             block.addChild(node);
             return true;
         } else {
@@ -152,6 +486,11 @@ public class SimpleFunctionStart extends BaseSimpleNode 
implements BlockStart {
 
     @Override
     public String createCode(CamelContext camelContext, String expression) 
throws SimpleParserException {
+        // Check if the block contains ternary expression nodes - if so, 
process them first
+        if (containsTernaryExpressionNodes()) {
+            return doCreateTernaryCode(camelContext, expression);
+        }
+
         String answer;
         // a function can either be a simple literal function or contain 
nested functions
         if (block.getChildren().size() == 1 && block.getChildren().get(0) 
instanceof LiteralNode) {
@@ -162,13 +501,163 @@ public class SimpleFunctionStart extends BaseSimpleNode 
implements BlockStart {
         return answer;
     }
 
+    /**
+     * Create code from a block that contains ternary expression nodes. This 
handles the pattern: condition ? trueValue
+     * : falseValue
+     */
+    private String doCreateTernaryCode(CamelContext camelContext, String 
expression) {
+        List<SimpleNode> children = block.getChildren();
+
+        // Find the ? operator
+        int questionIdx = -1;
+        for (int i = 0; i < children.size(); i++) {
+            SimpleNode child = children.get(i);
+            if (child instanceof TernaryExpression && 
"?".equals(child.getToken().getText())) {
+                questionIdx = i;
+                break;
+            }
+        }
+
+        if (questionIdx < 0) {
+            // No ? found, fall back to composite code
+            return doCreateCompositeCode(camelContext, expression);
+        }
+
+        // Find the : operator after the ?
+        int colonIdx = -1;
+        for (int i = questionIdx + 1; i < children.size(); i++) {
+            SimpleNode child = children.get(i);
+            if (child instanceof TernaryExpression && 
":".equals(child.getToken().getText())) {
+                colonIdx = i;
+                break;
+            }
+        }
+
+        if (colonIdx < 0) {
+            throw new SimpleParserException(
+                    "Ternary operator ? must be followed by :", 
children.get(questionIdx).getToken().getIndex());
+        }
+
+        // Extract condition, true value, and false value nodes
+        List<SimpleNode> conditionNodes = children.subList(0, questionIdx);
+        List<SimpleNode> trueNodes = children.subList(questionIdx + 1, 
colonIdx);
+        List<SimpleNode> falseNodes = children.subList(colonIdx + 1, 
children.size());
+
+        // Build the text from nodes
+        String conditionText = buildTextFromNodes(conditionNodes, 
camelContext);
+        String trueText = buildTextFromNodes(trueNodes, camelContext);
+        String falseText = buildTextFromNodes(falseNodes, camelContext);
+
+        // Wrap the condition for predicate parsing
+        String predicateText = wrapFunctionsInCondition(conditionText.trim());
+
+        // Parse the condition as a predicate and generate code
+        SimplePredicateParser predicateParser
+                = new SimplePredicateParser(camelContext, predicateText, true, 
skipFileFunctions, null);
+        String conditionCode = predicateParser.parseCode();
+
+        // Parse the true and false values as expressions and generate code
+        String trueCode = parseValueCode(camelContext, trueText.trim());
+        String falseCode = parseValueCode(camelContext, falseText.trim());
+
+        return BaseSimpleParser.CODE_START + "ternary(exchange, " + 
conditionCode + ", " + trueCode + ", " + falseCode
+               + ")" + BaseSimpleParser.CODE_END;
+    }
+
     private String doCreateLiteralCode(CamelContext camelContext, String 
expression) {
-        SimpleFunctionExpression function = new 
SimpleFunctionExpression(this.getToken(), cacheExpression, skipFileFunctions);
         LiteralNode literal = (LiteralNode) block.getChildren().get(0);
-        function.addText(literal.getText());
+        String text = literal.getText();
+
+        // Check if this is a ternary expression
+        String ternaryCode = tryParseTernaryCode(camelContext, text);
+        if (ternaryCode != null) {
+            return ternaryCode;
+        }
+
+        SimpleFunctionExpression function = new 
SimpleFunctionExpression(this.getToken(), cacheExpression, skipFileFunctions);
+        function.addText(text);
         return function.createCode(camelContext, expression);
     }
 
+    /**
+     * Try to parse the text as a ternary expression and generate code. 
Returns null if the text is not a ternary
+     * expression.
+     */
+    private String tryParseTernaryCode(CamelContext camelContext, String text) 
{
+        // Find the ? operator (not inside quotes or nested ${})
+        int questionIdx = findTernaryOperator(text, '?');
+        if (questionIdx < 0) {
+            return null;
+        }
+
+        // Find the : operator after the ?
+        int colonIdx = findTernaryOperator(text.substring(questionIdx + 1), 
':');
+        if (colonIdx < 0) {
+            return null;
+        }
+        colonIdx = questionIdx + 1 + colonIdx;
+
+        // Extract the three parts
+        String conditionText = text.substring(0, questionIdx).trim();
+        String trueText = text.substring(questionIdx + 1, colonIdx).trim();
+        String falseText = text.substring(colonIdx + 1).trim();
+
+        if (conditionText.isEmpty() || trueText.isEmpty() || 
falseText.isEmpty()) {
+            return null;
+        }
+
+        // The condition text needs to be wrapped with ${} for parsing
+        String predicateText = wrapFunctionsInCondition(conditionText);
+
+        // Parse the condition as a predicate and generate code
+        SimplePredicateParser predicateParser
+                = new SimplePredicateParser(camelContext, predicateText, true, 
skipFileFunctions, null);
+        String conditionCode = predicateParser.parseCode();
+
+        // Parse the true and false values as expressions and generate code
+        String trueCode = parseValueCode(camelContext, trueText);
+        String falseCode = parseValueCode(camelContext, falseText);
+
+        return BaseSimpleParser.CODE_START + "ternary(exchange, " + 
conditionCode + ", " + trueCode + ", " + falseCode
+               + ")" + BaseSimpleParser.CODE_END;
+    }
+
+    /**
+     * Parse a value as code. Handles quoted literals, functions, and null.
+     */
+    private String parseValueCode(CamelContext camelContext, String text) {
+        // Handle quoted strings - return as string literal
+        if ((text.startsWith("'") && text.endsWith("'")) || 
(text.startsWith("\"") && text.endsWith("\""))) {
+            String value = text.substring(1, text.length() - 1);
+            return "\"" + value + "\"";
+        }
+
+        // Handle null
+        if ("null".equals(text) || "${null}".equals(text)) {
+            return "null";
+        }
+
+        // Check if this is a nested ternary expression
+        String nestedTernary = tryParseTernaryCode(camelContext, text);
+        if (nestedTernary != null) {
+            // Remove the CODE_START and CODE_END markers for nested 
expressions
+            String code = nestedTernary.replace(BaseSimpleParser.CODE_START, 
"").replace(BaseSimpleParser.CODE_END, "");
+            return code;
+        }
+
+        // Handle function expressions (may or may not have ${})
+        String expText = text;
+        if (!text.startsWith("${")) {
+            expText = "${" + text + "}";
+        }
+        SimpleExpressionParser parser
+                = new SimpleExpressionParser(camelContext, expText, true, 
skipFileFunctions, null);
+        String code = parser.parseCode();
+        // Remove the CODE_START and CODE_END markers
+        code = code.replace(BaseSimpleParser.CODE_START, 
"").replace(BaseSimpleParser.CODE_END, "");
+        return code;
+    }
+
     private String doCreateCompositeCode(CamelContext camelContext, String 
expression) {
         StringBuilder sb = new StringBuilder(256);
         boolean quoteEmbeddedFunctions = false;
diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/TernaryExpression.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/TernaryExpression.java
new file mode 100644
index 000000000000..708a8ca5dd19
--- /dev/null
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/TernaryExpression.java
@@ -0,0 +1,156 @@
+/*
+ * 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.camel.language.simple.ast;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+import org.apache.camel.Expression;
+import org.apache.camel.Predicate;
+import org.apache.camel.language.simple.BaseSimpleParser;
+import org.apache.camel.language.simple.types.SimpleParserException;
+import org.apache.camel.language.simple.types.SimpleToken;
+import org.apache.camel.support.ExpressionToPredicateAdapter;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.StringHelper;
+
+/**
+ * Represents a ternary expression in the AST.
+ * <p>
+ * Syntax: condition ? trueValue : falseValue
+ */
+public class TernaryExpression extends BaseSimpleNode {
+
+    private SimpleNode condition;
+    private SimpleNode trueValue;
+    private SimpleNode falseValue;
+
+    public TernaryExpression(SimpleToken token) {
+        super(token);
+    }
+
+    @Override
+    public String toString() {
+        return condition + " ? " + trueValue + " : " + falseValue;
+    }
+
+    public boolean acceptCondition(SimpleNode condition) {
+        this.condition = condition;
+        return true;
+    }
+
+    public boolean acceptTrueValue(SimpleNode trueValue) {
+        this.trueValue = trueValue;
+        return true;
+    }
+
+    public boolean acceptFalseValue(SimpleNode falseValue) {
+        this.falseValue = falseValue;
+        return true;
+    }
+
+    public SimpleNode getCondition() {
+        return condition;
+    }
+
+    public SimpleNode getTrueValue() {
+        return trueValue;
+    }
+
+    public SimpleNode getFalseValue() {
+        return falseValue;
+    }
+
+    @Override
+    public Expression createExpression(CamelContext camelContext, String 
expression) {
+        if (condition == null) {
+            throw new SimpleParserException(
+                    "Ternary operator ? has no condition at index " + 
token.getIndex(), token.getIndex());
+        }
+        if (trueValue == null) {
+            throw new SimpleParserException(
+                    "Ternary operator ? has no true value at index " + 
token.getIndex(), token.getIndex());
+        }
+        if (falseValue == null) {
+            throw new SimpleParserException(
+                    "Ternary operator : has no false value at index " + 
token.getIndex(), token.getIndex());
+        }
+
+        // the expression parser does not parse literal text into 
single/double quote tokens
+        // so we need to manually remove leading quotes from the literal text
+        if (trueValue instanceof LiteralExpression le) {
+            String text = le.getText();
+            String changed = StringHelper.removeLeadingAndEndingQuotes(text);
+            if (!changed.equals(text)) {
+                le.replaceText(changed);
+            }
+        }
+        if (falseValue instanceof LiteralExpression le) {
+            String text = le.getText();
+            String changed = StringHelper.removeLeadingAndEndingQuotes(text);
+            if (!changed.equals(text)) {
+                le.replaceText(changed);
+            }
+        }
+
+        final Expression conditionExp = 
condition.createExpression(camelContext, expression);
+        final Expression trueExp = trueValue.createExpression(camelContext, 
expression);
+        final Expression falseExp = falseValue.createExpression(camelContext, 
expression);
+
+        return createTernaryExpression(camelContext, conditionExp, trueExp, 
falseExp);
+    }
+
+    private Expression createTernaryExpression(
+            final CamelContext camelContext, final Expression conditionExp,
+            final Expression trueExp, final Expression falseExp) {
+        return new Expression() {
+            @Override
+            public <T> T evaluate(Exchange exchange, Class<T> type) {
+                // Convert condition to predicate
+                Predicate predicate = 
ExpressionToPredicateAdapter.toPredicate(conditionExp);
+
+                if (predicate.matches(exchange)) {
+                    return trueExp.evaluate(exchange, type);
+                } else {
+                    return falseExp.evaluate(exchange, type);
+                }
+            }
+
+            @Override
+            public String toString() {
+                return condition + " ? " + trueValue + " : " + falseValue;
+            }
+        };
+    }
+
+    @Override
+    public String createCode(CamelContext camelContext, String expression) 
throws SimpleParserException {
+        return BaseSimpleParser.CODE_START + doCreateCode(camelContext, 
expression) + BaseSimpleParser.CODE_END;
+    }
+
+    private String doCreateCode(CamelContext camelContext, String expression) 
throws SimpleParserException {
+        ObjectHelper.notNull(condition, "condition node", this);
+        ObjectHelper.notNull(trueValue, "trueValue node", this);
+        ObjectHelper.notNull(falseValue, "falseValue node", this);
+
+        final String conditionCode = condition.createCode(camelContext, 
expression);
+        final String trueCode = trueValue.createCode(camelContext, expression);
+        final String falseCode = falseValue.createCode(camelContext, 
expression);
+
+        return "ternary(exchange, " + conditionCode + ", " + trueCode + ", " + 
falseCode + ")";
+    }
+
+}
diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/types/SimpleTokenType.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/types/SimpleTokenType.java
index 7663e1d21cd3..eb78ea50c991 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/types/SimpleTokenType.java
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/types/SimpleTokenType.java
@@ -152,6 +152,13 @@ public final class SimpleTokenType {
         return type == TokenType.numericValue;
     }
 
+    /**
+     * Whether the type is ternary operator
+     */
+    public boolean isTernary() {
+        return type == TokenType.ternaryOperator;
+    }
+
     @Override
     public String toString() {
         return value;
diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/types/TokenType.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/types/TernaryOperatorType.java
similarity index 54%
copy from 
core/camel-core-languages/src/main/java/org/apache/camel/language/simple/types/TokenType.java
copy to 
core/camel-core-languages/src/main/java/org/apache/camel/language/simple/types/TernaryOperatorType.java
index e737ab809b09..75d919c36ab2 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/types/TokenType.java
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/types/TernaryOperatorType.java
@@ -17,25 +17,34 @@
 package org.apache.camel.language.simple.types;
 
 /**
- * Classifications of known token types.
+ * Types of ternary operators supported
  */
-public enum TokenType {
+public enum TernaryOperatorType {
 
-    whiteSpace,
-    character,
-    booleanValue,
-    numericValue,
-    nullValue,
-    singleQuote,
-    doubleQuote,
-    minusValue,
-    escape,
-    functionStart,
-    functionEnd,
-    binaryOperator,
-    otherOperator,
-    unaryOperator,
-    logicalOperator,
-    eol
+    QUESTION,
+    COLON;
+
+    public static TernaryOperatorType asOperator(String text) {
+        if ("?".equals(text)) {
+            return QUESTION;
+        } else if (":".equals(text)) {
+            return COLON;
+        }
+        throw new IllegalArgumentException("Operator not supported: " + text);
+    }
+
+    public static String getOperatorText(TernaryOperatorType operator) {
+        if (operator == QUESTION) {
+            return "?";
+        } else if (operator == COLON) {
+            return ":";
+        }
+        return "";
+    }
+
+    @Override
+    public String toString() {
+        return getOperatorText(this);
+    }
 
 }
diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/types/TokenType.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/types/TokenType.java
index e737ab809b09..ebd3f73e5818 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/types/TokenType.java
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/types/TokenType.java
@@ -36,6 +36,7 @@ public enum TokenType {
     otherOperator,
     unaryOperator,
     logicalOperator,
+    ternaryOperator,
     eol
 
 }
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleTest.java
index eb3eb01d45ef..1e3981e971b6 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleTest.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleTest.java
@@ -2209,6 +2209,91 @@ public class SimpleTest extends LanguageTestSupport {
         assertExpression("${iif(${file:name} startsWith 'test',foo,bar)}", 
"bar");
     }
 
+    @Test
+    public void testTernaryOperator() {
+        // Test that the same expression object evaluates correctly with 
different header values
+        exchange.getIn().setHeader("foo", 44);
+        Expression exp = 
context.resolveLanguage("simple").createExpression("${header.foo > 0 ? 
'positive' : 'negative'}");
+        assertEquals("positive", exp.evaluate(exchange, String.class), "First 
evaluation with foo=44");
+
+        exchange.getIn().setHeader("foo", -123);
+        assertEquals("negative", exp.evaluate(exchange, String.class), "Second 
evaluation with foo=-123");
+
+        // Test a simple ternary with a constant condition
+        Expression expTrue = 
context.resolveLanguage("simple").createExpression("${true ? 'yes' : 'no'}");
+        assertEquals("yes", expTrue.evaluate(exchange, String.class), 
"Constant true ternary");
+
+        Expression expFalse = 
context.resolveLanguage("simple").createExpression("${false ? 'yes' : 'no'}");
+        assertEquals("no", expFalse.evaluate(exchange, String.class), 
"Constant false ternary");
+
+        // Test with body
+        exchange.getIn().setBody("Hello World");
+        exchange.getIn().setHeader("foo", 44);
+        assertExpression("${header.foo > 0 ? ${body} : 'Bye World'}", "Hello 
World");
+        exchange.getIn().setHeader("foo", -123);
+        assertExpression("${header.foo > 0 ? ${body} : 'Bye World'}", "Bye 
World");
+        assertExpression("${header.foo > 0 ? ${body} : ${null}}", null);
+
+        // Test with file name
+        exchange.getIn().setHeader("CamelFileName", "testfile.txt");
+        assertExpression("${file:name startsWith 'test' ? 'foo' : 'bar'}", 
"foo");
+        exchange.getIn().setHeader("CamelFileName", "dummy.txt");
+        assertExpression("${file:name startsWith 'test' ? 'foo' : 'bar'}", 
"bar");
+    }
+
+    @Test
+    public void testTernaryOperatorWithNumbers() {
+        exchange.getIn().setHeader("score", 85);
+        assertExpression("${header.score >= 90 ? 'A' : 'B'}", "B");
+        exchange.getIn().setHeader("score", 95);
+        assertExpression("${header.score >= 90 ? 'A' : 'B'}", "A");
+
+        exchange.getIn().setHeader("age", 25);
+        assertExpression("${header.age >= 18 ? 'adult' : 'minor'}", "adult");
+        exchange.getIn().setHeader("age", 15);
+        assertExpression("${header.age >= 18 ? 'adult' : 'minor'}", "minor");
+    }
+
+    @Test
+    public void testTernaryOperatorWithBooleans() {
+        exchange.getIn().setHeader("enabled", true);
+        assertExpression("${header.enabled == true ? 'yes' : 'no'}", "yes");
+        exchange.getIn().setHeader("enabled", false);
+        assertExpression("${header.enabled == true ? 'yes' : 'no'}", "no");
+    }
+
+    @Test
+    public void testTernaryOperatorWithNull() {
+        exchange.getIn().setHeader("value", null);
+        assertExpression("${header.value == null ? 'empty' : 'full'}", 
"empty");
+        exchange.getIn().setHeader("value", "something");
+        assertExpression("${header.value == null ? 'empty' : 'full'}", "full");
+    }
+
+    @Test
+    public void testTernaryOperatorNested() {
+        // Nested ternary operators
+        exchange.getIn().setHeader("score", 95);
+        assertExpression("${header.score >= 90 ? 'A' : ${header.score} >= 80 ? 
'B' : 'C'}", "A");
+        exchange.getIn().setHeader("score", 85);
+        assertExpression("${header.score >= 90 ? 'A' : ${header.score} >= 80 ? 
'B' : 'C'}", "B");
+        exchange.getIn().setHeader("score", 75);
+        assertExpression("${header.score >= 90 ? 'A' : ${header.score} >= 80 ? 
'B' : 'C'}", "C");
+    }
+
+    @Test
+    public void testTernaryOperatorWithStrings() {
+        exchange.getIn().setBody("Hello");
+        assertExpression("${body == 'Hello' ? 'greeting' : 'other'}", 
"greeting");
+        exchange.getIn().setBody("Goodbye");
+        assertExpression("${body == 'Hello' ? 'greeting' : 'other'}", "other");
+
+        exchange.getIn().setHeader("name", "John");
+        assertExpression("${header.name contains 'John' ? 'found' : 'not 
found'}", "found");
+        exchange.getIn().setHeader("name", "Jane");
+        assertExpression("${header.name contains 'John' ? 'found' : 'not 
found'}", "not found");
+    }
+
     @Test
     public void testListRemoveByInstance() {
         List<Object> data = new ArrayList<>();

Reply via email to