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 10490761ffc CAMEL-21081: Add iif function to simple language (#15168)
10490761ffc is described below

commit 10490761ffcb1601985e59912eb9d11cf7183800
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Aug 15 10:11:36 2024 +0200

    CAMEL-21081: Add iif function to simple language (#15168)
    
    CAMEL-21081: Add iif function to simple and csimple language
    CAMEL-21086: Add more complex functions to csimple language
    
    ---------
    
    Co-authored-by: Adriano Machado <[email protected]>
---
 .../src/main/docs/csimple-joor.adoc                |   3 -
 .../language/csimple/joor/OriginalSimpleTest.java  | 126 ++++++++-
 .../modules/languages/pages/simple-language.adoc   |   3 +
 .../language/csimple/CSimpleCodeGenerator.java     |  13 +-
 .../camel/language/csimple/CSimpleHelper.java      |  67 ++++-
 .../camel/language/simple/BaseSimpleParser.java    |  12 +-
 .../language/simple/SimpleExpressionBuilder.java   |  40 ++-
 .../language/simple/SimpleExpressionParser.java    |  17 +-
 .../language/simple/SimplePredicateParser.java     |   5 +-
 .../language/simple/ast/BinaryExpression.java      |   5 +
 .../language/simple/ast/LogicalExpression.java     |   5 +
 .../simple/ast/SimpleFunctionExpression.java       | 299 ++++++++++++++++++++-
 .../language/simple/ast/SimpleFunctionStart.java   |   2 +-
 .../camel/language/simple/ast/UnaryExpression.java |   5 +
 .../apache/camel/language/simple/SimpleTest.java   |  15 ++
 15 files changed, 582 insertions(+), 35 deletions(-)

diff --git a/components/camel-csimple-joor/src/main/docs/csimple-joor.adoc 
b/components/camel-csimple-joor/src/main/docs/csimple-joor.adoc
index f3424d9e1ad..168a531d290 100644
--- a/components/camel-csimple-joor/src/main/docs/csimple-joor.adoc
+++ b/components/camel-csimple-joor/src/main/docs/csimple-joor.adoc
@@ -18,9 +18,6 @@ This module includes the jOOR compiler for the CSimple 
language for runtime comp
 
 To use this, just include `camel-csimple-joor` in the classpath.
 
-NOTE: Java 8 is not supported. Java 11 or newer is required.
-
-
 == Limitations
 
 The supported runtime is intended for Java standalone, Spring Boot, Camel 
Quarkus and other microservices runtimes.
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 4c55bfb9b0b..c3b2d990983 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
@@ -16,6 +16,8 @@
  */
 package org.apache.camel.language.csimple.joor;
 
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Date;
@@ -41,11 +43,13 @@ import 
org.apache.camel.language.bean.RuntimeBeanExpressionException;
 import org.apache.camel.language.csimple.CSimpleLanguage;
 import org.apache.camel.language.simple.types.SimpleIllegalSyntaxException;
 import org.apache.camel.spi.Language;
+import org.apache.camel.spi.UuidGenerator;
 import org.apache.camel.spi.VariableRepository;
 import org.apache.camel.spi.VariableRepositoryFactory;
 import org.apache.camel.support.ExchangeHelper;
 import org.apache.camel.test.junit5.LanguageTestSupport;
 import org.apache.camel.util.InetAddressUtil;
+import org.apache.camel.util.StringHelper;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 
@@ -63,11 +67,13 @@ public class OriginalSimpleTest extends LanguageTestSupport 
{
 
     private static final String INDEX_OUT_OF_BOUNDS_ERROR_MSG = "Index 2 out 
of bounds for length 2";
 
+    @SuppressWarnings("unused")
     @BindToRegistry
-    private Animal myAnimal = new Animal("Donkey", 17);
+    private final Animal myAnimal = new Animal("Donkey", 17);
 
+    @SuppressWarnings("unused")
     @BindToRegistry
-    private Greeter greeter = new Greeter();
+    private final Greeter greeter = new Greeter();
 
     @Override
     protected CamelContext createCamelContext() throws Exception {
@@ -2086,6 +2092,122 @@ public class OriginalSimpleTest extends 
LanguageTestSupport {
         }
     }
 
+    @Test
+    public void testIif() {
+        exchange.getIn().setHeader("foo", 44);
+        assertExpression("${iif(${headerAs(foo,int)} > 
0,'positive','negative')}", "positive");
+        exchange.getIn().setHeader("foo", -123);
+        assertExpression("${iif(${headerAs(foo,int)} > 
0,'positive','negative')}", "negative");
+
+        exchange.getIn().setBody("Hello World");
+        exchange.getIn().setHeader("foo", 44);
+        assertExpression("${iif(${headerAs(foo,int)} > 0,${body},'Bye 
World')}", "Hello World");
+        exchange.getIn().setHeader("foo", -123);
+        assertExpression("${iif(${headerAs(foo,int)} > 0,${body},'Bye 
World')}", "Bye World");
+        assertExpression("${iif(${headerAs(foo,int)} > 0,${body},${null})}", 
null);
+
+        exchange.setVariable("cheese", "gauda");
+        exchange.getMessage().setHeader("counter", 3);
+        assertExpression("${iif(true, ${variableAs('cheese', 'String')}, 
${headerAs('counter', 'Integer')})}", "gauda");
+        assertExpression("${iif(false, ${variableAs('cheese', 'String')}, 
${headerAs('counter', 'Integer')})}", "3");
+    }
+
+    @Test
+    public void testJoinBody() {
+        List<Object> data = new ArrayList<>();
+        data.add("A");
+        data.add("B");
+        data.add("C");
+        exchange.getIn().setBody(data);
+
+        assertExpression("${join()}", "A,B,C");
+        assertExpression("${join(;)}", "A;B;C");
+        assertExpression("${join(' ')}", "A B C");
+        assertExpression("${join(',','id=')}", "id=A,id=B,id=C");
+        assertExpression("${join(&,id=)}", "id=A&id=B&id=C");
+    }
+
+    @Test
+    public void testJoinHeader() {
+        List<Object> data = new ArrayList<>();
+        data.add("A");
+        data.add("B");
+        data.add("C");
+        exchange.getIn().setHeader("id", data);
+
+        assertExpression("${join('&','id=',${header.id})}", "id=A&id=B&id=C");
+    }
+
+    @Test
+    public void testHash() throws Exception {
+        Expression expression = 
context.resolveLanguage("csimple").createExpression("${hash('hello')}");
+        String s = expression.evaluate(exchange, String.class);
+        assertNotNull(s);
+        MessageDigest digest = MessageDigest.getInstance("SHA-256");
+        byte[] bytes = digest.digest("hello".getBytes(StandardCharsets.UTF_8));
+        String expected = StringHelper.bytesToHex(bytes);
+        assertEquals(expected, s);
+
+        expression = 
context.resolveLanguage("csimple").createExpression("${hash(${body})}");
+        s = expression.evaluate(exchange, String.class);
+        assertNotNull(s);
+        digest = MessageDigest.getInstance("SHA-256");
+        bytes = 
digest.digest(exchange.getMessage().getBody(String.class).getBytes(StandardCharsets.UTF_8));
+        expected = StringHelper.bytesToHex(bytes);
+        assertEquals(expected, s);
+
+        expression = 
context.resolveLanguage("csimple").createExpression("${hash(${header.foo})}");
+        s = expression.evaluate(exchange, String.class);
+        assertNotNull(s);
+
+        expression = 
context.resolveLanguage("csimple").createExpression("${hash('hello',SHA3-256)}");
+        s = expression.evaluate(exchange, String.class);
+        assertNotNull(s);
+
+        expression = 
context.resolveLanguage("csimple").createExpression("${hash(${body},SHA3-256)}");
+        s = expression.evaluate(exchange, String.class);
+        assertNotNull(s);
+        digest = MessageDigest.getInstance("SHA3-256");
+        bytes = 
digest.digest(exchange.getMessage().getBody(String.class).getBytes(StandardCharsets.UTF_8));
+        expected = StringHelper.bytesToHex(bytes);
+        assertEquals(expected, s);
+
+        expression = 
context.resolveLanguage("csimple").createExpression("${hash(${header.foo},SHA3-256)}");
+        s = expression.evaluate(exchange, String.class);
+        assertNotNull(s);
+
+        expression = 
context.resolveLanguage("csimple").createExpression("${hash(${header.unknown})}");
+        s = expression.evaluate(exchange, String.class);
+        assertNull(s);
+    }
+
+    @Test
+    public void testUuid() {
+        Expression expression = 
context.resolveLanguage("csimple").createExpression("${uuid}");
+        String s = expression.evaluate(exchange, String.class);
+        assertNotNull(s);
+
+        expression = 
context.resolveLanguage("csimple").createExpression("${uuid(default)}");
+        s = expression.evaluate(exchange, String.class);
+        assertNotNull(s);
+
+        expression = 
context.resolveLanguage("csimple").createExpression("${uuid(short)}");
+        s = expression.evaluate(exchange, String.class);
+        assertNotNull(s);
+
+        expression = 
context.resolveLanguage("csimple").createExpression("${uuid(simple)}");
+        s = expression.evaluate(exchange, String.class);
+        assertNotNull(s);
+
+        expression = 
context.resolveLanguage("csimple").createExpression("${uuid(classic)}");
+        s = expression.evaluate(exchange, String.class);
+        assertNotNull(s);
+
+        // custom generator
+        context.getRegistry().bind("mygen", (UuidGenerator) () -> "1234");
+        assertExpression("${uuid(mygen)}", "1234");
+    }
+
     @Override
     protected String getLanguageName() {
         return "csimple";
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 f39955569da..ac47f5fafee 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
@@ -314,6 +314,9 @@ For _input_ you can choose `header:key`, 
`exchangeProperty:key` or `variable:key
 
 |pretty(exp) | String | Converts the inlined expression to a String, and 
attempts to pretty print if JSon or XML, otherwise the expression is returned 
as the String value.
 
+|iif(predicate, trueExp, falseExp) | Object | Evaluates the `predicate` 
expression and returns the value of `trueExp` if the predicate is
+true, otherwise the value of `falseExp` is returned. This function is similar 
to the ternary operator in Java.
+
 |=======================================================================
 
 == OGNL expression support
diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimpleCodeGenerator.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimpleCodeGenerator.java
index 5f263650f97..f6304517930 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimpleCodeGenerator.java
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimpleCodeGenerator.java
@@ -60,7 +60,7 @@ public class CSimpleCodeGenerator {
 
     private CSimpleGeneratedCode generateCode(String fqn, String script, 
boolean predicate) {
         String text = script;
-        // text should be single line and trimmed as it can be multi lined
+        // text should be single line and trimmed as it can be multi-lined
         text = text.replaceAll("\n", "");
         text = text.trim();
 
@@ -96,6 +96,7 @@ public class CSimpleCodeGenerator {
         sb.append("public class ").append(name).append(" extends 
org.apache.camel.language.csimple.CSimpleSupport {\n");
         sb.append("\n");
         sb.append("    Language bean;\n");
+        sb.append("    UuidGenerator uuid;\n");
         sb.append("\n");
         sb.append("    public ").append(name).append("() {\n");
         sb.append("    }\n");
@@ -111,7 +112,7 @@ public class CSimpleCodeGenerator {
         sb.append("    public String getText() {\n");
         // \ should be escaped
         String escaped = text.replace("\\", "\\\\");
-        // we need to escape all " so its a single literal string
+        // we need to escape all " so it's a single literal string
         escaped = escaped.replace("\"", "\\\"");
         sb.append("        return \"").append(escaped).append("\";\n");
         sb.append("    }\n");
@@ -121,9 +122,6 @@ public class CSimpleCodeGenerator {
         sb.append(
                 "    public Object evaluate(CamelContext context, Exchange 
exchange, Message message, Object body) throws Exception {\n");
         sb.append("        ");
-        if (!script.contains("return ")) {
-            sb.append("return ");
-        }
 
         if (predicate) {
             CSimplePredicateParser parser = new CSimplePredicateParser();
@@ -136,11 +134,14 @@ public class CSimpleCodeGenerator {
             CSimpleExpressionParser parser = new CSimpleExpressionParser();
             script = parser.parseExpression(script);
             if (script.isBlank()) {
-                // an expression can be whitespace but then we need to wrap 
this in quotes
+                // an expression can be whitespace, but then we need to wrap 
this in quotes
                 script = "\"" + script + "\"";
             }
         }
 
+        if (!script.contains("return ")) {
+            sb.append("return ");
+        }
         sb.append(script);
         if (!script.endsWith("}") && !script.endsWith(";")) {
             sb.append(";");
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 45dad64ea19..b572aef5478 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
@@ -17,6 +17,7 @@
 package org.apache.camel.language.csimple;
 
 import java.lang.reflect.Array;
+import java.security.MessageDigest;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.Iterator;
@@ -29,6 +30,7 @@ import java.util.regex.Pattern;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.CamelExchangeException;
+import org.apache.camel.CamelExecutionException;
 import org.apache.camel.Exchange;
 import org.apache.camel.ExchangePropertyKey;
 import org.apache.camel.Expression;
@@ -39,10 +41,16 @@ import org.apache.camel.RuntimeCamelException;
 import org.apache.camel.spi.ExchangeFormatter;
 import org.apache.camel.spi.Language;
 import org.apache.camel.spi.PropertiesComponent;
+import org.apache.camel.spi.UuidGenerator;
+import org.apache.camel.support.CamelContextHelper;
+import org.apache.camel.support.ClassicUuidGenerator;
+import org.apache.camel.support.DefaultUuidGenerator;
 import org.apache.camel.support.ExchangeHelper;
 import org.apache.camel.support.GroupIterator;
 import org.apache.camel.support.LanguageHelper;
 import org.apache.camel.support.MessageHelper;
+import org.apache.camel.support.ShortUuidGenerator;
+import org.apache.camel.support.SimpleUuidGenerator;
 import org.apache.camel.util.FileUtil;
 import org.apache.camel.util.InetAddressUtil;
 import org.apache.camel.util.ObjectHelper;
@@ -69,6 +77,14 @@ public final class CSimpleHelper {
     private CSimpleHelper() {
     }
 
+    public static <T> T convertTo(Exchange exchange, Class<T> type, Object 
value) {
+        return exchange.getContext().getTypeConverter().convertTo(type, 
exchange, value);
+    }
+
+    public static <T> T tryConvertTo(Exchange exchange, Class<T> type, Object 
value) {
+        return exchange.getContext().getTypeConverter().tryConvertTo(type, 
exchange, value);
+    }
+
     public static <T> T messageAs(Exchange exchange, Class<T> type) {
         return exchange.getMessage(type);
     }
@@ -355,9 +371,7 @@ public final class CSimpleHelper {
     private static Object doDate(Exchange exchange, String commandWithOffsets, 
String timezone, String pattern) {
         final String command = commandWithOffsets.split("[+-]", 2)[0].trim();
         final List<Long> offsets = 
LanguageHelper.captureOffsets(commandWithOffsets, OFFSET_PATTERN);
-
         Date date = evalDate(exchange, command);
-
         return LanguageHelper.applyDateOffsets(date, offsets, pattern, 
timezone);
     }
 
@@ -695,4 +709,53 @@ public final class CSimpleHelper {
         return num;
     }
 
+    public static Object join(Exchange exchange, Object value, String 
separator, String prefix) {
+        Iterator<?> it = convertTo(exchange, Iterator.class, value);
+        StringBuilder sb = new StringBuilder();
+        while (it.hasNext()) {
+            Object o = it.next();
+            if (o != null) {
+                String s = tryConvertTo(exchange, String.class, o);
+                if (s != null) {
+                    if (!sb.isEmpty()) {
+                        sb.append(separator);
+                    }
+                    if (prefix != null) {
+                        sb.append(prefix);
+                    }
+                    sb.append(s);
+                }
+            }
+        }
+        return sb.toString();
+    }
+
+    public static Object hash(Exchange exchange, Object value, String 
algorithm) {
+        byte[] data = convertTo(exchange, byte[].class, value);
+        if (data != null && data.length > 0) {
+            try {
+                MessageDigest digest = MessageDigest.getInstance(algorithm);
+                byte[] bytes = digest.digest(data);
+                return StringHelper.bytesToHex(bytes);
+            } catch (Exception e) {
+                throw 
CamelExecutionException.wrapCamelExecutionException(exchange, e);
+            }
+        }
+        return null;
+    }
+
+    public static UuidGenerator createUuidGenerator(Exchange exchange, String 
generator) {
+        if ("classic".equalsIgnoreCase(generator)) {
+            return new ClassicUuidGenerator();
+        } else if ("short".equals(generator)) {
+            return new ShortUuidGenerator();
+        } else if ("simple".equals(generator)) {
+            return new SimpleUuidGenerator();
+        } else if (generator == null || "default".equals(generator)) {
+            return new DefaultUuidGenerator();
+        } else {
+            // lookup custom generator
+            return CamelContextHelper.mandatoryLookup(exchange.getContext(), 
generator, UuidGenerator.class);
+        }
+    }
 }
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 0b9d67349bb..01f2080a7c2 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
@@ -41,6 +41,16 @@ import org.apache.camel.language.simple.types.TokenType;
  */
 public abstract class BaseSimpleParser {
 
+    /**
+     * Marker used for start of generated code for csimple
+     */
+    public static final String CODE_START = "\"@@code[[";
+
+    /**
+     * Marker used for end of generated code for csimple
+     */
+    public static final String CODE_END = "]]code@@\"";
+
     protected final CamelContext camelContext;
     protected final String expression;
     protected final List<SimpleToken> tokens = new ArrayList<>();
@@ -106,7 +116,7 @@ public abstract class BaseSimpleParser {
     }
 
     /**
-     * Prepares blocks, such as functions, single or double quoted texts.
+     * Prepares blocks, such as functions, single or double-quoted texts.
      * <p/>
      * This process prepares the {@link Block}s in the AST. This is done by 
linking child {@link SimpleNode nodes} which
      * are within the start and end of the blocks, as child to the given 
block. This is done to have the AST graph
diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionBuilder.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionBuilder.java
index 54e59bba477..dab6fdb1ec4 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionBuilder.java
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionBuilder.java
@@ -32,6 +32,7 @@ import org.apache.camel.Exchange;
 import org.apache.camel.ExchangePropertyKey;
 import org.apache.camel.Expression;
 import org.apache.camel.InvalidPayloadException;
+import org.apache.camel.Predicate;
 import org.apache.camel.spi.ClassResolver;
 import org.apache.camel.spi.ExchangeFormatter;
 import org.apache.camel.spi.Language;
@@ -176,6 +177,41 @@ public final class SimpleExpressionBuilder {
         };
     }
 
+    /**
+     * A ternary condition expression
+     */
+    public static Expression iifExpression(final String predicate, final 
String trueValue, final String falseValue) {
+        return new ExpressionAdapter() {
+            private Predicate pred;
+            private Expression expTrue;
+            private Expression expFalse;
+
+            @Override
+            public void init(CamelContext context) {
+                pred = 
context.resolveLanguage("simple").createPredicate(predicate);
+                pred.init(context);
+                expTrue = 
context.resolveLanguage("simple").createExpression(trueValue);
+                expTrue.init(context);
+                expFalse = 
context.resolveLanguage("simple").createExpression(falseValue);
+                expFalse.init(context);
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                if (pred.matches(exchange)) {
+                    return expTrue.evaluate(exchange, Object.class);
+                } else {
+                    return expFalse.evaluate(exchange, Object.class);
+                }
+            }
+
+            @Override
+            public String toString() {
+                return "iif(" + predicate + "," + trueValue + "," + falseValue 
+ ")";
+            }
+        };
+    }
+
     /**
      * Joins together the values from the expression
      */
@@ -391,7 +427,7 @@ public final class SimpleExpressionBuilder {
     }
 
     /**
-     * Returns a uuid string based on the given generator (default, classic, 
short, simple)
+     * Returns an uuid string based on the given generator (default, classic, 
short, simple)
      */
     public static Expression uuidExpression(final String generator) {
         return new ExpressionAdapter() {
@@ -1110,7 +1146,7 @@ public final class SimpleExpressionBuilder {
     }
 
     /**
-     * Returns the expression for the exchanges exception invoking methods 
defined in a simple OGNL notation
+     * Returns the expression for the exchange's exception invoking methods 
defined in a simple OGNL notation
      *
      * @param ognl methods to invoke on the body in a simple OGNL syntax
      */
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 03587e0d8ee..63578bfccbc 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
@@ -95,9 +95,9 @@ public class SimpleExpressionParser extends BaseSimpleParser {
             nextToken();
         }
 
-        // now after parsing we need a bit of work to do, to make it easier to 
turn the tokens
+        // now after parsing, we need a bit of work to do, to make it easier 
to turn the tokens
         // into an ast, and then from the ast, to Camel expression(s).
-        // hence why there is a number of tasks going on below to accomplish 
this
+        // hence why there are a number of tasks going on below to accomplish 
this
 
         // turn the tokens into the ast model
         parseAndCreateAstModel();
@@ -153,7 +153,7 @@ public class SimpleExpressionParser extends 
BaseSimpleParser {
                 continue;
             }
 
-            // if no token was created then its a character/whitespace/escaped 
symbol
+            // if no token was created, then it's a 
character/whitespace/escaped symbol
             // which we need to add together in the same image
             if (imageToken == null) {
                 imageToken = new LiteralExpression(token);
@@ -240,7 +240,10 @@ public class SimpleExpressionParser extends 
BaseSimpleParser {
                 }
             }
         }
-        return sb.toString();
+        String code = sb.toString();
+        code = code.replace(BaseSimpleParser.CODE_START, "");
+        code = code.replace(BaseSimpleParser.CODE_END, "");
+        return code;
     }
 
     // --------------------------------------------------------------
@@ -249,11 +252,11 @@ public class SimpleExpressionParser extends 
BaseSimpleParser {
 
     // the expression parser only understands
     // - template = literal texts with can contain embedded functions
-    // - function = simple functions such as ${body} etc
-    // - unary operator = operator attached to the left hand side node
+    // - function = simple functions such as ${body} etc.
+    // - unary operator = operator attached to the left-hand side node
 
     protected void templateText() {
-        // for template we accept anything but functions
+        // for template, we accept anything but functions
         while (!token.getType().isFunctionStart() && 
!token.getType().isFunctionEnd() && !token.getType().isEol()) {
             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 b7df246923b..1ba3f142fca 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
@@ -187,7 +187,10 @@ public class SimplePredicateParser extends 
BaseSimpleParser {
                 sb.append(exp);
             }
         }
-        return sb.toString();
+        String code = sb.toString();
+        code = code.replace(BaseSimpleParser.CODE_START, "");
+        code = code.replace(BaseSimpleParser.CODE_END, "");
+        return code;
     }
 
     /**
diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/BinaryExpression.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/BinaryExpression.java
index 76931d3b3c8..e6349c533d7 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/BinaryExpression.java
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/BinaryExpression.java
@@ -26,6 +26,7 @@ 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.BinaryOperatorType;
 import org.apache.camel.language.simple.types.SimpleIllegalSyntaxException;
 import org.apache.camel.language.simple.types.SimpleParserException;
@@ -282,6 +283,10 @@ public class BinaryExpression extends BaseSimpleNode {
 
     @Override
     public String createCode(String expression) throws SimpleParserException {
+        return BaseSimpleParser.CODE_START + doCreateCode(expression) + 
BaseSimpleParser.CODE_END;
+    }
+
+    private String doCreateCode(String expression) throws 
SimpleParserException {
         org.apache.camel.util.ObjectHelper.notNull(left, "left node", this);
         org.apache.camel.util.ObjectHelper.notNull(right, "right node", this);
 
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 99caa50af08..9fc06ecd133 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
@@ -20,6 +20,7 @@ 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.LogicalOperatorType;
 import org.apache.camel.language.simple.types.SimpleParserException;
 import org.apache.camel.language.simple.types.SimpleToken;
@@ -123,6 +124,10 @@ public class LogicalExpression extends BaseSimpleNode {
 
     @Override
     public String createCode(String expression) throws SimpleParserException {
+        return BaseSimpleParser.CODE_START + doCreateCode(expression) + 
BaseSimpleParser.CODE_END;
+    }
+
+    private String doCreateCode(String expression) throws 
SimpleParserException {
         ObjectHelper.notNull(left, "left node", this);
         ObjectHelper.notNull(right, "right node", this);
 
diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java
index 4705ae54880..e0801692538 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java
@@ -24,6 +24,7 @@ import java.util.Map;
 import org.apache.camel.CamelContext;
 import org.apache.camel.Expression;
 import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.language.simple.BaseSimpleParser;
 import org.apache.camel.language.simple.SimpleExpressionBuilder;
 import org.apache.camel.language.simple.types.SimpleParserException;
 import org.apache.camel.language.simple.types.SimpleToken;
@@ -278,7 +279,7 @@ public class SimpleFunctionExpression extends 
LiteralExpression {
         if (remainder != null) {
             String[] parts = remainder.split(":", 2);
             if (parts.length > 2) {
-                throw new SimpleParserException("Valid syntax: 
${propertiesExist:key was: " + function, token.getIndex());
+                throw new SimpleParserException("Valid syntax: 
${propertiesExist:key} was: " + function, token.getIndex());
             }
             String key = parts[0];
             boolean negate = key != null && key.startsWith("!");
@@ -314,7 +315,7 @@ public class SimpleFunctionExpression extends 
LiteralExpression {
         if (remainder != null) {
             Expression exp = SimpleExpressionBuilder.typeExpression(remainder);
             exp.init(camelContext);
-            // we want to cache this expression so we wont re-evaluate it as 
the type/constant wont change
+            // we want to cache this expression, so we won't re-evaluate it as 
the type/constant won't change
             return SimpleExpressionBuilder.cacheExpression(exp);
         }
 
@@ -403,7 +404,7 @@ public class SimpleFunctionExpression extends 
LiteralExpression {
             remainder = ifStartsWithReturnRemainder("in.body", function);
         }
         if (remainder != null) {
-            // OGNL must start with a . ? or [
+            // OGNL must start with a ".", "?" or "[".
             boolean ognlStart = remainder.startsWith(".") || 
remainder.startsWith("?") || remainder.startsWith("[");
             boolean invalid = !ognlStart || 
OgnlHelper.isInvalidValidOgnlExpression(remainder);
             if (invalid) {
@@ -702,7 +703,7 @@ public class SimpleFunctionExpression extends 
LiteralExpression {
             String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', 
false);
             if (tokens.length > 3) {
                 throw new SimpleParserException(
-                        "Valid syntax: ${replace(num,num,expression)} was: " + 
function, token.getIndex());
+                        "Valid syntax: ${substring(num,num,expression)} was: " 
+ function, token.getIndex());
             }
             String num1 = tokens[0];
             String num2 = "0";
@@ -841,6 +842,21 @@ public class SimpleFunctionExpression extends 
LiteralExpression {
             }
             return SimpleExpressionBuilder.newEmptyExpression(value);
         }
+        // iif function
+        remainder = ifStartsWithReturnRemainder("iif(", function);
+        if (remainder != null) {
+            String values = StringHelper.beforeLast(remainder, ")");
+            if (values == null || ObjectHelper.isEmpty(values)) {
+                throw new SimpleParserException(
+                        "Valid syntax: 
${iif(predicate,trueExpression,falseExpression)} was: " + function, 
token.getIndex());
+            }
+            String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',');
+            if (tokens.length > 3) {
+                throw new SimpleParserException(
+                        "Valid syntax: 
${iif(predicate,trueExpression,falseExpression)} was: " + function, 
token.getIndex());
+            }
+            return SimpleExpressionBuilder.iifExpression(tokens[0].trim(), 
tokens[1].trim(), tokens[2].trim());
+        }
 
         return null;
     }
@@ -857,6 +873,10 @@ public class SimpleFunctionExpression extends 
LiteralExpression {
 
     @Override
     public String createCode(String expression) throws SimpleParserException {
+        return BaseSimpleParser.CODE_START + doCreateCode(expression) + 
BaseSimpleParser.CODE_END;
+    }
+
+    private String doCreateCode(String expression) throws 
SimpleParserException {
         String function = getText();
 
         // return the function directly if we can create function without 
analyzing the prefix
@@ -907,6 +927,7 @@ public class SimpleFunctionExpression extends 
LiteralExpression {
             }
             return "exceptionAs(exchange, " + type + ")" + 
ognlCodeMethods(remainder, type);
         }
+
         // Exception OGNL
         remainder = ifStartsWithReturnRemainder("exception", function);
         if (remainder != null) {
@@ -1372,7 +1393,7 @@ public class SimpleFunctionExpression extends 
LiteralExpression {
             }
 
             // the key can contain index as it may be a map header.foo[0]
-            // and the key can also be OGNL (eg if there is a dot)
+            // and the key can also be OGNL (e.g., if there is a dot)
             boolean index = false;
             List<String> parts = splitOgnl(key);
             if (!parts.isEmpty()) {
@@ -1389,7 +1410,7 @@ public class SimpleFunctionExpression extends 
LiteralExpression {
             }
             if (index) {
                 // is there any index, then we should use headerAsIndex 
function instead
-                // (use splitOgnl which assembles multiple indexes into a 
single part)
+                // (use splitOgnl which assembles multiple indexes into a 
single part?)
                 String func = "headerAsIndex(\"" + parts.get(0) + "\", 
Object.class, \"" + parts.get(1) + "\")";
                 if (parts.size() > 2) {
                     String last = String.join("", parts.subList(2, 
parts.size()));
@@ -1675,10 +1696,10 @@ public class SimpleFunctionExpression extends 
LiteralExpression {
                                                 + function,
                         token.getIndex());
             }
-            String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',');
+            String[] tokens = codeSplitSafe(values, ',', true, true);
             if (tokens.length > 2) {
                 throw new SimpleParserException(
-                        "Valid syntax: ${replace(num,num)} was: " + function, 
token.getIndex());
+                        "Valid syntax: ${substring(num,num)} was: " + 
function, token.getIndex());
             }
             String num1 = tokens[0];
             String num2 = "0";
@@ -1721,7 +1742,7 @@ public class SimpleFunctionExpression extends 
LiteralExpression {
                         "Valid syntax: ${replace(from,to)} was: " + function,
                         token.getIndex());
             }
-            String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',');
+            String[] tokens = codeSplitSafe(values, ',', true, false);
             if (tokens.length > 2) {
                 throw new SimpleParserException(
                         "Valid syntax: ${replace(from,to)} was: " + function, 
token.getIndex());
@@ -1778,10 +1799,49 @@ public class SimpleFunctionExpression extends 
LiteralExpression {
             return "messageHistory(exchange, true)";
         }
 
+        // join
+        remainder = ifStartsWithReturnRemainder("join(", function);
+        if (remainder != null) {
+            String values = StringHelper.beforeLast(remainder, ")");
+            String separator = "\",\"";
+            String prefix = null;
+            String exp = "body";
+            if (ObjectHelper.isNotEmpty(values)) {
+                String[] tokens = codeSplitSafe(values, ',', true, true);
+                if (tokens.length > 3) {
+                    throw new SimpleParserException(
+                            "Valid syntax: 
${join(separator,prefix,expression)} was: " + function, token.getIndex());
+                }
+                // single quotes should be double quotes
+                for (int i = 0; i < tokens.length; i++) {
+                    String s = tokens[i];
+                    if (StringHelper.isSingleQuoted(s)) {
+                        s = StringHelper.removeLeadingAndEndingQuotes(s);
+                        s = StringQuoteHelper.doubleQuote(s);
+                        tokens[i] = s;
+                    } else if (i < 2 && !StringHelper.isDoubleQuoted(s)) {
+                        s = StringQuoteHelper.doubleQuote(s);
+                        tokens[i] = s;
+                    }
+                }
+                if (tokens.length == 3) {
+                    separator = tokens[0];
+                    prefix = tokens[1];
+                    exp = tokens[2];
+                } else if (tokens.length == 2) {
+                    separator = tokens[0];
+                    prefix = tokens[1];
+                } else {
+                    separator = tokens[0];
+                }
+            }
+            return "var val = " + exp + ";\n        return join(exchange, val, 
" + separator + ", " + prefix + ");";
+        }
+
         // empty function
         remainder = ifStartsWithReturnRemainder("empty(", function);
         if (remainder != null) {
-            String value = StringHelper.before(remainder, ")");
+            String value = StringHelper.beforeLast(remainder, ")");
             if (ObjectHelper.isEmpty(value)) {
                 throw new SimpleParserException(
                         "Valid syntax: ${empty(<type>)} but was: " + function, 
token.getIndex());
@@ -1790,6 +1850,80 @@ public class SimpleFunctionExpression extends 
LiteralExpression {
             return "empty(exchange, " + value + ")";
         }
 
+        // hash function
+        remainder = ifStartsWithReturnRemainder("hash(", function);
+        if (remainder != null) {
+            String values = StringHelper.beforeLast(remainder, ")");
+            if (values == null || ObjectHelper.isEmpty(values)) {
+                throw new SimpleParserException(
+                        "Valid syntax: ${hash(value,algorithm)} or 
${hash(value)} was: " + function, token.getIndex());
+            }
+            String[] tokens = codeSplitSafe(values, ',', true, true);
+            if (tokens.length > 2) {
+                throw new SimpleParserException(
+                        "Valid syntax: ${hash(value,algorithm)} or 
${hash(value)} was: " + function, token.getIndex());
+            }
+            // single quotes should be double quotes
+            for (int i = 0; i < tokens.length; i++) {
+                String s = tokens[i];
+                if (StringHelper.isSingleQuoted(s)) {
+                    s = StringHelper.removeLeadingAndEndingQuotes(s);
+                    s = StringQuoteHelper.doubleQuote(s);
+                    tokens[i] = s;
+                }
+            }
+            String algo = "\"SHA-256\"";
+            if (tokens.length == 2) {
+                algo = tokens[1];
+                if (!StringHelper.isQuoted(algo)) {
+                    algo = StringQuoteHelper.doubleQuote(algo);
+                }
+            }
+            return "var val = " + tokens[0] + ";\n        return 
hash(exchange, val, " + algo + ");";
+        }
+
+        // uuid function
+        remainder = ifStartsWithReturnRemainder("uuid", function);
+        if (remainder == null && "uuid".equals(function)) {
+            remainder = "(default)";
+        }
+        if (remainder != null) {
+            String generator = StringHelper.between(remainder, "(", ")");
+            if (generator == null) {
+                generator = "default";
+            }
+            generator = StringQuoteHelper.doubleQuote(generator);
+            return "if (uuid == null) uuid = createUuidGenerator(exchange, " + 
generator + "); return uuid.generateUuid();";
+        }
+
+        // iif function
+        remainder = ifStartsWithReturnRemainder("iif(", function);
+        if (remainder != null) {
+            String values = StringHelper.beforeLast(remainder, ")");
+            if (values == null || ObjectHelper.isEmpty(values)) {
+                throw new SimpleParserException(
+                        "Valid syntax: 
${iif(predicate,trueExpression,falseExpression)} was: " + function, 
token.getIndex());
+            }
+            String[] tokens = codeSplitSafe(values, ',', true, true);
+            if (tokens.length != 3) {
+                throw new SimpleParserException(
+                        "Valid syntax: 
${iif(predicate,trueExpression,falseExpression)} was: " + function, 
token.getIndex());
+            }
+            // single quotes should be double quotes
+            for (int i = 0; i < 3; i++) {
+                String s = tokens[i];
+                if (StringHelper.isSingleQuoted(s)) {
+                    s = StringHelper.removeLeadingAndEndingQuotes(s);
+                    s = StringQuoteHelper.doubleQuote(s);
+                    tokens[i] = s;
+                }
+            }
+
+            return "Object o = " + tokens[0]
+                   + ";\n        boolean b = convertTo(exchange, 
boolean.class, o);\n        return b ? "
+                   + tokens[1] + " : " + tokens[2];
+        }
+
         return null;
     }
 
@@ -1898,4 +2032,149 @@ public class SimpleFunctionExpression extends 
LiteralExpression {
         }
     }
 
+    private static String[] codeSplitSafe(String input, char separator, 
boolean trim, boolean keepQuotes) {
+        if (input == null) {
+            return null;
+        }
+
+        if (input.indexOf(separator) == -1) {
+            if (input.length() > 1) {
+                char ch = input.charAt(0);
+                char ch2 = input.charAt(input.length() - 1);
+                boolean singleQuoted = ch == '\'' && ch2 == '\'';
+                boolean doubleQuoted = ch == '"' && ch2 == '"';
+                if (!keepQuotes && (singleQuoted || doubleQuoted)) {
+                    input = input.substring(1, input.length() - 1);
+                    // do not trim quoted text
+                } else if (trim) {
+                    input = input.trim();
+                }
+            }
+            // no separator in data, so return single string with input as is
+            return new String[] { input };
+        }
+
+        List<String> answer = new ArrayList<>();
+        StringBuilder sb = new StringBuilder();
+
+        int codeLevel = 0;
+        boolean singleQuoted = false;
+        boolean doubleQuoted = false;
+        boolean separating = false;
+
+        for (int i = 0; i < input.length(); i++) {
+            char ch = input.charAt(i);
+            char prev = i > 0 ? input.charAt(i - 1) : 0;
+            boolean isQuoting = singleQuoted || doubleQuoted;
+            boolean last = i == input.length() - 1;
+
+            // do not split inside code blocks
+            if (input.indexOf(BaseSimpleParser.CODE_START, i) == i) {
+                codeLevel++;
+                sb.append(BaseSimpleParser.CODE_START);
+                i = i + BaseSimpleParser.CODE_START.length() - 1;
+                continue;
+            } else if (input.indexOf(BaseSimpleParser.CODE_END, i) == i) {
+                codeLevel--;
+                sb.append(BaseSimpleParser.CODE_END);
+                i = i + BaseSimpleParser.CODE_END.length() - 1;
+                continue;
+            }
+            if (codeLevel > 0) {
+                sb.append(ch);
+                continue;
+            }
+
+            if (!doubleQuoted && ch == '\'') {
+                if (singleQuoted && prev == ch && sb.isEmpty()) {
+                    // its an empty quote so add empty text
+                    if (keepQuotes) {
+                        answer.add("''");
+                    } else {
+                        answer.add("");
+                    }
+                }
+                // special logic needed if this quote is the end
+                if (last) {
+                    if (singleQuoted && !sb.isEmpty()) {
+                        String text = sb.toString();
+                        // do not trim a quoted string
+                        if (keepQuotes) {
+                            answer.add(text + "'"); // append ending quote
+                        } else {
+                            answer.add(text);
+                        }
+                        sb.setLength(0);
+                    }
+                    break; // break out as we are finished
+                }
+                singleQuoted = !singleQuoted;
+                if (keepQuotes) {
+                    sb.append(ch);
+                }
+                continue;
+            } else if (!singleQuoted && ch == '"') {
+                if (doubleQuoted && prev == ch && sb.isEmpty()) {
+                    // its an empty quote so add empty text
+                    if (keepQuotes) {
+                        answer.add("\""); // append ending quote
+                    } else {
+                        answer.add("");
+                    }
+                }
+                // special logic needed if this quote is the end
+                if (last) {
+                    if (doubleQuoted && !sb.isEmpty()) {
+                        String text = sb.toString();
+                        // do not trim a quoted string
+                        if (keepQuotes) {
+                            answer.add(text + "\"");
+                        } else {
+                            answer.add(text);
+                        }
+                        sb.setLength(0);
+                    }
+                    break; // break out as we are finished
+                }
+                doubleQuoted = !doubleQuoted;
+                if (keepQuotes) {
+                    sb.append(ch);
+                }
+                continue;
+            } else if (!isQuoting && ch == separator) {
+                separating = true;
+                // add as answer if we are not in a quote
+                if (!sb.isEmpty()) {
+                    String text = sb.toString();
+                    if (trim) {
+                        text = text.trim();
+                    }
+                    answer.add(text);
+                    sb.setLength(0);
+                }
+                // we should avoid adding the separator
+                continue;
+            }
+
+            if (trim && !isQuoting && separating && separator != ' ' && ch == 
' ') {
+                continue;
+            }
+            separating = false;
+
+            // append char
+            sb.append(ch);
+        }
+
+        // any leftover
+        if (!sb.isEmpty()) {
+            String text = sb.toString();
+            if (trim) {
+                text = text.trim();
+            }
+            answer.add(text);
+        }
+
+        return answer.toArray(new String[0]);
+    }
+
 }
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 ddd8f9e3f23..d62a9cd7fc0 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
@@ -151,7 +151,7 @@ public class SimpleFunctionStart extends BaseSimpleNode 
implements BlockStart {
     @Override
     public String createCode(String expression) throws SimpleParserException {
         String answer;
-        // a function can either be a simple literal function, or contain 
nested functions
+        // a function can either be a simple literal function or contain 
nested functions
         if (block.getChildren().size() == 1 && block.getChildren().get(0) 
instanceof LiteralNode) {
             answer = doCreateLiteralCode(expression);
         } else {
diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/UnaryExpression.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/UnaryExpression.java
index e87c8a5d4e4..e39ab05d16c 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/UnaryExpression.java
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/UnaryExpression.java
@@ -22,6 +22,7 @@ import org.apache.camel.Exchange;
 import org.apache.camel.Expression;
 import org.apache.camel.NoTypeConversionAvailableException;
 import org.apache.camel.RuntimeCamelException;
+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.language.simple.types.UnaryOperatorType;
@@ -147,6 +148,10 @@ public class UnaryExpression extends BaseSimpleNode {
 
     @Override
     public String createCode(String expression) throws SimpleParserException {
+        return BaseSimpleParser.CODE_START + doCreateCode(expression) + 
BaseSimpleParser.CODE_END;
+    }
+
+    private String doCreateCode(String expression) throws 
SimpleParserException {
         ObjectHelper.notNull(left, "left node", this);
 
         final String number = left.createCode(expression);
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 46bec4cc7b7..3b16729a7b6 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
@@ -2114,6 +2114,21 @@ public class SimpleTest extends LanguageTestSupport {
         assertExpression("${substring(0,0,${header.foo})}", "1234567890");
     }
 
+    @Test
+    public void testIif() {
+        exchange.getIn().setHeader("foo", 44);
+        assertExpression("${iif(${header.foo} > 0,'positive','negative')}", 
"positive");
+        exchange.getIn().setHeader("foo", -123);
+        assertExpression("${iif(${header.foo} > 0,'positive','negative')}", 
"negative");
+
+        exchange.getIn().setBody("Hello World");
+        exchange.getIn().setHeader("foo", 44);
+        assertExpression("${iif(${header.foo} > 0,${body},'Bye World')}", 
"Hello World");
+        exchange.getIn().setHeader("foo", -123);
+        assertExpression("${iif(${header.foo} > 0,${body},'Bye World')}", "Bye 
World");
+        assertExpression("${iif(${header.foo} > 0,${body},${null})}", null);
+    }
+
     @Test
     public void testListRemoveByInstance() {
         List<Object> data = new ArrayList<>();

Reply via email to