You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2026/06/04 05:27:06 UTC

(camel) branch main updated: CAMEL-22894: Split SimpleExpressionBuilder into domain-aligned builder classes (#23719)

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 2123fcd28946 CAMEL-22894: Split SimpleExpressionBuilder into domain-aligned builder classes (#23719)
2123fcd28946 is described below

commit 2123fcd289460b3e876bb96d5b9db773575b0a28
Author: Adriano Machado <60...@users.noreply.github.com>
AuthorDate: Thu Jun 4 01:26:59 2026 -0400

    CAMEL-22894: Split SimpleExpressionBuilder into domain-aligned builder classes (#23719)
    
    * CAMEL-22894: Split SimpleExpressionBuilder into domain-aligned builder classes
    
    Replace the monolithic SimpleExpressionBuilder (3,523 lines, 100+ static
    methods) with seven focused builder classes:
    
    - CollectionExpressionBuilder: list, map, filter, sort, set-header, etc.
    - DateExpressionBuilder:       date/time expressions with offset support
    - FileExpressionBuilder:       file name, path, size, extension, etc.
    - MathExpressionBuilder:       abs, floor, ceil, sum, min, max, average
    - MiscExpressionBuilder:       uuid, random, type, hash, convert, etc.
    - OgnlExpressionBuilder:       OGNL body/header/variable/exchange navigation
    - StringExpressionBuilder:     pad, trim, substring, replace, contains, etc.
    
    KeyedOgnlExpressionAdapter (inner class) moves to OgnlExpressionBuilder.
    OFFSET_PATTERN static field moves to DateExpressionBuilder.
    
    All 22 function factories and ast/SimpleFunctionExpression updated to import
    their respective domain builder. AttachmentExpressionBuilder (camel-attachments)
    updated to use OgnlExpressionBuilder.KeyedOgnlExpressionAdapter.
    
    Pure mechanical refactor: no behavioral change.
    
    Co-Authored-By: Claude Sonnet 4.6 <no...@anthropic.com>
    
    rh-pre-commit.version: 2.4.0
    rh-pre-commit.check-secrets: ENABLED
    
    * CAMEL-22894: Fix pre-existing bugs surfaced by SimpleExpressionBuilder split
    
    - shuffleExpression.toString() returned "reverse(...)" due to a copy-paste
      error; now returns "shuffle(...)"
    - mapExpression.toString() printed compiled Expression[] object refs instead
      of the source String[] pairs; now prints the source pairs
    - collateExpression(String,String) overwrote the 'exp' field inside
      evaluate(), wrapping an already-grouped iterator on every call and causing
      a data race under concurrent route execution; fixed by using a local
      variable for the grouped expression
    - throwExceptionExpression's catch(Exception) block caught the RuntimeException
      it had just thrown and double-wrapped it, breaking typed onException handlers;
      restructured so RuntimeExceptions propagate unwrapped
    - skipExpression(String,String) unboxed a potentially-null Integer without a
      null check; now throws IllegalArgumentException with a clear message
    - padExpression returned NPE when the string or width expression was null;
      now returns null for a null string and throws IllegalArgumentException for
      a null width
    - floor() and ceil() cast Math.floor/ceil to int, silently overflowing for
      values outside Integer range; now returns long
    - range(max) in the csimple code path used min=0 while the simple interpreter
      used min=1; unified to min=1 to match documented behaviour
    
    Tests added to document each pre-existing bug and verify the fix.
    
    Co-Authored-By: Claude Sonnet 4.6 <no...@anthropic.com>
    
    rh-pre-commit.version: 2.4.0
    rh-pre-commit.check-secrets: ENABLED
    
    * CAMEL-22894: Add upgrade guide entries for SimpleExpressionBuilder split and Simple language behavioral changes
    
    Co-Authored-By: Claude Sonnet 4.6 <no...@anthropic.com>
    
    rh-pre-commit.version: 2.4.0
    rh-pre-commit.check-secrets: ENABLED
---
 .../attachment/AttachmentExpressionBuilder.java    |    4 +-
 .../simple/CollectionExpressionBuilder.java        |  696 ++++
 .../language/simple/DateExpressionBuilder.java     |  104 +
 .../language/simple/FileExpressionBuilder.java     |  238 ++
 .../language/simple/MathExpressionBuilder.java     |  337 ++
 .../language/simple/MiscExpressionBuilder.java     | 1095 ++++++
 .../language/simple/OgnlExpressionBuilder.java     |  476 +++
 .../language/simple/SimpleExpressionBuilder.java   | 3523 --------------------
 .../language/simple/StringExpressionBuilder.java   |  804 +++++
 .../simple/ast/SimpleFunctionExpression.java       |   30 +-
 .../simple/functions/BodyFunctionFactory.java      |   10 +-
 .../simple/functions/CollateFunctionFactory.java   |    4 +-
 .../functions/CollectionFunctionFactory.java       |   38 +-
 .../simple/functions/CustomFunctionFactory.java    |    4 +-
 .../simple/functions/DateFunctionFactory.java      |    8 +-
 .../simple/functions/DirectFunctionFactory.java    |    4 +-
 .../simple/functions/ExchangeFunctionFactory.java  |   10 +-
 .../simple/functions/HeaderFunctionFactory.java    |    4 +-
 .../simple/functions/JoinFunctionFactory.java      |    4 +-
 .../simple/functions/MathFunctionFactory.java      |   18 +-
 .../simple/functions/MessageFunctionFactory.java   |    4 +-
 .../simple/functions/MiscFunctionFactory.java      |   42 +-
 .../functions/QueryLanguageFunctionFactory.java    |    4 +-
 .../simple/functions/RandomFunctionFactory.java    |    6 +-
 .../simple/functions/SkipFunctionFactory.java      |    4 +-
 .../simple/functions/StringFunctionFactory.java    |   38 +-
 .../simple/functions/TypeFunctionFactory.java      |    6 +-
 .../simple/functions/VariableFunctionFactory.java  |    4 +-
 .../apache/camel/language/simple/SimpleTest.java   |   13 +-
 .../functions/CollateFunctionFactoryTest.java      |   26 +
 .../functions/CollectionFunctionFactoryTest.java   |   36 +-
 .../simple/functions/MathFunctionFactoryTest.java  |   14 +
 .../simple/functions/MiscFunctionFactoryTest.java  |   45 +
 .../simple/functions/SkipFunctionFactoryTest.java  |   14 +
 .../ROOT/pages/camel-4x-upgrade-guide-4_21.adoc    |   34 +
 35 files changed, 4050 insertions(+), 3651 deletions(-)

diff --git a/components/camel-attachments/src/main/java/org/apache/camel/attachment/AttachmentExpressionBuilder.java b/components/camel-attachments/src/main/java/org/apache/camel/attachment/AttachmentExpressionBuilder.java
index c8fe2220ce82..7f5967613a46 100644
--- a/components/camel-attachments/src/main/java/org/apache/camel/attachment/AttachmentExpressionBuilder.java
+++ b/components/camel-attachments/src/main/java/org/apache/camel/attachment/AttachmentExpressionBuilder.java
@@ -29,7 +29,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.SimpleExpressionBuilder;
+import org.apache.camel.language.simple.OgnlExpressionBuilder;
 import org.apache.camel.support.ExpressionAdapter;
 import org.apache.camel.support.ObjectHelper;
 import org.apache.camel.support.ResourceHelper;
@@ -219,7 +219,7 @@ public class AttachmentExpressionBuilder {
      * @param ognl methods to invoke on the attachment in a simple OGNL syntax
      */
     public static Expression attachmentOgnlExpression(final String ognl) {
-        return new SimpleExpressionBuilder.KeyedOgnlExpressionAdapter(
+        return new OgnlExpressionBuilder.KeyedOgnlExpressionAdapter(
                 ognl, "attachmentOgnl(" + ognl + ")",
                 (exchange, exp) -> {
                     String key = exp.evaluate(exchange, String.class);
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/CollectionExpressionBuilder.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/CollectionExpressionBuilder.java
new file mode 100644
index 000000000000..54b38e923e31
--- /dev/null
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/CollectionExpressionBuilder.java
@@ -0,0 +1,696 @@
+/*
+ * 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;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.Exchange;
+import org.apache.camel.Expression;
+import org.apache.camel.Predicate;
+import org.apache.camel.spi.ClassResolver;
+import org.apache.camel.support.ExchangeHelper;
+import org.apache.camel.support.ExpressionAdapter;
+import org.apache.camel.support.builder.ExpressionBuilder;
+import org.apache.camel.util.ObjectHelper;
+
+/**
+ * Expression builder for collection and list functions used by the simple language.
+ */
+public final class CollectionExpressionBuilder {
+
+    private CollectionExpressionBuilder() {
+    }
+
+    /**
+     * An expression that returns the distinct values from the expressions
+     */
+    public static Expression distinctExpression(String[] values) {
+        return new ExpressionAdapter() {
+
+            private final Expression[] exps = new Expression[values != null ? values.length : 0];
+
+            @Override
+            public void init(CamelContext context) {
+                for (int i = 0; values != null && i < values.length; i++) {
+                    Expression exp = context.resolveLanguage("simple").createExpression(values[i]);
+                    exp.init(context);
+                    exps[i] = exp;
+                }
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                Set<Object> answer = new LinkedHashSet<>();
+                for (Expression exp : exps) {
+                    Object o = exp.evaluate(exchange, Object.class);
+                    // this may be an object that we can iterate
+                    Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
+                    for (Object i : it) {
+                        answer.add(i);
+                    }
+                }
+                return answer;
+            }
+
+            @Override
+            public String toString() {
+                return "distinct(" + Arrays.toString(values) + ")";
+            }
+        };
+    }
+
+    /**
+     * An expression that returns the elements in reverse order
+     */
+    public static Expression reverseExpression(String[] values) {
+        return new ExpressionAdapter() {
+
+            private final Expression[] exps = new Expression[values != null ? values.length : 0];
+
+            @Override
+            public void init(CamelContext context) {
+                for (int i = 0; values != null && i < values.length; i++) {
+                    Expression exp = context.resolveLanguage("simple").createExpression(values[i]);
+                    exp.init(context);
+                    exps[i] = exp;
+                }
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                List<Object> answer = new ArrayList<>();
+                for (Expression exp : exps) {
+                    Object o = exp.evaluate(exchange, Object.class);
+                    // this may be an object that we can iterate
+                    Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
+                    for (Object i : it) {
+                        answer.add(i);
+                    }
+                }
+                Collections.reverse(answer);
+                return answer;
+            }
+
+            @Override
+            public String toString() {
+                return "reverse(" + Arrays.toString(values) + ")";
+            }
+        };
+    }
+
+    /**
+     * An expression that returns the elements in random order
+     */
+    public static Expression shuffleExpression(String[] values) {
+        return new ExpressionAdapter() {
+
+            private final Expression[] exps = new Expression[values != null ? values.length : 0];
+
+            @Override
+            public void init(CamelContext context) {
+                for (int i = 0; values != null && i < values.length; i++) {
+                    Expression exp = context.resolveLanguage("simple").createExpression(values[i]);
+                    exp.init(context);
+                    exps[i] = exp;
+                }
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                List<Object> answer = new ArrayList<>();
+                for (Expression exp : exps) {
+                    Object o = exp.evaluate(exchange, Object.class);
+                    // this may be an object that we can iterate
+                    Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
+                    for (Object i : it) {
+                        answer.add(i);
+                    }
+                }
+                Collections.shuffle(answer);
+                return answer;
+            }
+
+            @Override
+            public String toString() {
+                return "shuffle(" + Arrays.toString(values) + ")";
+            }
+        };
+    }
+
+    /**
+     * An expression that creates an ArrayList
+     */
+    public static Expression listExpression(String[] values) {
+        return new ExpressionAdapter() {
+
+            private final Expression[] exps = new Expression[values != null ? values.length : 0];
+
+            @Override
+            public void init(CamelContext context) {
+                for (int i = 0; values != null && i < values.length; i++) {
+                    Expression exp = context.resolveLanguage("simple").createExpression(values[i]);
+                    exp.init(context);
+                    exps[i] = exp;
+                }
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                List<Object> answer = new ArrayList<>(values != null ? values.length : 0);
+                for (Expression exp : exps) {
+                    Object val = exp.evaluate(exchange, Object.class);
+                    answer.add(val);
+                }
+                return answer;
+            }
+
+            @Override
+            public String toString() {
+                return "list(" + Arrays.toString(values) + ")";
+            }
+        };
+    }
+
+    /**
+     * An expression that creates an LinkedHashMap
+     */
+    public static Expression mapExpression(String[] pairs) {
+        return new ExpressionAdapter() {
+
+            private final Expression[] keys = new Expression[pairs != null ? pairs.length / 2 : 0];
+            private final Expression[] values = new Expression[pairs != null ? pairs.length / 2 : 0];
+
+            @Override
+            public void init(CamelContext context) {
+                for (int i = 0, j = 0; pairs != null && i < pairs.length - 1; j++) {
+                    String key = pairs[i];
+                    String value = pairs[i + 1];
+                    Expression exp = context.resolveLanguage("simple").createExpression(key);
+                    exp.init(context);
+                    keys[j] = exp;
+                    exp = context.resolveLanguage("simple").createExpression(value);
+                    exp.init(context);
+                    values[j] = exp;
+                    i = i + 2;
+                }
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                Map<String, Object> answer = new LinkedHashMap<>(keys.length);
+                for (int i = 0; i < keys.length; i++) {
+                    String key = keys[i].evaluate(exchange, String.class);
+                    Object val = values[i].evaluate(exchange, Object.class);
+                    answer.put(key, val);
+                }
+                return answer;
+            }
+
+            @Override
+            public String toString() {
+                return "map(" + Arrays.toString(pairs) + ")";
+            }
+        };
+    }
+
+    /**
+     * Joins together the values from the expression
+     */
+    public static Expression joinExpression(final String expression, final String separator, final String prefix) {
+        return new ExpressionAdapter() {
+            private Expression exp;
+
+            @Override
+            public void init(CamelContext context) {
+                exp = context.resolveLanguage("simple").createExpression(expression);
+                exp.init(context);
+                exp = ExpressionBuilder.joinExpression(exp, separator, prefix);
+                exp.init(context);
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                return exp.evaluate(exchange, Object.class);
+            }
+
+            @Override
+            public String toString() {
+                if (prefix != null) {
+                    return "join(" + expression + "," + separator + "," + prefix + ")";
+                } else {
+                    return "join(" + expression + "," + separator + ")";
+                }
+            }
+        };
+    }
+
+    /**
+     * Split the String values from the expression using the given separator
+     */
+    public static Expression splitStringExpression(final String expression, final String separator) {
+        return new ExpressionAdapter() {
+            private Expression exp;
+
+            @Override
+            public void init(CamelContext context) {
+                exp = context.resolveLanguage("simple").createExpression(expression);
+                exp.init(context);
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                String text = exp.evaluate(exchange, String.class);
+                if (text == null) {
+                    return null;
+                }
+                return text.split(separator);
+            }
+
+            @Override
+            public String toString() {
+                return "split(" + expression + "," + separator + ")";
+            }
+        };
+    }
+
+    /**
+     * Sorts the expression
+     */
+    public static Expression sortExpression(final String expression, final boolean reverse) {
+        return new ExpressionAdapter() {
+            private Expression exp;
+
+            @Override
+            public void init(CamelContext context) {
+                exp = context.resolveLanguage("simple").createExpression(expression);
+                exp.init(context);
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                List answer = new ArrayList<>();
+                Object o = exp.evaluate(exchange, Object.class);
+                // this may be an object that we can iterate
+                Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
+                for (Object i : it) {
+                    answer.add(i);
+                }
+                Collections.sort(answer);
+                if (reverse) {
+                    Collections.reverse(answer);
+                }
+                return answer;
+            }
+
+            @Override
+            public String toString() {
+                return "sort(" + expression + ")";
+            }
+        };
+    }
+
+    /**
+     * For each value in the source expression then apply the function and return a list of responses from each function
+     */
+    public static Expression forEachExpression(final String source, final String function) {
+        return new ExpressionAdapter() {
+            private CamelContext context;
+            private Expression exp1;
+            private Expression exp2;
+
+            @Override
+            public void init(CamelContext context) {
+                this.context = context;
+                exp1 = context.resolveLanguage("simple").createExpression(source);
+                exp1.init(context);
+                exp2 = context.resolveLanguage("simple").createExpression(function);
+                exp2.init(context);
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                List<Object> answer = new ArrayList<>();
+                Object o = exp1.evaluate(exchange, Object.class);
+                Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
+                for (Object i : it) {
+                    // use a dummy exchange as the input is to be the message body
+                    Exchange dummy = ExchangeHelper.createCopy(exchange, true);
+                    dummy.getMessage().setBody(i);
+                    Object out = exp2.evaluate(dummy, Object.class);
+                    if (out != null) {
+                        answer.add(out);
+                    }
+                }
+                return answer;
+            }
+
+            @Override
+            public String toString() {
+                return "forEach(" + source + ", " + function + ")";
+            }
+        };
+    }
+
+    /**
+     * Adds the result of the function to the source list
+     */
+    public static Expression listAddExpression(final String source, final String function) {
+        return new ExpressionAdapter() {
+            private CamelContext context;
+            private Expression exp1;
+            private Expression exp2;
+
+            @Override
+            public void init(CamelContext context) {
+                this.context = context;
+                exp1 = context.resolveLanguage("simple").createExpression(source);
+                exp1.init(context);
+                exp2 = context.resolveLanguage("simple").createExpression(function);
+                exp2.init(context);
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                Collection<Object> col = exp1.evaluate(exchange, Collection.class);
+                if (col != null) {
+                    Object value = exp2.evaluate(exchange, Object.class);
+                    if (value != null) {
+                        col.add(value);
+                    }
+                }
+                return col;
+            }
+
+            @Override
+            public String toString() {
+                return "listAdd(" + source + ", " + function + ")";
+            }
+        };
+    }
+
+    /**
+     * Removes the result of the function from the source list
+     */
+    public static Expression listRemoveExpression(final String source, final String function) {
+        return new ExpressionAdapter() {
+            private CamelContext context;
+            private Expression exp1;
+            private Expression exp2;
+
+            @Override
+            public void init(CamelContext context) {
+                this.context = context;
+                exp1 = context.resolveLanguage("simple").createExpression(source);
+                exp1.init(context);
+                exp2 = context.resolveLanguage("simple").createExpression(function);
+                exp2.init(context);
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                List<Object> list = exp1.evaluate(exchange, List.class);
+                if (list != null) {
+                    Object value = exp2.evaluate(exchange, Object.class);
+                    if (value != null) {
+                        boolean removed = list.remove(value);
+                        if (!removed) {
+                            Integer pos;
+                            // special name to remove last
+                            if ("last".equals(value)) {
+                                pos = list.size() - 1;
+                            } else {
+                                // this may be an integer
+                                pos = context.getTypeConverter().tryConvertTo(int.class, exchange, value);
+                            }
+                            if (pos != null) {
+                                if (pos >= 0 && pos < list.size()) {
+                                    list.remove((int) pos);
+                                }
+                            }
+                        }
+                    }
+                }
+                return list;
+            }
+
+            @Override
+            public String toString() {
+                return "listRemove(" + source + ", " + function + ")";
+            }
+        };
+    }
+
+    /**
+     * Adds the result of the function to the source map
+     */
+    public static Expression mapAddExpression(final String source, final String key, final String function) {
+        return new ExpressionAdapter() {
+            private CamelContext context;
+            private Expression exp1;
+            private Expression exp2;
+
+            @Override
+            public void init(CamelContext context) {
+                this.context = context;
+                exp1 = context.resolveLanguage("simple").createExpression(source);
+                exp1.init(context);
+                exp2 = context.resolveLanguage("simple").createExpression(function);
+                exp2.init(context);
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                Map<String, Object> map = exp1.evaluate(exchange, Map.class);
+                if (map != null) {
+                    Object value = exp2.evaluate(exchange, Object.class);
+                    if (value != null) {
+                        map.put(key, value);
+                    }
+                }
+                return map;
+            }
+
+            @Override
+            public String toString() {
+                return "mapAdd(" + source + ", " + key + ", " + function + ")";
+            }
+        };
+    }
+
+    /**
+     * Removes the result of the function from the source map
+     */
+    public static Expression mapRemoveExpression(final String source, final String key) {
+        return new ExpressionAdapter() {
+            private CamelContext context;
+            private Expression exp1;
+
+            @Override
+            public void init(CamelContext context) {
+                this.context = context;
+                exp1 = context.resolveLanguage("simple").createExpression(source);
+                exp1.init(context);
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                Map<String, Object> map = exp1.evaluate(exchange, Map.class);
+                if (map != null) {
+                    map.remove(key);
+                }
+                return map;
+            }
+
+            @Override
+            public String toString() {
+                return "mapRemove(" + source + ", " + key + ")";
+            }
+        };
+    }
+
+    /**
+     * Filters the values from the source that matches the predicate function
+     */
+    public static Expression filterExpression(final String source, final String function) {
+        return new ExpressionAdapter() {
+            private CamelContext context;
+            private Expression exp1;
+            private Predicate exp2;
+
+            @Override
+            public void init(CamelContext context) {
+                this.context = context;
+                exp1 = context.resolveLanguage("simple").createExpression(source);
+                exp1.init(context);
+                exp2 = context.resolveLanguage("simple").createPredicate(function);
+                exp2.init(context);
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                List<Object> answer = new ArrayList<>();
+                Object o = exp1.evaluate(exchange, Object.class);
+                Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
+                for (Object i : it) {
+                    // use a dummy exchange as the input is to be the message body
+                    Exchange dummy = ExchangeHelper.createCopy(exchange, true);
+                    dummy.getMessage().setBody(i);
+                    if (exp2.matches(dummy)) {
+                        answer.add(i);
+                    }
+                }
+                return answer;
+            }
+
+            @Override
+            public String toString() {
+                return "filter(" + source + ", " + function + ")";
+            }
+        };
+    }
+
+    /**
+     * Sets the message header with the given expression value
+     */
+    public static Expression setHeaderExpression(final String name, final String type, final String expression) {
+        return new ExpressionAdapter() {
+            private ClassResolver classResolver;
+            private Expression exp;
+
+            @Override
+            public void init(CamelContext context) {
+                classResolver = context.getClassResolver();
+                exp = context.resolveLanguage("simple").createExpression(expression);
+                exp.init(context);
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                Class<?> clazz = Object.class;
+                if (type != null) {
+                    try {
+                        clazz = classResolver.resolveMandatoryClass(type);
+                    } catch (ClassNotFoundException e) {
+                        throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
+                    }
+                }
+                Object value = exp.evaluate(exchange, clazz);
+                if (value != null) {
+                    exchange.getMessage().setHeader(name, value);
+                } else {
+                    exchange.getMessage().removeHeader(name);
+                }
+                // does not return anything
+                return null;
+            }
+
+            @Override
+            public String toString() {
+                return "setHeader(" + name + "," + expression + ")";
+            }
+        };
+    }
+
+    /**
+     * Sets the variable with the given expression value
+     */
+    public static Expression setVariableExpression(final String name, final String type, final String expression) {
+        return new ExpressionAdapter() {
+            private ClassResolver classResolver;
+            private Expression exp;
+
+            @Override
+            public void init(CamelContext context) {
+                classResolver = context.getClassResolver();
+                exp = context.resolveLanguage("simple").createExpression(expression);
+                exp.init(context);
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                Class<?> clazz = Object.class;
+                if (type != null) {
+                    try {
+                        clazz = classResolver.resolveMandatoryClass(type);
+                    } catch (ClassNotFoundException e) {
+                        throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
+                    }
+                }
+                Object value = exp.evaluate(exchange, clazz);
+                if (value != null) {
+                    exchange.setVariable(name, value);
+                } else {
+                    exchange.removeVariable(name);
+                }
+                // does not return anything
+                return null;
+            }
+
+            @Override
+            public String toString() {
+                return "setVariable(" + name + "," + expression + ")";
+            }
+        };
+    }
+
+    /**
+     * Returns a range of number between min and max (exclusive)
+     */
+    public static Expression rangeExpression(final String min, final String max) {
+        return new ExpressionAdapter() {
+            private Expression exp1;
+            private Expression exp2;
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                int num1 = exp1.evaluate(exchange, Integer.class);
+                int num2 = exp2.evaluate(exchange, Integer.class);
+                if (num1 >= 0 && num1 <= num2 && num1 != num2) {
+                    List<Integer> answer = new ArrayList<>();
+                    for (int i = num1; i < num2; i++) {
+                        answer.add(i);
+                    }
+                    return answer;
+                }
+                return null;
+            }
+
+            @Override
+            public void init(CamelContext context) {
+                exp1 = ExpressionBuilder.simpleExpression(min);
+                exp1.init(context);
+                exp2 = ExpressionBuilder.simpleExpression(max);
+                exp2.init(context);
+            }
+
+            @Override
+            public String toString() {
+                return "range(" + min + "," + max + ")";
+            }
+        };
+    }
+}
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/DateExpressionBuilder.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/DateExpressionBuilder.java
new file mode 100644
index 000000000000..fc9a006d0b81
--- /dev/null
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/DateExpressionBuilder.java
@@ -0,0 +1,104 @@
+/*
+ * 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;
+
+import java.util.Date;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Expression;
+import org.apache.camel.support.ExpressionAdapter;
+import org.apache.camel.support.LanguageHelper;
+
+/**
+ * Expression builder for date/time functions used by the simple language.
+ */
+public final class DateExpressionBuilder {
+
+    private static final Pattern OFFSET_PATTERN = Pattern.compile("([+-])([^+-]+)");
+
+    private DateExpressionBuilder() {
+    }
+
+    public static Expression dateExpression(final String command) {
+        return dateExpression(command, null, null);
+    }
+
+    public static Expression dateExpression(final String command, final String pattern) {
+        return dateExpression(command, null, pattern);
+    }
+
+    public static Expression dateExpression(
+            final String commandWithOffsets, final String timezone,
+            final String pattern) {
+        final String command = commandWithOffsets.split("[+-]", 2)[0].trim();
+        final List<Long> offsets = LanguageHelper.captureOffsets(commandWithOffsets, OFFSET_PATTERN);
+
+        return new ExpressionAdapter() {
+            @Override
+            public Object evaluate(Exchange exchange) {
+                Date date = evalDate(exchange, command);
+                Object answer = LanguageHelper.applyDateOffsets(date, offsets, pattern, timezone);
+                if ("millis".equals(command) && answer instanceof Date d) {
+                    answer = d.getTime();
+                }
+                return answer;
+            }
+
+            @Override
+            public String toString() {
+                if (timezone != null && pattern != null) {
+                    return "date(" + commandWithOffsets + ":" + timezone + ":" + pattern + ")";
+                } else if (pattern != null) {
+                    return "date(" + commandWithOffsets + ":" + pattern + ")";
+                } else {
+                    return "date(" + commandWithOffsets + ")";
+                }
+            }
+        };
+    }
+
+    private static Date evalDate(Exchange exchange, String command) {
+        Date date;
+        if ("now".equals(command) || "millis".equals(command)) {
+            date = new Date();
+        } else if ("exchangeCreated".equals(command)) {
+            date = LanguageHelper.dateFromExchangeCreated(exchange);
+        } else if (command.startsWith("header.")) {
+            date = LanguageHelper.dateFromHeader(exchange, command, (e, o) -> tryConvertingAsDate(e, o, command));
+        } else if (command.startsWith("variable.")) {
+            date = LanguageHelper.dateFromVariable(exchange, command, (e, o) -> tryConvertingAsDate(e, o, command));
+        } else if (command.startsWith("exchangeProperty.")) {
+            date = LanguageHelper.dateFromExchangeProperty(exchange, command, (e, o) -> tryConvertingAsDate(e, o, command));
+        } else if ("file".equals(command)) {
+            date = LanguageHelper.dateFromFileLastModified(exchange, command);
+        } else {
+            throw new IllegalArgumentException("Command not supported for dateExpression: " + command);
+        }
+        return date;
+    }
+
+    private static Date tryConvertingAsDate(Exchange exchange, Object obj, String command) {
+        final Date date = exchange.getContext().getTypeConverter().tryConvertTo(Date.class, exchange, obj);
+        if (date == null) {
+            throw new IllegalArgumentException("Cannot find Date/long object at command: " + command);
+        }
+
+        return date;
+    }
+}
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/FileExpressionBuilder.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/FileExpressionBuilder.java
new file mode 100644
index 000000000000..a93a5e0136f1
--- /dev/null
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/FileExpressionBuilder.java
@@ -0,0 +1,238 @@
+/*
+ * 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;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Expression;
+import org.apache.camel.support.ExpressionAdapter;
+import org.apache.camel.util.FileUtil;
+
+/**
+ * Expression builder for file-related functions used by the simple language.
+ */
+public final class FileExpressionBuilder {
+
+    private FileExpressionBuilder() {
+    }
+
+    public static Expression fileNameExpression() {
+        return new ExpressionAdapter() {
+            @Override
+            public Object evaluate(Exchange exchange) {
+                return exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
+            }
+
+            @Override
+            public String toString() {
+                return "file:name";
+            }
+        };
+    }
+
+    public static Expression fileOnlyNameExpression() {
+        return new ExpressionAdapter() {
+            @Override
+            public Object evaluate(Exchange exchange) {
+                String answer = exchange.getIn().getHeader(Exchange.FILE_NAME_ONLY, String.class);
+                if (answer == null) {
+                    answer = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
+                    answer = FileUtil.stripPath(answer);
+                }
+                return answer;
+            }
+
+            @Override
+            public String toString() {
+                return "file:onlyname";
+            }
+        };
+    }
+
+    public static Expression fileNameNoExtensionExpression() {
+        return new ExpressionAdapter() {
+            @Override
+            public Object evaluate(Exchange exchange) {
+                String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
+                return FileUtil.stripExt(name);
+            }
+
+            @Override
+            public String toString() {
+                return "file:name.noext";
+            }
+        };
+    }
+
+    public static Expression fileNameNoExtensionSingleExpression() {
+        return new ExpressionAdapter() {
+            @Override
+            public Object evaluate(Exchange exchange) {
+                String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
+                return FileUtil.stripExt(name, true);
+            }
+
+            @Override
+            public String toString() {
+                return "file:name.noext.single";
+            }
+        };
+    }
+
+    public static Expression fileOnlyNameNoExtensionExpression() {
+        return new ExpressionAdapter() {
+            @Override
+            public Object evaluate(Exchange exchange) {
+                String name = fileOnlyNameExpression().evaluate(exchange, String.class);
+                return FileUtil.stripExt(name);
+            }
+
+            @Override
+            public String toString() {
+                return "file:onlyname.noext";
+            }
+        };
+    }
+
+    public static Expression fileOnlyNameNoExtensionSingleExpression() {
+        return new ExpressionAdapter() {
+            @Override
+            public Object evaluate(Exchange exchange) {
+                String name = fileOnlyNameExpression().evaluate(exchange, String.class);
+                return FileUtil.stripExt(name, true);
+            }
+
+            @Override
+            public String toString() {
+                return "file:onlyname.noext.single";
+            }
+        };
+    }
+
+    public static Expression fileExtensionExpression() {
+        return new ExpressionAdapter() {
+            @Override
+            public Object evaluate(Exchange exchange) {
+                String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
+                return FileUtil.onlyExt(name);
+            }
+
+            @Override
+            public String toString() {
+                return "file:ext";
+            }
+        };
+    }
+
+    public static Expression fileExtensionSingleExpression() {
+        return new ExpressionAdapter() {
+            @Override
+            public Object evaluate(Exchange exchange) {
+                String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
+                return FileUtil.onlyExt(name, true);
+            }
+
+            @Override
+            public String toString() {
+                return "file:ext.single";
+            }
+        };
+    }
+
+    public static Expression fileParentExpression() {
+        return new ExpressionAdapter() {
+            @Override
+            public Object evaluate(Exchange exchange) {
+                return exchange.getIn().getHeader("CamelFileParent", String.class);
+            }
+
+            @Override
+            public String toString() {
+                return "file:parent";
+            }
+        };
+    }
+
+    public static Expression filePathExpression() {
+        return new ExpressionAdapter() {
+            @Override
+            public Object evaluate(Exchange exchange) {
+                return exchange.getIn().getHeader("CamelFilePath", String.class);
+            }
+
+            @Override
+            public String toString() {
+                return "file:path";
+            }
+        };
+    }
+
+    public static Expression fileAbsolutePathExpression() {
+        return new ExpressionAdapter() {
+            @Override
+            public Object evaluate(Exchange exchange) {
+                return exchange.getIn().getHeader("CamelFileAbsolutePath", String.class);
+            }
+
+            @Override
+            public String toString() {
+                return "file:absolute.path";
+            }
+        };
+    }
+
+    public static Expression fileAbsoluteExpression() {
+        return new ExpressionAdapter() {
+            @Override
+            public Object evaluate(Exchange exchange) {
+                return exchange.getIn().getHeader("CamelFileAbsolute", Boolean.class);
+            }
+
+            @Override
+            public String toString() {
+                return "file:absolute";
+            }
+        };
+    }
+
+    public static Expression fileSizeExpression() {
+        return new ExpressionAdapter() {
+            @Override
+            public Object evaluate(Exchange exchange) {
+                return exchange.getIn().getHeader(Exchange.FILE_LENGTH, Long.class);
+            }
+
+            @Override
+            public String toString() {
+                return "file:length";
+            }
+        };
+    }
+
+    public static Expression fileLastModifiedExpression() {
+        return new ExpressionAdapter() {
+            @Override
+            public Object evaluate(Exchange exchange) {
+                return exchange.getIn().getHeader(Exchange.FILE_LAST_MODIFIED, Long.class);
+            }
+
+            @Override
+            public String toString() {
+                return "file:modified";
+            }
+        };
+    }
+}
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/MathExpressionBuilder.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/MathExpressionBuilder.java
new file mode 100644
index 000000000000..bcc830538791
--- /dev/null
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/MathExpressionBuilder.java
@@ -0,0 +1,337 @@
+/*
+ * 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;
+
+import java.util.Arrays;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+import org.apache.camel.Expression;
+import org.apache.camel.support.ExpressionAdapter;
+import org.apache.camel.util.ObjectHelper;
+
+/**
+ * Expression builder for numeric and math functions used by the simple language.
+ */
+public final class MathExpressionBuilder {
+
+    private MathExpressionBuilder() {
+    }
+
+    /**
+     * Converts the given expressions to a number and return the absolute value (uses message body if expression is
+     * null)
+     */
+    public static Expression absExpression(final String expression) {
+        return new ExpressionAdapter() {
+            private Expression exp;
+
+            @Override
+            public void init(CamelContext context) {
+                if (expression != null) {
+                    exp = context.resolveLanguage("simple").createExpression(expression);
+                    exp.init(context);
+                }
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                Long value;
+                if (exp != null) {
+                    value = exp.evaluate(exchange, Long.class);
+                } else {
+                    value = exchange.getMessage().getBody(Long.class);
+                }
+                if (value != null) {
+                    value = Math.abs(value);
+                }
+                return value;
+            }
+
+            @Override
+            public String toString() {
+                if (expression != null) {
+                    return "abs(" + expression + ")";
+                } else {
+                    return "abs()";
+                }
+            }
+        };
+    }
+
+    /**
+     * Converts the given expressions to a floating number and return the floor value (uses message body if expression
+     * is null)
+     */
+    public static Expression floorExpression(final String expression) {
+        return new ExpressionAdapter() {
+            private Expression exp;
+
+            @Override
+            public void init(CamelContext context) {
+                if (expression != null) {
+                    exp = context.resolveLanguage("simple").createExpression(expression);
+                    exp.init(context);
+                }
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                Double value;
+                if (exp != null) {
+                    value = exp.evaluate(exchange, Double.class);
+                } else {
+                    value = exchange.getMessage().getBody(Double.class);
+                }
+                if (value != null) {
+                    double d = Math.floor(value);
+                    return (long) d;
+                }
+                return value;
+            }
+
+            @Override
+            public String toString() {
+                if (expression != null) {
+                    return "floor(" + expression + ")";
+                } else {
+                    return "floor()";
+                }
+            }
+        };
+    }
+
+    /**
+     * Converts the given expressions to a floating number and return the ceil value (uses message body if expression is
+     * null)
+     */
+    public static Expression ceilExpression(final String expression) {
+        return new ExpressionAdapter() {
+            private Expression exp;
+
+            @Override
+            public void init(CamelContext context) {
+                if (expression != null) {
+                    exp = context.resolveLanguage("simple").createExpression(expression);
+                    exp.init(context);
+                }
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                Double value;
+                if (exp != null) {
+                    value = exp.evaluate(exchange, Double.class);
+                } else {
+                    value = exchange.getMessage().getBody(Double.class);
+                }
+                if (value != null) {
+                    double d = Math.ceil(value);
+                    return (long) d;
+                }
+                return value;
+            }
+
+            @Override
+            public String toString() {
+                if (expression != null) {
+                    return "ceil(" + expression + ")";
+                } else {
+                    return "ceil()";
+                }
+            }
+        };
+    }
+
+    /**
+     * An expression that converts the expressions to number and sum the values
+     */
+    public static Expression sumExpression(String[] numbers) {
+        return new ExpressionAdapter() {
+
+            private final Expression[] exps = new Expression[numbers != null ? numbers.length : 0];
+
+            @Override
+            public void init(CamelContext context) {
+                for (int i = 0; numbers != null && i < numbers.length; i++) {
+                    Expression exp = context.resolveLanguage("simple").createExpression(numbers[i]);
+                    exp.init(context);
+                    exps[i] = exp;
+                }
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                Long answer = null;
+                for (Expression exp : exps) {
+                    Object o = exp.evaluate(exchange, Object.class);
+                    // this may be an object that we can iterate
+                    Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
+                    for (Object i : it) {
+                        Long val = exchange.getContext().getTypeConverter().tryConvertTo(Long.class, exchange, i);
+                        if (val != null) {
+                            if (answer == null) {
+                                answer = 0L;
+                            }
+                            answer += val;
+                        }
+                    }
+                }
+                return answer;
+            }
+
+            @Override
+            public String toString() {
+                return "sum(" + Arrays.toString(numbers) + ")";
+            }
+        };
+    }
+
+    /**
+     * An expression that converts the expressions to number and returns the maximum number
+     */
+    public static Expression maxExpression(String[] numbers) {
+        return new ExpressionAdapter() {
+
+            private final Expression[] exps = new Expression[numbers != null ? numbers.length : 0];
+
+            @Override
+            public void init(CamelContext context) {
+                for (int i = 0; numbers != null && i < numbers.length; i++) {
+                    Expression exp = context.resolveLanguage("simple").createExpression(numbers[i]);
+                    exp.init(context);
+                    exps[i] = exp;
+                }
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                Long answer = null;
+                for (Expression exp : exps) {
+                    Object o = exp.evaluate(exchange, Object.class);
+                    // this may be an object that we can iterate
+                    Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
+                    for (Object i : it) {
+                        Long val = exchange.getContext().getTypeConverter().tryConvertTo(Long.class, exchange, i);
+                        if (val != null) {
+                            if (answer == null) {
+                                answer = val;
+                            }
+                            answer = Math.max(answer, val);
+                        }
+                    }
+                }
+                return answer;
+            }
+
+            @Override
+            public String toString() {
+                return "max(" + Arrays.toString(numbers) + ")";
+            }
+        };
+    }
+
+    /**
+     * An expression that converts the expressions to number and returns the minimum number
+     */
+    public static Expression minExpression(String[] numbers) {
+        return new ExpressionAdapter() {
+
+            private final Expression[] exps = new Expression[numbers != null ? numbers.length : 0];
+
+            @Override
+            public void init(CamelContext context) {
+                for (int i = 0; numbers != null && i < numbers.length; i++) {
+                    Expression exp = context.resolveLanguage("simple").createExpression(numbers[i]);
+                    exp.init(context);
+                    exps[i] = exp;
+                }
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                Long answer = null;
+                for (Expression exp : exps) {
+                    Object o = exp.evaluate(exchange, Object.class);
+                    // this may be an object that we can iterate
+                    Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
+                    for (Object i : it) {
+                        Long val = exchange.getContext().getTypeConverter().tryConvertTo(Long.class, exchange, i);
+                        if (val != null) {
+                            if (answer == null) {
+                                answer = val;
+                            }
+                            answer = Math.min(answer, val);
+                        }
+                    }
+                }
+                return answer;
+            }
+
+            @Override
+            public String toString() {
+                return "min(" + Arrays.toString(numbers) + ")";
+            }
+        };
+    }
+
+    /**
+     * An expression that converts the expressions to number and returns the average number
+     */
+    public static Expression averageExpression(String[] numbers) {
+        return new ExpressionAdapter() {
+
+            private final Expression[] exps = new Expression[numbers != null ? numbers.length : 0];
+
+            @Override
+            public void init(CamelContext context) {
+                for (int i = 0; numbers != null && i < numbers.length; i++) {
+                    Expression exp = context.resolveLanguage("simple").createExpression(numbers[i]);
+                    exp.init(context);
+                    exps[i] = exp;
+                }
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                Long answer = null;
+                int counter = 0;
+                for (Expression exp : exps) {
+                    Object o = exp.evaluate(exchange, Object.class);
+                    // this may be an object that we can iterate
+                    Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
+                    for (Object i : it) {
+                        Long val = exchange.getContext().getTypeConverter().tryConvertTo(Long.class, exchange, i);
+                        if (val != null) {
+                            if (answer == null) {
+                                answer = 0L;
+                            }
+                            answer += val;
+                            counter++;
+                        }
+                    }
+                }
+                return answer != null ? answer / counter : null;
+            }
+
+            @Override
+            public String toString() {
+                return "average(" + Arrays.toString(numbers) + ")";
+            }
+        };
+    }
+}
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/MiscExpressionBuilder.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/MiscExpressionBuilder.java
new file mode 100644
index 000000000000..6114be621b1b
--- /dev/null
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/MiscExpressionBuilder.java
@@ -0,0 +1,1095 @@
+/*
+ * 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;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Constructor;
+import java.security.DigestInputStream;
+import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.Exchange;
+import org.apache.camel.Expression;
+import org.apache.camel.Predicate;
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.spi.ClassResolver;
+import org.apache.camel.spi.ExchangeFormatter;
+import org.apache.camel.spi.Language;
+import org.apache.camel.spi.SimpleFunctionRegistry;
+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.ExpressionAdapter;
+import org.apache.camel.support.LanguageHelper;
+import org.apache.camel.support.MessageHelper;
+import org.apache.camel.support.RandomUuidGenerator;
+import org.apache.camel.support.ResourceHelper;
+import org.apache.camel.support.ShortUuidGenerator;
+import org.apache.camel.support.SimpleUuidGenerator;
+import org.apache.camel.support.builder.ExpressionBuilder;
+import org.apache.camel.support.builder.PredicateBuilder;
+import org.apache.camel.util.IOHelper;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.SkipIterator;
+import org.apache.camel.util.StringHelper;
+import org.apache.camel.util.json.JsonArray;
+import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.Jsonable;
+
+/**
+ * Expression builder for miscellaneous functions used by the simple language.
+ */
+public final class MiscExpressionBuilder {
+
+    private MiscExpressionBuilder() {
+    }
+
+    /**
+     * Returns the message history (including exchange details or not)
+     */
+    public static Expression messageHistoryExpression(final boolean detailed) {
+        return new ExpressionAdapter() {
+
+            private ExchangeFormatter formatter;
+
+            @Override
+            public void init(CamelContext context) {
+                if (detailed) {
+                    // use the exchange formatter to log exchange details
+                    formatter = getOrCreateExchangeFormatter(context);
+                }
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                return MessageHelper.dumpMessageHistoryStacktrace(exchange, formatter, false);
+            }
+
+            private ExchangeFormatter getOrCreateExchangeFormatter(CamelContext camelContext) {
+                return LanguageHelper.getOrCreateExchangeFormatter(camelContext, formatter);
+            }
+
+            @Override
+            public String toString() {
+                return "messageHistory(" + detailed + ")";
+            }
+        };
+    }
+
+    /**
+     * Returns an iterator to collate (iterate) the given expression
+     */
+    public static Expression collateExpression(final String expression, final int group) {
+        return new ExpressionAdapter() {
+            private Expression exp;
+
+            @Override
+            public void init(CamelContext context) {
+                // first use simple then create the group expression
+                exp = context.resolveLanguage("simple").createExpression(expression);
+                exp.init(context);
+                exp = ExpressionBuilder.groupIteratorExpression(exp, null, Integer.toString(group), false);
+                exp.init(context);
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                return exp.evaluate(exchange, Object.class);
+            }
+
+            @Override
+            public String toString() {
+                return "collate(" + expression + "," + group + ")";
+            }
+        };
+    }
+
+    /**
+     * Returns an iterator to collate (iterate) the given expression
+     */
+    public static Expression collateExpression(final String expression, final String group) {
+        return new ExpressionAdapter() {
+            private Expression exp;
+            private Expression num;
+
+            @Override
+            public void init(CamelContext context) {
+                exp = context.resolveLanguage("simple").createExpression(expression);
+                exp.init(context);
+                num = context.resolveLanguage("simple").createExpression(group);
+                num.init(context);
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                Integer n = num.evaluate(exchange, Integer.class);
+                Expression grouped = ExpressionBuilder.groupIteratorExpression(exp, null, Integer.toString(n), false);
+                grouped.init(exchange.getContext());
+                return grouped.evaluate(exchange, Object.class);
+            }
+
+            @Override
+            public String toString() {
+                return "collate(" + expression + "," + group + ")";
+            }
+        };
+    }
+
+    /**
+     * Returns an iterator to skip (iterate) the given expression
+     */
+    public static Expression skipExpression(final String expression, final int number) {
+        return new ExpressionAdapter() {
+            private Expression exp;
+
+            @Override
+            public void init(CamelContext context) {
+                exp = context.resolveLanguage("simple").createExpression(expression);
+                exp.init(context);
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                return skipIteratorExpression(exp, number).evaluate(exchange, Object.class);
+            }
+
+            @Override
+            public String toString() {
+                return "skip(" + expression + "," + number + ")";
+            }
+        };
+    }
+
+    /**
+     * Returns an iterator to skip (iterate) the given expression
+     */
+    public static Expression skipExpression(final String expression, final String number) {
+        return new ExpressionAdapter() {
+            private Expression exp;
+            private Expression num;
+
+            @Override
+            public void init(CamelContext context) {
+                exp = context.resolveLanguage("simple").createExpression(expression);
+                exp.init(context);
+                num = context.resolveLanguage("simple").createExpression(number);
+                num.init(context);
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                Integer n = num.evaluate(exchange, Integer.class);
+                if (n == null) {
+                    throw new IllegalArgumentException("skip number expression evaluated to null: " + number);
+                }
+                return skipIteratorExpression(exp, n).evaluate(exchange, Object.class);
+            }
+
+            @Override
+            public String toString() {
+                return "skip(" + expression + "," + number + ")";
+            }
+        };
+    }
+
+    /**
+     * Whether the expression is empty or having a list/map which has no elements.
+     */
+    public static Expression isEmptyExpression(final String expression) {
+        return new ExpressionAdapter() {
+            private Expression exp;
+
+            @Override
+            public void init(CamelContext context) {
+                if (expression != null) {
+                    exp = context.resolveLanguage("simple").createExpression(expression);
+                    exp.init(context);
+                }
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                Object body;
+                if (exp != null) {
+                    body = exp.evaluate(exchange, Object.class);
+                } else {
+                    body = exchange.getMessage().getBody();
+                }
+                // this may be an object that we can iterate
+                Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(body);
+                for (Object o : it) {
+                    if (o != null) {
+                        return false;
+                    }
+                }
+                return true;
+            }
+
+            @Override
+            public String toString() {
+                if (expression != null) {
+                    return "isEmpty(" + expression + ")";
+                } else {
+                    return "isEmpty()";
+                }
+            }
+        };
+    }
+
+    /**
+     * Whether the expression is an alphabetic String
+     */
+    public static Expression isAlphaExpression(final String expression) {
+        return new ExpressionAdapter() {
+            private Expression exp;
+
+            @Override
+            public void init(CamelContext context) {
+                if (expression != null) {
+                    exp = context.resolveLanguage("simple").createExpression(expression);
+                    exp.init(context);
+                }
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                String body;
+                if (exp != null) {
+                    body = exp.evaluate(exchange, String.class);
+                } else {
+                    body = exchange.getMessage().getBody(String.class);
+                }
+                if (body == null || body.isBlank()) {
+                    return false;
+                }
+                for (int i = 0; i < body.length(); i++) {
+                    char ch = body.charAt(i);
+                    if (!Character.isLetter(ch)) {
+                        return false;
+                    }
+                }
+                return true;
+            }
+
+            @Override
+            public String toString() {
+                if (expression != null) {
+                    return "isAlpha(" + expression + ")";
+                } else {
+                    return "isAlpha()";
+                }
+            }
+        };
+    }
+
+    /**
+     * Whether the expression is a number (integral or floating)
+     */
+    public static Expression isNumericExpression(final String expression) {
+        return new ExpressionAdapter() {
+            private Expression exp;
+
+            @Override
+            public void init(CamelContext context) {
+                if (expression != null) {
+                    exp = context.resolveLanguage("simple").createExpression(expression);
+                    exp.init(context);
+                }
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                String body;
+                if (exp != null) {
+                    body = exp.evaluate(exchange, String.class);
+                } else {
+                    body = exchange.getMessage().getBody(String.class);
+                }
+                if (body == null || body.isBlank()) {
+                    return false;
+                }
+                for (int i = 0; i < body.length(); i++) {
+                    char ch = body.charAt(i);
+                    if (!Character.isDigit(ch)) {
+                        return false;
+                    }
+                }
+                return true;
+            }
+
+            @Override
+            public String toString() {
+                if (expression != null) {
+                    return "isNumeric(" + expression + ")";
+                } else {
+                    return "isNumeric()";
+                }
+            }
+        };
+    }
+
+    /**
+     * Whether the expression is an alphabetic or numeric String
+     */
+    public static Expression isAlphaNumericExpression(final String expression) {
+        return new ExpressionAdapter() {
+            private Expression exp;
+
+            @Override
+            public void init(CamelContext context) {
+                if (expression != null) {
+                    exp = context.resolveLanguage("simple").createExpression(expression);
+                    exp.init(context);
+                }
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                String body;
+                if (exp != null) {
+                    body = exp.evaluate(exchange, String.class);
+                } else {
+                    body = exchange.getMessage().getBody(String.class);
+                }
+                if (body == null || body.isBlank()) {
+                    return false;
+                }
+                for (int i = 0; i < body.length(); i++) {
+                    char ch = body.charAt(i);
+                    if (!Character.isLetterOrDigit(ch)) {
+                        return false;
+                    }
+                }
+                return true;
+            }
+
+            @Override
+            public String toString() {
+                if (expression != null) {
+                    return "isAlphaNumeric(" + expression + ")";
+                } else {
+                    return "isAlphaNumeric()";
+                }
+            }
+        };
+    }
+
+    /**
+     * Returns the opposite result of the predicate
+     */
+    public static Expression isNotPredicate(final String predicate) {
+        return new ExpressionAdapter() {
+            private Predicate pred;
+
+            @Override
+            public void init(CamelContext context) {
+                pred = PredicateBuilder.not(context.resolveLanguage("simple").createPredicate(predicate));
+                pred.init(context);
+            }
+
+            public Object evaluate(Exchange exchange) {
+                return pred.matches(exchange);
+            }
+
+            @Override
+            public String toString() {
+                if (predicate != null) {
+                    return "not(" + predicate + ")";
+                } else {
+                    return "not()";
+                }
+            }
+        };
+    }
+
+    /**
+     * What kind of type is the expression
+     */
+    public static Expression kindOfTypeExpression(final String expression) {
+        return new ExpressionAdapter() {
+            private Expression exp;
+
+            @Override
+            public void init(CamelContext context) {
+                if (expression != null) {
+                    exp = context.resolveLanguage("simple").createExpression(expression);
+                    exp.init(context);
+                }
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                Object value;
+                if (exp != null) {
+                    value = exp.evaluate(exchange, Object.class);
+                } else {
+                    value = exchange.getMessage().getBody();
+                }
+                if (value != null) {
+                    Class<?> type = value.getClass();
+                    if (ObjectHelper.isNumericType(type)) {
+                        return "number";
+                    } else if (boolean.class == type || Boolean.class == type) {
+                        return "boolean";
+                    } else if (value instanceof CharSequence) {
+                        return "string";
+                    } else if (ObjectHelper.isPrimitiveArrayType(type) || value instanceof Collection
+                            || value instanceof Map<?, ?>) {
+                        return "array";
+                    } else {
+                        return "object";
+                    }
+                }
+                return "null";
+            }
+
+            @Override
+            public String toString() {
+                if (expression != null) {
+                    return "kindOfType(" + expression + ")";
+                } else {
+                    return "kindOfType()";
+                }
+            }
+        };
+    }
+
+    /**
+     * Throws an exception with the given message.
+     */
+    public static Expression throwExceptionExpression(String msg, String type) {
+        return new ExpressionAdapter() {
+            private Class<?> clazz;
+            private Expression exp;
+
+            @Override
+            public void init(CamelContext context) {
+                if (type == null) {
+                    clazz = IllegalArgumentException.class;
+                } else {
+                    try {
+                        clazz = context.getClassResolver().resolveMandatoryClass(type);
+                    } catch (ClassNotFoundException e) {
+                        throw CamelExecutionException.wrapRuntimeException(e);
+                    }
+                }
+                exp = context.resolveLanguage("simple").createExpression(msg);
+                exp.init(context);
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                String text = exp.evaluate(exchange, String.class);
+                try {
+                    // create a new exception of that type, and provide the message
+                    Constructor<?> constructor = clazz.getConstructor(String.class);
+                    Exception cause = (Exception) constructor.newInstance(text);
+                    if (cause instanceof RuntimeException re) {
+                        throw re;
+                    }
+                    throw new RuntimeCamelException(cause);
+                } catch (RuntimeException re) {
+                    throw re;
+                } catch (Exception e) {
+                    throw new RuntimeException(e);
+                }
+            }
+
+            @Override
+            public String toString() {
+                return "throwException(" + msg + ")";
+            }
+        };
+    }
+
+    /**
+     * Assert the expression
+     */
+    public static Expression assertExpression(String expression, String message) {
+        return new ExpressionAdapter() {
+            private Predicate pred;
+
+            @Override
+            public void init(CamelContext context) {
+                pred = context.resolveLanguage("simple").createPredicate(expression);
+                pred.init(context);
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                if (!pred.matches(exchange)) {
+                    throw new SimpleAssertionException(message);
+                }
+                return null;
+            }
+
+            @Override
+            public String toString() {
+                return "assert(" + pred + ", " + message + ")";
+            }
+        };
+    }
+
+    /**
+     * Converts the result of the expression to the given type
+     */
+    public static Expression convertToExpression(final String expression, final String type) {
+        return new ExpressionAdapter() {
+            private Class<?> clazz;
+            private Expression exp;
+
+            @Override
+            public void init(CamelContext context) {
+                try {
+                    clazz = context.getClassResolver().resolveMandatoryClass(type);
+                } catch (ClassNotFoundException e) {
+                    throw CamelExecutionException.wrapRuntimeException(e);
+                }
+                exp = context.resolveLanguage("simple").createExpression(expression);
+                exp.init(context);
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                return exp.evaluate(exchange, clazz);
+            }
+
+            @Override
+            public String toString() {
+                if (expression != null) {
+                    return "convertTo(" + expression + ", " + type + ")";
+                } else {
+                    return "convertTo(" + type + ")";
+                }
+            }
+        };
+    }
+
+    /**
+     * Converts the result of the expression to the given type and invoking methods on the converted object defined in a
+     * simple OGNL notation
+     */
+    public static Expression convertToOgnlExpression(final String expression, final String type, final String ognl) {
+        return new ExpressionAdapter() {
+            private Class<?> clazz;
+            private Expression exp;
+            private Language bean;
+
+            @Override
+            public void init(CamelContext context) {
+                try {
+                    clazz = context.getClassResolver().resolveMandatoryClass(type);
+                } catch (ClassNotFoundException e) {
+                    throw CamelExecutionException.wrapRuntimeException(e);
+                }
+                exp = context.resolveLanguage("simple").createExpression(expression);
+                exp.init(context);
+                bean = context.resolveLanguage("bean");
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                Object body = exp.evaluate(exchange, clazz);
+                if (body == null) {
+                    return null;
+                }
+                Expression ognlExp = bean.createExpression(null, new Object[] { null, body, ognl });
+                ognlExp.init(exchange.getContext());
+                return ognlExp.evaluate(exchange, Object.class);
+            }
+
+            @Override
+            public String toString() {
+                if (expression != null) {
+                    return "convertToOgnl(" + expression + ", " + type + ")." + ognl;
+                } else {
+                    return "convertToOgnl(" + type + ")." + ognl;
+                }
+            }
+        };
+    }
+
+    /**
+     * 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 + ")";
+            }
+        };
+    }
+
+    /**
+     * Hashes the value using the given algorithm
+     */
+    public static Expression hashExpression(final String expression, final String algorithm) {
+        return new ExpressionAdapter() {
+            private Expression exp;
+
+            @Override
+            public void init(CamelContext context) {
+                exp = context.resolveLanguage("simple").createExpression(expression);
+                exp.init(context);
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                InputStream is = exp.evaluate(exchange, InputStream.class);
+                if (is != null) {
+                    try {
+                        // calculate the hash in chunks in case the payload is big
+                        MessageDigest digest = MessageDigest.getInstance(algorithm);
+                        DigestInputStream dis = new DigestInputStream(is, digest);
+                        IOHelper.copy(dis, new OutputStream() {
+                            @Override
+                            public void write(int b) throws IOException {
+                                // ignore
+                            }
+                        });
+                        return StringHelper.bytesToHex(digest.digest());
+                    } catch (Exception e) {
+                        throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
+                    } finally {
+                        // reset cached streams so they can be read again
+                        MessageHelper.resetStreamCache(exchange.getMessage());
+                    }
+                }
+                return null;
+            }
+
+            @Override
+            public String toString() {
+                return "hash(" + expression + "," + algorithm + ")";
+            }
+        };
+    }
+
+    /**
+     * Returns a random number between min and max (exclusive)
+     */
+    public static Expression randomExpression(final String min, final String max) {
+        return new ExpressionAdapter() {
+            private Expression exp1;
+            private Expression exp2;
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                int num1 = exp1.evaluate(exchange, Integer.class);
+                int num2 = exp2.evaluate(exchange, Integer.class);
+                Random random = new Random(); // NOSONAR
+                return random.nextInt(num2 - num1) + num1;
+            }
+
+            @Override
+            public void init(CamelContext context) {
+                exp1 = ExpressionBuilder.simpleExpression(min);
+                exp1.init(context);
+                exp2 = ExpressionBuilder.simpleExpression(max);
+                exp2.init(context);
+            }
+
+            @Override
+            public String toString() {
+                return "random(" + min + "," + max + ")";
+            }
+        };
+    }
+
+    /**
+     * Returns a random number between 0 and max (exclusive)
+     */
+    public static Expression randomExpression(final int max) {
+        return randomExpression(0, max);
+    }
+
+    /**
+     * Returns a random number between min and max (exclusive)
+     */
+    public static Expression randomExpression(final int min, final int max) {
+        return new ExpressionAdapter() {
+            @Override
+            public Object evaluate(Exchange exchange) {
+                Random random = new Random(); // NOSONAR
+                return random.nextInt(max - min) + min;
+            }
+
+            @Override
+            public String toString() {
+                return "random(" + min + "," + max + ")";
+            }
+        };
+    }
+
+    /**
+     * Returns a new empty object of the given type
+     */
+    public static Expression newEmptyExpression(final String type) {
+
+        return new ExpressionAdapter() {
+            @Override
+            public Object evaluate(Exchange exchange) {
+                if ("map".equalsIgnoreCase(type)) {
+                    return new LinkedHashMap<>();
+                } else if ("string".equalsIgnoreCase(type)) {
+                    return "";
+                } else if ("list".equalsIgnoreCase(type)) {
+                    return new ArrayList<>();
+                } else if ("set".equalsIgnoreCase(type)) {
+                    return new LinkedHashSet<>();
+                }
+                throw new IllegalArgumentException("function empty(%s) has unknown type".formatted(type));
+            }
+
+            @Override
+            public String toString() {
+                return "empty(%s)".formatted(type);
+            }
+        };
+    }
+
+    /**
+     * Returns an uuid string based on the given generator (default, classic, short, simple)
+     */
+    public static Expression uuidExpression(final String generator) {
+        return new ExpressionAdapter() {
+
+            UuidGenerator uuid;
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                return uuid.generateUuid();
+            }
+
+            @Override
+            public void init(CamelContext context) {
+                if ("classic".equalsIgnoreCase(generator)) {
+                    uuid = new ClassicUuidGenerator();
+                } else if ("short".equals(generator)) {
+                    uuid = new ShortUuidGenerator();
+                } else if ("simple".equals(generator)) {
+                    uuid = new SimpleUuidGenerator();
+                } else if ("random".equals(generator)) {
+                    uuid = new RandomUuidGenerator();
+                } else if (generator == null || "default".equals(generator)) {
+                    uuid = new DefaultUuidGenerator();
+                } else {
+                    // lookup custom generator
+                    uuid = CamelContextHelper.mandatoryLookup(context, generator, UuidGenerator.class);
+                }
+            }
+
+            @Override
+            public String toString() {
+                if (generator != null) {
+                    return "uuid(" + generator + ")";
+                } else {
+                    return "uuid";
+                }
+            }
+        };
+    }
+
+    public static Expression skipIteratorExpression(final Expression expression, final int skip) {
+        return new ExpressionAdapter() {
+            @Override
+            public Object evaluate(Exchange exchange) {
+                // evaluate expression as iterator
+                Iterator<?> it = expression.evaluate(exchange, Iterator.class);
+                ObjectHelper.notNull(it,
+                        "expression: " + expression + " evaluated on " + exchange + " must return an java.util.Iterator");
+                return new SkipIterator(it, skip);
+            }
+
+            @Override
+            public String toString() {
+                return "skip " + expression + " " + skip + " times";
+            }
+        };
+    }
+
+    /**
+     * Returns the expression for the {@code null} value
+     */
+    public static Expression nullExpression() {
+        return new ExpressionAdapter() {
+            @Override
+            public Object evaluate(Exchange exchange) {
+                return null;
+            }
+
+            @Override
+            public String toString() {
+                return "null";
+            }
+        };
+    }
+
+    /**
+     * Returns an expression that caches the evaluation of another expression and returns the cached value, to avoid
+     * re-evaluating the expression.
+     *
+     * @param  expression the target expression to cache
+     * @return            the cached value
+     */
+    public static Expression cacheExpression(final Expression expression) {
+        return new ExpressionAdapter() {
+            private final AtomicReference<Object> cache = new AtomicReference<>();
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                Object answer = cache.get();
+                if (answer == null) {
+                    answer = expression.evaluate(exchange, Object.class);
+                    cache.set(answer);
+                }
+                return answer;
+            }
+
+            @Override
+            public String toString() {
+                return expression.toString();
+            }
+        };
+    }
+
+    /**
+     * Loads the given resource from classpath
+     */
+    public static Expression loadExpression(final String expression) {
+        return new ExpressionAdapter() {
+
+            private Expression exp;
+
+            @Override
+            public void init(CamelContext context) {
+                exp = context.resolveLanguage("simple").createExpression(expression);
+                exp.init(context);
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                String name = exp.evaluate(exchange, String.class);
+                if (name != null) {
+                    name = name.trim();
+                    String part = StringHelper.after(name, ":", name);
+                    boolean optional = part.endsWith("?optional=true");
+                    if (optional) {
+                        part = part.substring(0, part.length() - 14);
+                    }
+                    if (part.endsWith("?optional=false")) {
+                        part = part.substring(0, part.length() - 15);
+                    }
+                    try {
+                        InputStream is;
+                        if (!optional) {
+                            is = ResourceHelper.resolveMandatoryResourceAsInputStream(exchange.getContext(), part);
+                        } else {
+                            is = ResourceHelper.resolveResourceAsInputStream(exchange.getContext(), part);
+                        }
+                        if (is == null) {
+                            return null;
+                        }
+                        return IOHelper.loadText(is, false);
+                    } catch (Exception e) {
+                        throw RuntimeCamelException.wrapRuntimeException(e);
+                    }
+                }
+                return null;
+            }
+
+            @Override
+            public String toString() {
+                return "load(" + expression + ")";
+            }
+        };
+    }
+
+    /**
+     * Returns an expression for a type value
+     *
+     * @param  name the type name
+     * @return      an expression object which will return the type value
+     */
+    public static Expression typeExpression(final String name) {
+        return new ExpressionAdapter() {
+            private ClassResolver classResolver;
+            private Expression exp;
+
+            @Override
+            public void init(CamelContext context) {
+                classResolver = context.getClassResolver();
+                exp = ExpressionBuilder.simpleExpression(name);
+                exp.init(context);
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                // it may refer to a class type
+                String text = exp.evaluate(exchange, String.class);
+                Class<?> type = classResolver.resolveClass(text);
+                if (type != null) {
+                    return type;
+                }
+
+                int pos = text.lastIndexOf('.');
+                if (pos > 0) {
+                    String before = text.substring(0, pos);
+                    String after = text.substring(pos + 1);
+                    type = classResolver.resolveClass(before);
+                    if (type != null) {
+                        // special for enum constants
+                        if (type.isEnum()) {
+                            Class<Enum<?>> enumClass = (Class<Enum<?>>) type;
+                            for (Enum<?> enumValue : enumClass.getEnumConstants()) {
+                                if (enumValue.name().equalsIgnoreCase(after)) {
+                                    return type.cast(enumValue);
+                                }
+                            }
+                            throw CamelExecutionException.wrapCamelExecutionException(exchange,
+                                    new ClassNotFoundException("Cannot find enum: " + after + " on type: " + type));
+                        } else {
+                            // we assume it is a field constant
+                            Object answer = ObjectHelper.lookupConstantFieldValue(type, after);
+                            if (answer != null) {
+                                return answer;
+                            }
+                        }
+                    }
+                }
+
+                throw CamelExecutionException.wrapCamelExecutionException(exchange,
+                        new ClassNotFoundException("Cannot find type: " + text));
+            }
+
+            @Override
+            public String toString() {
+                return "type:" + name;
+            }
+        };
+    }
+
+    /**
+     * Executes a custom simple function
+     */
+    public static Expression customFunction(final String name, final String parameter) {
+        return new ExpressionAdapter() {
+            private Expression func;
+            private Expression exp;
+
+            @Override
+            public void init(CamelContext context) {
+                super.init(context);
+                SimpleFunctionRegistry registry
+                        = context.getCamelContextExtension().getContextPlugin(SimpleFunctionRegistry.class);
+                func = registry.getFunction(name);
+                if (func == null) {
+                    throw new IllegalArgumentException("No custom simple function with name: " + name);
+                }
+                exp = ExpressionBuilder.simpleExpression(parameter);
+                exp.init(context);
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                final Object originalBody = exchange.getMessage().getBody();
+                try {
+                    Object input = exp.evaluate(exchange, Object.class);
+                    exchange.getMessage().setBody(input);
+                    return func.evaluate(exchange, Object.class);
+                } finally {
+                    exchange.getMessage().setBody(originalBody);
+                }
+            }
+
+            public String toString() {
+                return "function(" + name + ")";
+            }
+        };
+    }
+
+    /**
+     * Evaluates the simple jsonpath with the source input
+     */
+    public static Expression simpleJsonPathExpression(String source, String path) {
+        return new ExpressionAdapter() {
+            private Expression input;
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                Jsonable j = input.evaluate(exchange, Jsonable.class);
+                if (j instanceof JsonObject jo) {
+                    return jo.path(path);
+                } else if (j instanceof JsonArray ja) {
+                    // wrap array in pseudo root to leverage json-path here
+                    JsonObject jo = new JsonObject();
+                    jo.put("_root_", ja);
+                    return jo.path("_root_." + path);
+                }
+                return null;
+            }
+
+            @Override
+            public void init(CamelContext context) {
+                super.init(context);
+                input = ExpressionBuilder.singleInputExpression(source);
+                input.init(context);
+            }
+
+            @Override
+            public String toString() {
+                return "simpleJsonpath[" + path + "]";
+            }
+        };
+    }
+}
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/OgnlExpressionBuilder.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/OgnlExpressionBuilder.java
new file mode 100644
index 000000000000..de266e2bd43c
--- /dev/null
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/OgnlExpressionBuilder.java
@@ -0,0 +1,476 @@
+/*
+ * 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;
+
+import java.util.List;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.Exchange;
+import org.apache.camel.ExchangePropertyKey;
+import org.apache.camel.Expression;
+import org.apache.camel.InvalidPayloadException;
+import org.apache.camel.spi.ClassResolver;
+import org.apache.camel.spi.Language;
+import org.apache.camel.support.ExchangeHelper;
+import org.apache.camel.support.ExpressionAdapter;
+import org.apache.camel.support.builder.ExpressionBuilder;
+import org.apache.camel.util.OgnlHelper;
+import org.apache.camel.util.StringHelper;
+
+/**
+ * Expression builder for OGNL-based navigation functions used by the simple language.
+ */
+public final class OgnlExpressionBuilder {
+
+    private OgnlExpressionBuilder() {
+    }
+
+    /**
+     * Returns the expression for the exchanges inbound message header invoking methods defined in a simple OGNL
+     * notation
+     *
+     * @param ognl methods to invoke on the header in a simple OGNL syntax
+     */
+    public static Expression headersOgnlExpression(final String ognl) {
+        return new KeyedOgnlExpressionAdapter(
+                ognl, "headerOgnl(" + ognl + ")",
+                (exchange, exp) -> {
+                    String text = exp.evaluate(exchange, String.class);
+                    return exchange.getIn().getHeader(text);
+                });
+    }
+
+    /**
+     * Returns the expression for the variable invoking methods defined in a simple OGNL notation
+     *
+     * @param ognl methods to invoke on the variable in a simple OGNL syntax
+     */
+    public static Expression variablesOgnlExpression(final String ognl) {
+        return new KeyedOgnlExpressionAdapter(
+                ognl, "variableOgnl(" + ognl + ")",
+                (exchange, exp) -> {
+                    String text = exp.evaluate(exchange, String.class);
+                    return ExchangeHelper.getVariable(exchange, text);
+                });
+    }
+
+    /**
+     * Returns the expression for the exchanges inbound message body converted to the given type and invoking methods on
+     * the converted body defined in a simple OGNL notation
+     */
+    public static Expression mandatoryBodyOgnlExpression(final String name, final String ognl) {
+        return new ExpressionAdapter() {
+            private ClassResolver classResolver;
+            private Expression exp;
+            private Language bean;
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                String text = exp.evaluate(exchange, String.class);
+                Class<?> type;
+                try {
+                    type = classResolver.resolveMandatoryClass(text);
+                } catch (ClassNotFoundException e) {
+                    throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
+                }
+                Object body;
+                try {
+                    body = exchange.getIn().getMandatoryBody(type);
+                } catch (InvalidPayloadException e) {
+                    throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
+                }
+                Expression ognlExp = bean.createExpression(null, new Object[] { null, body, ognl });
+                ognlExp.init(exchange.getContext());
+                return ognlExp.evaluate(exchange, Object.class);
+            }
+
+            @Override
+            public void init(CamelContext context) {
+                classResolver = context.getClassResolver();
+                exp = ExpressionBuilder.simpleExpression(name);
+                exp.init(context);
+                bean = context.resolveLanguage("bean");
+            }
+
+            @Override
+            public String toString() {
+                return "mandatoryBodyAs[" + name + "](" + ognl + ")";
+            }
+        };
+    }
+
+    /**
+     * Returns the expression for the exchanges inbound message body converted to the given type
+     */
+    public static Expression mandatoryBodyExpression(final String name) {
+        return new ExpressionAdapter() {
+            private ClassResolver classResolver;
+            private Expression exp;
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                String text = exp.evaluate(exchange, String.class);
+                Class<?> type;
+                try {
+                    type = classResolver.resolveMandatoryClass(text);
+                } catch (ClassNotFoundException e) {
+                    throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
+                }
+                try {
+                    return exchange.getIn().getMandatoryBody(type);
+                } catch (InvalidPayloadException e) {
+                    throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
+                }
+            }
+
+            @Override
+            public void init(CamelContext context) {
+                classResolver = context.getClassResolver();
+                exp = ExpressionBuilder.simpleExpression(name);
+                exp.init(context);
+            }
+
+            @Override
+            public String toString() {
+                return "mandatoryBodyAs[" + name + "]";
+            }
+        };
+    }
+
+    /**
+     * Returns the expression for the message converted to the given type and invoking methods on the converted message
+     * defined in a simple OGNL notation
+     */
+    public static Expression messageOgnlExpression(final String name, final String ognl) {
+        return new ExpressionAdapter() {
+            private ClassResolver classResolver;
+            private Expression exp;
+            private Language bean;
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                String text = exp.evaluate(exchange, String.class);
+                Class<?> type;
+                try {
+                    type = classResolver.resolveMandatoryClass(text);
+                } catch (ClassNotFoundException e) {
+                    throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
+                }
+                Object msg = exchange.getMessage(type);
+                if (msg != null) {
+                    // ognl is able to evaluate method name if it contains nested functions
+                    // so we should not eager evaluate ognl as a string
+                    Expression ognlExp = bean.createExpression(null, new Object[] { null, msg, ognl });
+                    ognlExp.init(exchange.getContext());
+                    return ognlExp.evaluate(exchange, Object.class);
+                } else {
+                    return null;
+                }
+            }
+
+            @Override
+            public void init(CamelContext context) {
+                classResolver = context.getClassResolver();
+                exp = ExpressionBuilder.simpleExpression(name);
+                exp.init(context);
+                bean = context.resolveLanguage("bean");
+            }
+
+            @Override
+            public String toString() {
+                return "messageOgnlAs[" + name + "](" + ognl + ")";
+            }
+        };
+    }
+
+    /**
+     * Returns the expression for the exchanges inbound message body converted to the given type and invoking methods on
+     * the converted body defined in a simple OGNL notation
+     */
+    public static Expression bodyOgnlExpression(final String name, final String ognl) {
+        return new ExpressionAdapter() {
+            private ClassResolver classResolver;
+            private Expression exp;
+            private Language bean;
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                String text = exp.evaluate(exchange, String.class);
+                Class<?> type;
+                try {
+                    type = classResolver.resolveMandatoryClass(text);
+                } catch (ClassNotFoundException e) {
+                    throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
+                }
+                Object body = exchange.getIn().getBody(type);
+                if (body != null) {
+                    // ognl is able to evaluate method name if it contains nested functions
+                    // so we should not eager evaluate ognl as a string
+                    Expression ognlExp = bean.createExpression(null, new Object[] { null, body, ognl });
+                    ognlExp.init(exchange.getContext());
+                    return ognlExp.evaluate(exchange, Object.class);
+                } else {
+                    return null;
+                }
+            }
+
+            @Override
+            public void init(CamelContext context) {
+                classResolver = context.getClassResolver();
+                exp = ExpressionBuilder.simpleExpression(name);
+                exp.init(context);
+                bean = context.resolveLanguage("bean");
+            }
+
+            @Override
+            public String toString() {
+                return "bodyOgnlAs[" + name + "](" + ognl + ")";
+            }
+        };
+    }
+
+    /**
+     * Returns the expression for the exchange invoking methods defined in a simple OGNL notation
+     *
+     * @param ognl methods to invoke on the exchange in a simple OGNL syntax
+     */
+    public static Expression exchangeOgnlExpression(final String ognl) {
+        return new ExpressionAdapter() {
+            private Language bean;
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                // ognl is able to evaluate method name if it contains nested functions
+                // so we should not eager evaluate ognl as a string
+                Expression ognlExp = bean.createExpression(null, new Object[] { null, exchange, ognl });
+                ognlExp.init(exchange.getContext());
+                return ognlExp.evaluate(exchange, Object.class);
+            }
+
+            @Override
+            public void init(CamelContext context) {
+                bean = context.resolveLanguage("bean");
+            }
+
+            @Override
+            public String toString() {
+                return "exchangeOgnl(" + ognl + ")";
+            }
+        };
+    }
+
+    /**
+     * Returns the expression for the exchanges camelContext invoking methods defined in a simple OGNL notation
+     *
+     * @param ognl methods to invoke on the context in a simple OGNL syntax
+     */
+    public static Expression camelContextOgnlExpression(final String ognl) {
+        return new ExpressionAdapter() {
+            private Expression exp;
+
+            @Override
+            public void init(CamelContext context) {
+                exp = ExpressionBuilder.beanExpression(context, ognl);
+                exp.init(context);
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                // ognl is able to evaluate method name if it contains nested functions
+                // so we should not eager evaluate ognl as a string
+                return exp.evaluate(exchange, Object.class);
+            }
+
+            @Override
+            public String toString() {
+                return "camelContextOgnl(" + ognl + ")";
+            }
+        };
+    }
+
+    /**
+     * Returns the expression for the exchanges inbound message body invoking methods defined in a simple OGNL notation
+     *
+     * @param ognl methods to invoke on the body in a simple OGNL syntax
+     */
+    public static Expression bodyOgnlExpression(final String ognl) {
+        return new ExpressionAdapter() {
+            private Language bean;
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                Object body = exchange.getIn().getBody();
+                if (body == null) {
+                    return null;
+                }
+                Expression ognlExp = bean.createExpression(null, new Object[] { null, body, ognl });
+                ognlExp.init(exchange.getContext());
+                return ognlExp.evaluate(exchange, Object.class);
+            }
+
+            @Override
+            public void init(CamelContext context) {
+                bean = context.resolveLanguage("bean");
+            }
+
+            @Override
+            public String toString() {
+                return "bodyOgnl(" + ognl + ")";
+            }
+        };
+    }
+
+    /**
+     * Returns an expression for the property value of exchange with the given name invoking methods defined in a simple
+     * OGNL notation
+     *
+     * @param ognl methods to invoke on the property in a simple OGNL syntax
+     */
+    public static Expression propertyOgnlExpression(final String ognl) {
+        return new KeyedOgnlExpressionAdapter(
+                ognl, "propertyOgnl(" + ognl + ")",
+                (exchange, exp) -> {
+                    String text = exp.evaluate(exchange, String.class);
+                    return exchange.getProperty(text);
+                });
+    }
+
+    /**
+     * 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
+     */
+    public static Expression exchangeExceptionOgnlExpression(final String ognl) {
+        return new ExpressionAdapter() {
+            private Language bean;
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                Object exception = exchange.getException();
+                if (exception == null) {
+                    exception = exchange.getProperty(ExchangePropertyKey.EXCEPTION_CAUGHT, Exception.class);
+                }
+
+                if (exception == null) {
+                    return null;
+                }
+
+                // ognl is able to evaluate method name if it contains nested functions
+                // so we should not eager evaluate ognl as a string
+                Expression ognlExp = bean.createExpression(null, new Object[] { null, exception, ognl });
+                ognlExp.init(exchange.getContext());
+                return ognlExp.evaluate(exchange, Object.class);
+            }
+
+            @Override
+            public void init(CamelContext context) {
+                bean = context.resolveLanguage("bean");
+            }
+
+            @Override
+            public String toString() {
+                return "exchangeExceptionOgnl(" + ognl + ")";
+            }
+        };
+    }
+
+    /**
+     * Expression adapter for OGNL expression from Message Header or Exchange property
+     */
+    public static class KeyedOgnlExpressionAdapter extends ExpressionAdapter {
+        private final String ognl;
+        private final String toStringValue;
+        private final KeyedEntityRetrievalStrategy keyedEntityRetrievalStrategy;
+        private String key;
+        private final String method;
+        private Expression keyExpression;
+        private Expression ognlExpression;
+        private Language beanLanguage;
+
+        public KeyedOgnlExpressionAdapter(String ognl, String toStringValue,
+                                          KeyedEntityRetrievalStrategy keyedEntityRetrievalStrategy) {
+            this.ognl = ognl;
+            this.toStringValue = toStringValue;
+            this.keyedEntityRetrievalStrategy = keyedEntityRetrievalStrategy;
+
+            // Split ognl except when this is not a Map, Array
+            // and we would like to keep the dots within the key name
+            List<String> methods = OgnlHelper.splitOgnl(ognl);
+
+            key = methods.get(0);
+            String keySuffix = "";
+            // if ognl starts with a key inside brackets (eg: [foo.bar])
+            // remove starting and ending brackets from key
+            if (key.startsWith("[") && key.endsWith("]")) {
+                key = StringHelper.removeLeadingAndEndingQuotes(key.substring(1, key.length() - 1));
+                keySuffix = StringHelper.after(methods.get(0), key);
+            }
+            // remove any OGNL operators so we got the pure key name
+            key = OgnlHelper.removeOperators(key);
+            // and this may be the last remainder method to try as OGNL if there are no exchange properties with those key names
+            method = StringHelper.after(ognl, key + keySuffix);
+        }
+
+        @Override
+        public void init(CamelContext context) {
+            beanLanguage = context.resolveLanguage("bean");
+            ognlExpression = ExpressionBuilder.simpleExpression(ognl);
+            ognlExpression.init(context);
+            // key must be lazy eval as it only used in special situations
+        }
+
+        @Override
+        public Object evaluate(Exchange exchange) {
+            // try with full name first
+            Object property = keyedEntityRetrievalStrategy.getKeyedEntity(exchange, ognlExpression);
+            if (property != null) {
+                return property;
+            }
+
+            // key must be lazy eval as it only used in special situations
+            if (keyExpression == null) {
+                keyExpression = ExpressionBuilder.simpleExpression(key);
+                keyExpression.init(exchange.getContext());
+            }
+
+            property = keyedEntityRetrievalStrategy.getKeyedEntity(exchange, keyExpression);
+            if (property == null) {
+                return null;
+            }
+            if (method != null) {
+                Expression exp = beanLanguage.createExpression(null, new Object[] { null, property, method });
+                exp.init(exchange.getContext());
+                return exp.evaluate(exchange, Object.class);
+            } else {
+                return property;
+            }
+        }
+
+        @Override
+        public String toString() {
+            return toStringValue;
+        }
+
+        /**
+         * Strategy to retrieve the value based on the key
+         */
+        public interface KeyedEntityRetrievalStrategy {
+            Object getKeyedEntity(Exchange exchange, Expression key);
+        }
+    }
+}
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
deleted file mode 100644
index 81ff6c0225a7..000000000000
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionBuilder.java
+++ /dev/null
@@ -1,3523 +0,0 @@
-/*
- * 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;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.lang.reflect.Constructor;
-import java.security.DigestInputStream;
-import java.security.MessageDigest;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Random;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.regex.Pattern;
-
-import org.apache.camel.CamelContext;
-import org.apache.camel.CamelExecutionException;
-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.RuntimeCamelException;
-import org.apache.camel.StreamCache;
-import org.apache.camel.spi.ClassResolver;
-import org.apache.camel.spi.ExchangeFormatter;
-import org.apache.camel.spi.Language;
-import org.apache.camel.spi.SimpleFunctionRegistry;
-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.ExpressionAdapter;
-import org.apache.camel.support.LanguageHelper;
-import org.apache.camel.support.MessageHelper;
-import org.apache.camel.support.RandomUuidGenerator;
-import org.apache.camel.support.ResourceHelper;
-import org.apache.camel.support.ShortUuidGenerator;
-import org.apache.camel.support.SimpleUuidGenerator;
-import org.apache.camel.support.builder.ExpressionBuilder;
-import org.apache.camel.support.builder.PredicateBuilder;
-import org.apache.camel.util.FileUtil;
-import org.apache.camel.util.IOHelper;
-import org.apache.camel.util.ObjectHelper;
-import org.apache.camel.util.OgnlHelper;
-import org.apache.camel.util.SkipIterator;
-import org.apache.camel.util.StringHelper;
-import org.apache.camel.util.StringQuoteHelper;
-import org.apache.camel.util.json.JsonArray;
-import org.apache.camel.util.json.JsonObject;
-import org.apache.camel.util.json.Jsonable;
-
-/**
- * Expression builder used by the simple language.
- */
-public final class SimpleExpressionBuilder {
-
-    private static final Pattern OFFSET_PATTERN = Pattern.compile("([+-])([^+-]+)");
-
-    private SimpleExpressionBuilder() {
-    }
-
-    /**
-     * Returns the expression for the exchanges inbound message header invoking methods defined in a simple OGNL
-     * notation
-     *
-     * @param ognl methods to invoke on the header in a simple OGNL syntax
-     */
-    public static Expression headersOgnlExpression(final String ognl) {
-        return new KeyedOgnlExpressionAdapter(
-                ognl, "headerOgnl(" + ognl + ")",
-                (exchange, exp) -> {
-                    String text = exp.evaluate(exchange, String.class);
-                    return exchange.getIn().getHeader(text);
-                });
-    }
-
-    /**
-     * Returns the expression for the variable invoking methods defined in a simple OGNL notation
-     *
-     * @param ognl methods to invoke on the variable in a simple OGNL syntax
-     */
-    public static Expression variablesOgnlExpression(final String ognl) {
-        return new KeyedOgnlExpressionAdapter(
-                ognl, "variableOgnl(" + ognl + ")",
-                (exchange, exp) -> {
-                    String text = exp.evaluate(exchange, String.class);
-                    return ExchangeHelper.getVariable(exchange, text);
-                });
-    }
-
-    /**
-     * Returns the message history (including exchange details or not)
-     */
-    public static Expression messageHistoryExpression(final boolean detailed) {
-        return new ExpressionAdapter() {
-
-            private ExchangeFormatter formatter;
-
-            @Override
-            public void init(CamelContext context) {
-                if (detailed) {
-                    // use the exchange formatter to log exchange details
-                    formatter = getOrCreateExchangeFormatter(context);
-                }
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                return MessageHelper.dumpMessageHistoryStacktrace(exchange, formatter, false);
-            }
-
-            private ExchangeFormatter getOrCreateExchangeFormatter(CamelContext camelContext) {
-                return LanguageHelper.getOrCreateExchangeFormatter(camelContext, formatter);
-            }
-
-            @Override
-            public String toString() {
-                return "messageHistory(" + detailed + ")";
-            }
-        };
-    }
-
-    /**
-     * Returns an iterator to collate (iterate) the given expression
-     */
-    public static Expression collateExpression(final String expression, final int group) {
-        return new ExpressionAdapter() {
-            private Expression exp;
-
-            @Override
-            public void init(CamelContext context) {
-                // first use simple then create the group expression
-                exp = context.resolveLanguage("simple").createExpression(expression);
-                exp.init(context);
-                exp = ExpressionBuilder.groupIteratorExpression(exp, null, Integer.toString(group), false);
-                exp.init(context);
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                return exp.evaluate(exchange, Object.class);
-            }
-
-            @Override
-            public String toString() {
-                return "collate(" + expression + "," + group + ")";
-            }
-        };
-    }
-
-    /**
-     * Returns an iterator to collate (iterate) the given expression
-     */
-    public static Expression collateExpression(final String expression, final String group) {
-        return new ExpressionAdapter() {
-            private Expression exp;
-            private Expression num;
-
-            @Override
-            public void init(CamelContext context) {
-                exp = context.resolveLanguage("simple").createExpression(expression);
-                exp.init(context);
-                num = context.resolveLanguage("simple").createExpression(group);
-                num.init(context);
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                Integer n = num.evaluate(exchange, Integer.class);
-                exp = ExpressionBuilder.groupIteratorExpression(exp, null, Integer.toString(n), false);
-                exp.init(exchange.getContext());
-                return exp.evaluate(exchange, Object.class);
-            }
-
-            @Override
-            public String toString() {
-                return "collate(" + expression + "," + group + ")";
-            }
-        };
-    }
-
-    /**
-     * Returns an iterator to skip (iterate) the given expression
-     */
-    public static Expression skipExpression(final String expression, final int number) {
-        return new ExpressionAdapter() {
-            private Expression exp;
-
-            @Override
-            public void init(CamelContext context) {
-                exp = context.resolveLanguage("simple").createExpression(expression);
-                exp.init(context);
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                return skipIteratorExpression(exp, number).evaluate(exchange, Object.class);
-            }
-
-            @Override
-            public String toString() {
-                return "skip(" + expression + "," + number + ")";
-            }
-        };
-    }
-
-    /**
-     * Returns an iterator to skip (iterate) the given expression
-     */
-    public static Expression skipExpression(final String expression, final String number) {
-        return new ExpressionAdapter() {
-            private Expression exp;
-            private Expression num;
-
-            @Override
-            public void init(CamelContext context) {
-                exp = context.resolveLanguage("simple").createExpression(expression);
-                exp.init(context);
-                num = context.resolveLanguage("simple").createExpression(number);
-                num.init(context);
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                int n = num.evaluate(exchange, Integer.class);
-                return skipIteratorExpression(exp, n).evaluate(exchange, Object.class);
-            }
-
-            @Override
-            public String toString() {
-                return "skip(" + expression + "," + number + ")";
-            }
-        };
-    }
-
-    /**
-     * Converts the given expressions to a number and return the absolute value (uses message body if expression is
-     * null)
-     */
-    public static Expression absExpression(final String expression) {
-        return new ExpressionAdapter() {
-            private Expression exp;
-
-            @Override
-            public void init(CamelContext context) {
-                if (expression != null) {
-                    exp = context.resolveLanguage("simple").createExpression(expression);
-                    exp.init(context);
-                }
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                Long value;
-                if (exp != null) {
-                    value = exp.evaluate(exchange, Long.class);
-                } else {
-                    value = exchange.getMessage().getBody(Long.class);
-                }
-                if (value != null) {
-                    value = Math.abs(value);
-                }
-                return value;
-            }
-
-            @Override
-            public String toString() {
-                if (expression != null) {
-                    return "abs(" + expression + ")";
-                } else {
-                    return "abs()";
-                }
-            }
-        };
-    }
-
-    /**
-     * Converts the given expressions to a floating number and return the floor value (uses message body if expression
-     * is null)
-     */
-    public static Expression floorExpression(final String expression) {
-        return new ExpressionAdapter() {
-            private Expression exp;
-
-            @Override
-            public void init(CamelContext context) {
-                if (expression != null) {
-                    exp = context.resolveLanguage("simple").createExpression(expression);
-                    exp.init(context);
-                }
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                Double value;
-                if (exp != null) {
-                    value = exp.evaluate(exchange, Double.class);
-                } else {
-                    value = exchange.getMessage().getBody(Double.class);
-                }
-                if (value != null) {
-                    double d = Math.floor(value);
-                    return (int) d;
-                }
-                return value;
-            }
-
-            @Override
-            public String toString() {
-                if (expression != null) {
-                    return "floor(" + expression + ")";
-                } else {
-                    return "floor()";
-                }
-            }
-        };
-    }
-
-    /**
-     * Converts the given expressions to a floating number and return the ceil value (uses message body if expression is
-     * null)
-     */
-    public static Expression ceilExpression(final String expression) {
-        return new ExpressionAdapter() {
-            private Expression exp;
-
-            @Override
-            public void init(CamelContext context) {
-                if (expression != null) {
-                    exp = context.resolveLanguage("simple").createExpression(expression);
-                    exp.init(context);
-                }
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                Double value;
-                if (exp != null) {
-                    value = exp.evaluate(exchange, Double.class);
-                } else {
-                    value = exchange.getMessage().getBody(Double.class);
-                }
-                if (value != null) {
-                    double d = Math.ceil(value);
-                    return (int) d;
-                }
-                return value;
-            }
-
-            @Override
-            public String toString() {
-                if (expression != null) {
-                    return "ceil(" + expression + ")";
-                } else {
-                    return "ceil()";
-                }
-            }
-        };
-    }
-
-    /**
-     * Whether the expression is empty or having a list/map which has no elements.
-     */
-    public static Expression isEmptyExpression(final String expression) {
-        return new ExpressionAdapter() {
-            private Expression exp;
-
-            @Override
-            public void init(CamelContext context) {
-                if (expression != null) {
-                    exp = context.resolveLanguage("simple").createExpression(expression);
-                    exp.init(context);
-                }
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                Object body;
-                if (exp != null) {
-                    body = exp.evaluate(exchange, Object.class);
-                } else {
-                    body = exchange.getMessage().getBody();
-                }
-                // this may be an object that we can iterate
-                Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(body);
-                for (Object o : it) {
-                    if (o != null) {
-                        return false;
-                    }
-                }
-                return true;
-            }
-
-            @Override
-            public String toString() {
-                if (expression != null) {
-                    return "isEmpty(" + expression + ")";
-                } else {
-                    return "isEmpty()";
-                }
-            }
-        };
-    }
-
-    /**
-     * Whether the expression is an alphabetic String
-     */
-    public static Expression isAlphaExpression(final String expression) {
-        return new ExpressionAdapter() {
-            private Expression exp;
-
-            @Override
-            public void init(CamelContext context) {
-                if (expression != null) {
-                    exp = context.resolveLanguage("simple").createExpression(expression);
-                    exp.init(context);
-                }
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                String body;
-                if (exp != null) {
-                    body = exp.evaluate(exchange, String.class);
-                } else {
-                    body = exchange.getMessage().getBody(String.class);
-                }
-                if (body == null || body.isBlank()) {
-                    return false;
-                }
-                for (int i = 0; i < body.length(); i++) {
-                    char ch = body.charAt(i);
-                    if (!Character.isLetter(ch)) {
-                        return false;
-                    }
-                }
-                return true;
-            }
-
-            @Override
-            public String toString() {
-                if (expression != null) {
-                    return "isAlpha(" + expression + ")";
-                } else {
-                    return "isAlpha()";
-                }
-            }
-        };
-    }
-
-    /**
-     * Whether the expression is a number (integral or floating)
-     */
-    public static Expression isNumericExpression(final String expression) {
-        return new ExpressionAdapter() {
-            private Expression exp;
-
-            @Override
-            public void init(CamelContext context) {
-                if (expression != null) {
-                    exp = context.resolveLanguage("simple").createExpression(expression);
-                    exp.init(context);
-                }
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                String body;
-                if (exp != null) {
-                    body = exp.evaluate(exchange, String.class);
-                } else {
-                    body = exchange.getMessage().getBody(String.class);
-                }
-                if (body == null || body.isBlank()) {
-                    return false;
-                }
-                for (int i = 0; i < body.length(); i++) {
-                    char ch = body.charAt(i);
-                    if (!Character.isDigit(ch)) {
-                        return false;
-                    }
-                }
-                return true;
-            }
-
-            @Override
-            public String toString() {
-                if (expression != null) {
-                    return "isNumeric(" + expression + ")";
-                } else {
-                    return "isNumeric()";
-                }
-            }
-        };
-    }
-
-    /**
-     * Whether the expression is an alphabetic or numeric String
-     */
-    public static Expression isAlphaNumericExpression(final String expression) {
-        return new ExpressionAdapter() {
-            private Expression exp;
-
-            @Override
-            public void init(CamelContext context) {
-                if (expression != null) {
-                    exp = context.resolveLanguage("simple").createExpression(expression);
-                    exp.init(context);
-                }
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                String body;
-                if (exp != null) {
-                    body = exp.evaluate(exchange, String.class);
-                } else {
-                    body = exchange.getMessage().getBody(String.class);
-                }
-                if (body == null || body.isBlank()) {
-                    return false;
-                }
-                for (int i = 0; i < body.length(); i++) {
-                    char ch = body.charAt(i);
-                    if (!Character.isLetterOrDigit(ch)) {
-                        return false;
-                    }
-                }
-                return true;
-            }
-
-            @Override
-            public String toString() {
-                if (expression != null) {
-                    return "isAlphaNumeric(" + expression + ")";
-                } else {
-                    return "isAlphaNumeric()";
-                }
-            }
-        };
-    }
-
-    /**
-     * Returns the opposite result of the predicate
-     */
-    public static Expression isNotPredicate(final String predicate) {
-        return new ExpressionAdapter() {
-            private Predicate pred;
-
-            @Override
-            public void init(CamelContext context) {
-                pred = PredicateBuilder.not(context.resolveLanguage("simple").createPredicate(predicate));
-                pred.init(context);
-            }
-
-            public Object evaluate(Exchange exchange) {
-                return pred.matches(exchange);
-            }
-
-            @Override
-            public String toString() {
-                if (predicate != null) {
-                    return "not(" + predicate + ")";
-                } else {
-                    return "not()";
-                }
-            }
-        };
-    }
-
-    /**
-     * What kind of type is the expression
-     */
-    public static Expression kindOfTypeExpression(final String expression) {
-        return new ExpressionAdapter() {
-            private Expression exp;
-
-            @Override
-            public void init(CamelContext context) {
-                if (expression != null) {
-                    exp = context.resolveLanguage("simple").createExpression(expression);
-                    exp.init(context);
-                }
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                Object value;
-                if (exp != null) {
-                    value = exp.evaluate(exchange, Object.class);
-                } else {
-                    value = exchange.getMessage().getBody();
-                }
-                if (value != null) {
-                    Class<?> type = value.getClass();
-                    if (ObjectHelper.isNumericType(type)) {
-                        return "number";
-                    } else if (boolean.class == type || Boolean.class == type) {
-                        return "boolean";
-                    } else if (value instanceof CharSequence) {
-                        return "string";
-                    } else if (ObjectHelper.isPrimitiveArrayType(type) || value instanceof Collection
-                            || value instanceof Map<?, ?>) {
-                        return "array";
-                    } else {
-                        return "object";
-                    }
-                }
-                return "null";
-            }
-
-            @Override
-            public String toString() {
-                if (expression != null) {
-                    return "kindOfType(" + expression + ")";
-                } else {
-                    return "kindOfType()";
-                }
-            }
-        };
-    }
-
-    /**
-     * Safe quotes the given expressions (uses message body if expression is null) if necessary.
-     */
-    public static Expression safeQuoteExpression(final String expression) {
-        return new ExpressionAdapter() {
-            private Expression exp;
-
-            @Override
-            public void init(CamelContext context) {
-                if (expression != null) {
-                    exp = context.resolveLanguage("simple").createExpression(expression);
-                    exp.init(context);
-                }
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                Object value;
-                if (exp != null) {
-                    value = exp.evaluate(exchange, Object.class);
-                } else {
-                    value = exchange.getMessage().getBody(Object.class);
-                }
-                if (value != null) {
-                    String type = kindOfType(value);
-                    if ("string".equals(type) || "array".equals(type) || "object".equals(type)) {
-                        String body = exchange.getContext().getTypeConverter().tryConvertTo(String.class, exchange, value);
-                        body = StringHelper.removeLeadingAndEndingQuotes(body);
-                        value = StringQuoteHelper.doubleQuote(body);
-                    }
-                }
-                return value;
-            }
-
-            private String kindOfType(Object value) {
-                Class<?> type = value.getClass();
-                if (ObjectHelper.isNumericType(type)) {
-                    return "number";
-                } else if (boolean.class == type || Boolean.class == type) {
-                    return "boolean";
-                } else if (value instanceof CharSequence) {
-                    return "string";
-                } else if (ObjectHelper.isPrimitiveArrayType(type) || value instanceof Collection
-                        || value instanceof Map<?, ?>) {
-                    return "array";
-                } else {
-                    return "object";
-                }
-            }
-
-            @Override
-            public String toString() {
-                if (expression != null) {
-                    return "safeQuote(" + expression + ")";
-                } else {
-                    return "safeQuote()";
-                }
-            }
-        };
-    }
-
-    /**
-     * Double quotes the given expressions (uses message body if expression is null)
-     */
-    public static Expression quoteExpression(final String expression) {
-        return new ExpressionAdapter() {
-            private Expression exp;
-
-            @Override
-            public void init(CamelContext context) {
-                if (expression != null) {
-                    exp = context.resolveLanguage("simple").createExpression(expression);
-                    exp.init(context);
-                }
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                String value;
-                if (exp != null) {
-                    value = exp.evaluate(exchange, String.class);
-                } else {
-                    value = exchange.getMessage().getBody(String.class);
-                }
-                if (value != null && !StringHelper.isDoubleQuoted(value)) {
-                    value = StringHelper.removeLeadingAndEndingQuotes(value);
-                    value = StringQuoteHelper.doubleQuote(value);
-                }
-                return value;
-            }
-
-            @Override
-            public String toString() {
-                if (expression != null) {
-                    return "quote(" + expression + ")";
-                } else {
-                    return "quote()";
-                }
-            }
-        };
-    }
-
-    /**
-     * Un quotes the given expressions (uses message body if expression is null)
-     */
-    public static Expression unquoteExpression(final String expression) {
-        return new ExpressionAdapter() {
-            private Expression exp;
-
-            @Override
-            public void init(CamelContext context) {
-                if (expression != null) {
-                    exp = context.resolveLanguage("simple").createExpression(expression);
-                    exp.init(context);
-                }
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                String value;
-                if (exp != null) {
-                    value = exp.evaluate(exchange, String.class);
-                } else {
-                    value = exchange.getMessage().getBody(String.class);
-                }
-                if (value != null) {
-                    value = StringHelper.removeLeadingAndEndingQuotes(value);
-                }
-                return value;
-            }
-
-            @Override
-            public String toString() {
-                if (expression != null) {
-                    return "unquote(" + expression + ")";
-                } else {
-                    return "unquote()";
-                }
-            }
-        };
-    }
-
-    /**
-     * Trims the given expressions (uses message body if expression is null)
-     */
-    public static Expression trimExpression(final String expression) {
-        return new ExpressionAdapter() {
-            private Expression exp;
-
-            @Override
-            public void init(CamelContext context) {
-                if (expression != null) {
-                    exp = context.resolveLanguage("simple").createExpression(expression);
-                    exp.init(context);
-                }
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                String value;
-                if (exp != null) {
-                    value = exp.evaluate(exchange, String.class);
-                } else {
-                    value = exchange.getMessage().getBody(String.class);
-                }
-                if (value != null) {
-                    value = value.trim();
-                }
-                return value;
-            }
-
-            @Override
-            public String toString() {
-                if (expression != null) {
-                    return "trim(" + expression + ")";
-                } else {
-                    return "trim()";
-                }
-            }
-        };
-    }
-
-    /**
-     * Capitalizes the given expressions (uses message body if expression is null)
-     */
-    public static Expression capitalizeExpression(final String expression) {
-        return new ExpressionAdapter() {
-            private Expression exp;
-
-            @Override
-            public void init(CamelContext context) {
-                if (expression != null) {
-                    exp = context.resolveLanguage("simple").createExpression(expression);
-                    exp.init(context);
-                }
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                String value;
-                if (exp != null) {
-                    value = exp.evaluate(exchange, String.class);
-                } else {
-                    value = exchange.getMessage().getBody(String.class);
-                }
-                if (value != null) {
-                    value = StringHelper.capitalizeAll(value);
-                }
-                return value;
-            }
-
-            @Override
-            public String toString() {
-                if (expression != null) {
-                    return "capitalize(" + expression + ")";
-                } else {
-                    return "capitalize()";
-                }
-            }
-        };
-    }
-
-    /**
-     * Pad the expression
-     */
-    public static Expression padExpression(final String expression, final String length, final String separator) {
-        return new ExpressionAdapter() {
-            private Expression exp;
-            private Expression len;
-
-            @Override
-            public void init(CamelContext context) {
-                exp = context.resolveLanguage("simple").createExpression(expression);
-                exp.init(context);
-                len = context.resolveLanguage("simple").createExpression(length);
-                len.init(context);
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                String answer = exp.evaluate(exchange, String.class);
-                Integer width = len.evaluate(exchange, Integer.class);
-                String sep = separator;
-                if (sep == null || sep.isEmpty()) {
-                    sep = " ";
-                }
-
-                int max = Math.abs(width);
-                while (max > answer.length()) {
-                    if (width > 0) {
-                        answer = answer + sep;
-                    } else {
-                        answer = sep + answer;
-                    }
-                }
-                return answer;
-            }
-
-            @Override
-            public String toString() {
-                return "pad(" + exp + "," + length + ")";
-            }
-        };
-    }
-
-    /**
-     * String concats the two expressions.
-     */
-    public static Expression concatExpression(final String right, final String left, String separator) {
-        return new ExpressionAdapter() {
-            private Expression exp1;
-            private Expression exp2;
-
-            @Override
-            public void init(CamelContext context) {
-                exp1 = context.resolveLanguage("simple").createExpression(right);
-                exp1.init(context);
-                exp2 = context.resolveLanguage("simple").createExpression(left);
-                exp2.init(context);
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                String value1 = exp1.evaluate(exchange, String.class);
-                String value2 = exp2.evaluate(exchange, String.class);
-                if (value1 != null && value2 != null) {
-                    return value1 + (separator != null ? separator : "") + value2;
-                } else {
-                    return value1 != null ? value1 : value2;
-                }
-            }
-
-            @Override
-            public String toString() {
-                return "concat(" + right + "," + left + ")";
-            }
-        };
-    }
-
-    /**
-     * Throws an exception with the given message.
-     */
-    public static Expression throwExceptionExpression(String msg, String type) {
-        return new ExpressionAdapter() {
-            private Class<?> clazz;
-            private Expression exp;
-
-            @Override
-            public void init(CamelContext context) {
-                if (type == null) {
-                    clazz = IllegalArgumentException.class;
-                } else {
-                    try {
-                        clazz = context.getClassResolver().resolveMandatoryClass(type);
-                    } catch (ClassNotFoundException e) {
-                        throw CamelExecutionException.wrapRuntimeException(e);
-                    }
-                }
-                exp = context.resolveLanguage("simple").createExpression(msg);
-                exp.init(context);
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                try {
-                    String text = exp.evaluate(exchange, String.class);
-                    // create a new exception to that type, and provide the message as
-                    Constructor<?> constructor = clazz.getConstructor(String.class);
-                    Exception cause = (Exception) constructor.newInstance(text);
-                    if (cause instanceof RuntimeException re) {
-                        throw re;
-                    } else {
-                        RuntimeException re = new RuntimeCamelException(cause);
-                        throw re;
-                    }
-                } catch (Exception e) {
-                    throw new RuntimeException(e);
-                }
-            }
-
-            @Override
-            public String toString() {
-                return "throwException(" + msg + ")";
-            }
-        };
-    }
-
-    /**
-     * Assert the expression
-     */
-    public static Expression assertExpression(String expression, String message) {
-        return new ExpressionAdapter() {
-            private Predicate pred;
-
-            @Override
-            public void init(CamelContext context) {
-                pred = context.resolveLanguage("simple").createPredicate(expression);
-                pred.init(context);
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                if (!pred.matches(exchange)) {
-                    throw new SimpleAssertionException(message);
-                }
-                return null;
-            }
-
-            @Override
-            public String toString() {
-                return "assert(" + pred + ", " + message + ")";
-            }
-        };
-    }
-
-    /**
-     * Converts the result of the expression to the given type
-     */
-    public static Expression convertToExpression(final String expression, final String type) {
-        return new ExpressionAdapter() {
-            private Class<?> clazz;
-            private Expression exp;
-
-            @Override
-            public void init(CamelContext context) {
-                try {
-                    clazz = context.getClassResolver().resolveMandatoryClass(type);
-                } catch (ClassNotFoundException e) {
-                    throw CamelExecutionException.wrapRuntimeException(e);
-                }
-                exp = context.resolveLanguage("simple").createExpression(expression);
-                exp.init(context);
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                return exp.evaluate(exchange, clazz);
-            }
-
-            @Override
-            public String toString() {
-                if (expression != null) {
-                    return "convertTo(" + expression + ", " + type + ")";
-                } else {
-                    return "convertTo(" + type + ")";
-                }
-            }
-        };
-    }
-
-    /**
-     * Converts the result of the expression to the given type and invoking methods on the converted object defined in a
-     * simple OGNL notation
-     */
-    public static Expression convertToOgnlExpression(final String expression, final String type, final String ognl) {
-        return new ExpressionAdapter() {
-            private Class<?> clazz;
-            private Expression exp;
-            private Language bean;
-
-            @Override
-            public void init(CamelContext context) {
-                try {
-                    clazz = context.getClassResolver().resolveMandatoryClass(type);
-                } catch (ClassNotFoundException e) {
-                    throw CamelExecutionException.wrapRuntimeException(e);
-                }
-                exp = context.resolveLanguage("simple").createExpression(expression);
-                exp.init(context);
-                bean = context.resolveLanguage("bean");
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                Object body = exp.evaluate(exchange, clazz);
-                if (body == null) {
-                    return null;
-                }
-                Expression ognlExp = bean.createExpression(null, new Object[] { null, body, ognl });
-                ognlExp.init(exchange.getContext());
-                return ognlExp.evaluate(exchange, Object.class);
-            }
-
-            @Override
-            public String toString() {
-                if (expression != null) {
-                    return "convertToOgnl(" + expression + ", " + type + ")." + ognl;
-                } else {
-                    return "convertToOgnl(" + type + ")." + ognl;
-                }
-            }
-        };
-    }
-
-    /**
-     * Uppercases the given expressions (uses message body if expression is null)
-     */
-    public static Expression uppercaseExpression(final String expression) {
-        return new ExpressionAdapter() {
-            private Expression exp;
-
-            @Override
-            public void init(CamelContext context) {
-                if (expression != null) {
-                    exp = context.resolveLanguage("simple").createExpression(expression);
-                    exp.init(context);
-                }
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                String value;
-                if (exp != null) {
-                    value = exp.evaluate(exchange, String.class);
-                } else {
-                    value = exchange.getMessage().getBody(String.class);
-                }
-                if (value != null) {
-                    value = value.toUpperCase(Locale.ENGLISH);
-                }
-                return value;
-            }
-
-            @Override
-            public String toString() {
-                if (expression != null) {
-                    return "uppercase(" + expression + ")";
-                } else {
-                    return "uppercase()";
-                }
-            }
-        };
-    }
-
-    /**
-     * Lowercases the given expressions (uses message body if expression is null)
-     */
-    public static Expression lowercaseExpression(final String expression) {
-        return new ExpressionAdapter() {
-            private Expression exp;
-
-            @Override
-            public void init(CamelContext context) {
-                if (expression != null) {
-                    exp = context.resolveLanguage("simple").createExpression(expression);
-                    exp.init(context);
-                }
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                String value;
-                if (exp != null) {
-                    value = exp.evaluate(exchange, String.class);
-                } else {
-                    value = exchange.getMessage().getBody(String.class);
-                }
-                if (value != null) {
-                    value = value.toLowerCase(Locale.ENGLISH);
-                }
-                return value;
-            }
-
-            @Override
-            public String toString() {
-                if (expression != null) {
-                    return "lowercase(" + expression + ")";
-                } else {
-                    return "lowercase()";
-                }
-            }
-        };
-    }
-
-    /**
-     * Returns the size of the expression (number of elements in collection/map; otherwise 1)
-     */
-    public static Expression sizeExpression(final String expression) {
-        return new ExpressionAdapter() {
-            private Expression exp;
-
-            @Override
-            public void init(CamelContext context) {
-                if (expression != null) {
-                    exp = context.resolveLanguage("simple").createExpression(expression);
-                    exp.init(context);
-                }
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                Object body;
-                if (exp != null) {
-                    body = exp.evaluate(exchange, Object.class);
-                } else {
-                    body = exchange.getMessage().getBody();
-                }
-                if (body != null) {
-                    if (body instanceof byte[] arr) {
-                        return arr.length;
-                    } else if (body instanceof char[] arr) {
-                        return arr.length;
-                    } else if (body instanceof int[] arr) {
-                        return arr.length;
-                    } else if (body instanceof long[] arr) {
-                        return arr.length;
-                    } else if (body instanceof double[] arr) {
-                        return arr.length;
-                    } else if (body instanceof String[] arr) {
-                        return arr.length;
-                    } else if (body instanceof Collection<?> c) {
-                        return c.size();
-                    } else if (body instanceof Map<?, ?> m) {
-                        return m.size();
-                    } else {
-                        return 1;
-                    }
-                }
-                return 0;
-            }
-
-            @Override
-            public String toString() {
-                if (expression != null) {
-                    return "size(" + expression + ")";
-                } else {
-                    return "size()";
-                }
-            }
-        };
-    }
-
-    /**
-     * Returns the length of the expression (length of payload in bytes)
-     */
-    public static Expression lengthExpression(final String expression) {
-        return new ExpressionAdapter() {
-            private Expression exp;
-
-            @Override
-            public void init(CamelContext context) {
-                if (expression != null) {
-                    exp = context.resolveLanguage("simple").createExpression(expression);
-                    exp.init(context);
-                }
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                Object body;
-                if (exp != null) {
-                    body = exp.evaluate(exchange, Object.class);
-                } else {
-                    body = exchange.getMessage().getBody();
-                }
-                try {
-                    if (body instanceof byte[] arr) {
-                        return arr.length;
-                    } else if (body instanceof char[] arr) {
-                        return arr.length;
-                    } else if (body instanceof int[] arr) {
-                        return arr.length;
-                    } else if (body instanceof long[] arr) {
-                        return arr.length;
-                    } else if (body instanceof double[] arr) {
-                        return arr.length;
-                    } else if (body instanceof String[] arr) {
-                        return arr.length;
-                    } else if (body instanceof StreamCache sc) {
-                        return (int) sc.length();
-                    } else {
-                        // first read as stream
-                        InputStream is = null;
-                        try {
-                            is = exchange.getContext().getTypeConverter().tryConvertTo(InputStream.class, exchange, body);
-                            int len = 0;
-                            while (is.read() != -1) {
-                                len++;
-                            }
-                            return len;
-                        } catch (Exception e) {
-                            // ignore
-                        } finally {
-                            IOHelper.close(is);
-                        }
-                        // fallback to use string based
-                        String data = exchange.getContext().getTypeConverter().tryConvertTo(String.class, exchange, body);
-                        if (data != null) {
-                            return data.length();
-                        }
-                    }
-                } finally {
-                    if (body instanceof StreamCache streamCache) {
-                        streamCache.reset();
-                    }
-                }
-                return null;
-            }
-
-            @Override
-            public String toString() {
-                if (expression != null) {
-                    return "length(" + expression + ")";
-                } else {
-                    return "length()";
-                }
-            }
-        };
-    }
-
-    /**
-     * Normalizes the whitespaces in the given expressions (uses message body if expression is null)
-     */
-    public static Expression normalizeWhitespaceExpression(final String expression) {
-        return new ExpressionAdapter() {
-            private Expression exp;
-
-            @Override
-            public void init(CamelContext context) {
-                if (expression != null) {
-                    exp = context.resolveLanguage("simple").createExpression(expression);
-                    exp.init(context);
-                }
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                String value;
-                if (exp != null) {
-                    value = exp.evaluate(exchange, String.class);
-                } else {
-                    value = exchange.getMessage().getBody(String.class);
-                }
-                if (value != null) {
-                    value = StringHelper.normalizeWhitespace(value);
-                }
-                return value;
-            }
-
-            @Override
-            public String toString() {
-                if (expression != null) {
-                    return "normalizeWhitespace(" + expression + ")";
-                } else {
-                    return "normalizeWhitespace()";
-                }
-            }
-        };
-    }
-
-    /**
-     * 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 + ")";
-            }
-        };
-    }
-
-    /**
-     * An expression that returns the distinct values from the expressions
-     */
-    public static Expression distinctExpression(String[] values) {
-        return new ExpressionAdapter() {
-
-            private final Expression[] exps = new Expression[values != null ? values.length : 0];
-
-            @Override
-            public void init(CamelContext context) {
-                for (int i = 0; values != null && i < values.length; i++) {
-                    Expression exp = context.resolveLanguage("simple").createExpression(values[i]);
-                    exp.init(context);
-                    exps[i] = exp;
-                }
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                Set<Object> answer = new LinkedHashSet<>();
-                for (Expression exp : exps) {
-                    Object o = exp.evaluate(exchange, Object.class);
-                    // this may be an object that we can iterate
-                    Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
-                    for (Object i : it) {
-                        answer.add(i);
-                    }
-                }
-                return answer;
-            }
-
-            @Override
-            public String toString() {
-                return "distinct(" + Arrays.toString(values) + ")";
-            }
-        };
-    }
-
-    /**
-     * An expression that returns the elements in reverse order
-     */
-    public static Expression reverseExpression(String[] values) {
-        return new ExpressionAdapter() {
-
-            private final Expression[] exps = new Expression[values != null ? values.length : 0];
-
-            @Override
-            public void init(CamelContext context) {
-                for (int i = 0; values != null && i < values.length; i++) {
-                    Expression exp = context.resolveLanguage("simple").createExpression(values[i]);
-                    exp.init(context);
-                    exps[i] = exp;
-                }
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                List<Object> answer = new ArrayList<>();
-                for (Expression exp : exps) {
-                    Object o = exp.evaluate(exchange, Object.class);
-                    // this may be an object that we can iterate
-                    Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
-                    for (Object i : it) {
-                        answer.add(i);
-                    }
-                }
-                Collections.reverse(answer);
-                return answer;
-            }
-
-            @Override
-            public String toString() {
-                return "reverse(" + Arrays.toString(values) + ")";
-            }
-        };
-    }
-
-    /**
-     * An expression that returns the elements in random order
-     */
-    public static Expression shuffleExpression(String[] values) {
-        return new ExpressionAdapter() {
-
-            private final Expression[] exps = new Expression[values != null ? values.length : 0];
-
-            @Override
-            public void init(CamelContext context) {
-                for (int i = 0; values != null && i < values.length; i++) {
-                    Expression exp = context.resolveLanguage("simple").createExpression(values[i]);
-                    exp.init(context);
-                    exps[i] = exp;
-                }
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                List<Object> answer = new ArrayList<>();
-                for (Expression exp : exps) {
-                    Object o = exp.evaluate(exchange, Object.class);
-                    // this may be an object that we can iterate
-                    Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
-                    for (Object i : it) {
-                        answer.add(i);
-                    }
-                }
-                Collections.shuffle(answer);
-                return answer;
-            }
-
-            @Override
-            public String toString() {
-                return "reverse(" + Arrays.toString(values) + ")";
-            }
-        };
-    }
-
-    /**
-     * An expression that converts the expressions to number and sum the values
-     */
-    public static Expression sumExpression(String[] numbers) {
-        return new ExpressionAdapter() {
-
-            private final Expression[] exps = new Expression[numbers != null ? numbers.length : 0];
-
-            @Override
-            public void init(CamelContext context) {
-                for (int i = 0; numbers != null && i < numbers.length; i++) {
-                    Expression exp = context.resolveLanguage("simple").createExpression(numbers[i]);
-                    exp.init(context);
-                    exps[i] = exp;
-                }
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                Long answer = null;
-                for (Expression exp : exps) {
-                    Object o = exp.evaluate(exchange, Object.class);
-                    // this may be an object that we can iterate
-                    Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
-                    for (Object i : it) {
-                        Long val = exchange.getContext().getTypeConverter().tryConvertTo(Long.class, exchange, i);
-                        if (val != null) {
-                            if (answer == null) {
-                                answer = 0L;
-                            }
-                            answer += val;
-                        }
-                    }
-                }
-                return answer;
-            }
-
-            @Override
-            public String toString() {
-                return "sum(" + Arrays.toString(numbers) + ")";
-            }
-        };
-    }
-
-    /**
-     * An expression that converts the expressions to number and returns the maximum number
-     */
-    public static Expression maxExpression(String[] numbers) {
-        return new ExpressionAdapter() {
-
-            private final Expression[] exps = new Expression[numbers != null ? numbers.length : 0];
-
-            @Override
-            public void init(CamelContext context) {
-                for (int i = 0; numbers != null && i < numbers.length; i++) {
-                    Expression exp = context.resolveLanguage("simple").createExpression(numbers[i]);
-                    exp.init(context);
-                    exps[i] = exp;
-                }
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                Long answer = null;
-                for (Expression exp : exps) {
-                    Object o = exp.evaluate(exchange, Object.class);
-                    // this may be an object that we can iterate
-                    Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
-                    for (Object i : it) {
-                        Long val = exchange.getContext().getTypeConverter().tryConvertTo(Long.class, exchange, i);
-                        if (val != null) {
-                            if (answer == null) {
-                                answer = val;
-                            }
-                            answer = Math.max(answer, val);
-                        }
-                    }
-                }
-                return answer;
-            }
-
-            @Override
-            public String toString() {
-                return "max(" + Arrays.toString(numbers) + ")";
-            }
-        };
-    }
-
-    /**
-     * An expression that converts the expressions to number and returns the minimum number
-     */
-    public static Expression minExpression(String[] numbers) {
-        return new ExpressionAdapter() {
-
-            private final Expression[] exps = new Expression[numbers != null ? numbers.length : 0];
-
-            @Override
-            public void init(CamelContext context) {
-                for (int i = 0; numbers != null && i < numbers.length; i++) {
-                    Expression exp = context.resolveLanguage("simple").createExpression(numbers[i]);
-                    exp.init(context);
-                    exps[i] = exp;
-                }
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                Long answer = null;
-                for (Expression exp : exps) {
-                    Object o = exp.evaluate(exchange, Object.class);
-                    // this may be an object that we can iterate
-                    Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
-                    for (Object i : it) {
-                        Long val = exchange.getContext().getTypeConverter().tryConvertTo(Long.class, exchange, i);
-                        if (val != null) {
-                            if (answer == null) {
-                                answer = val;
-                            }
-                            answer = Math.min(answer, val);
-                        }
-                    }
-                }
-                return answer;
-            }
-
-            @Override
-            public String toString() {
-                return "min(" + Arrays.toString(numbers) + ")";
-            }
-        };
-    }
-
-    /**
-     * An expression that converts the expressions to number and returns the average number
-     */
-    public static Expression averageExpression(String[] numbers) {
-        return new ExpressionAdapter() {
-
-            private final Expression[] exps = new Expression[numbers != null ? numbers.length : 0];
-
-            @Override
-            public void init(CamelContext context) {
-                for (int i = 0; numbers != null && i < numbers.length; i++) {
-                    Expression exp = context.resolveLanguage("simple").createExpression(numbers[i]);
-                    exp.init(context);
-                    exps[i] = exp;
-                }
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                Long answer = null;
-                int counter = 0;
-                for (Expression exp : exps) {
-                    Object o = exp.evaluate(exchange, Object.class);
-                    // this may be an object that we can iterate
-                    Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
-                    for (Object i : it) {
-                        Long val = exchange.getContext().getTypeConverter().tryConvertTo(Long.class, exchange, i);
-                        if (val != null) {
-                            if (answer == null) {
-                                answer = 0L;
-                            }
-                            answer += val;
-                            counter++;
-                        }
-                    }
-                }
-                return answer != null ? answer / counter : null;
-            }
-
-            @Override
-            public String toString() {
-                return "average(" + Arrays.toString(numbers) + ")";
-            }
-        };
-    }
-
-    /**
-     * An expression that creates an ArrayList
-     */
-    public static Expression listExpression(String[] values) {
-        return new ExpressionAdapter() {
-
-            private final Expression[] exps = new Expression[values != null ? values.length : 0];
-
-            @Override
-            public void init(CamelContext context) {
-                for (int i = 0; values != null && i < values.length; i++) {
-                    Expression exp = context.resolveLanguage("simple").createExpression(values[i]);
-                    exp.init(context);
-                    exps[i] = exp;
-                }
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                List<Object> answer = new ArrayList<>(values != null ? values.length : 0);
-                for (Expression exp : exps) {
-                    Object val = exp.evaluate(exchange, Object.class);
-                    answer.add(val);
-                }
-                return answer;
-            }
-
-            @Override
-            public String toString() {
-                return "list(" + Arrays.toString(values) + ")";
-            }
-        };
-    }
-
-    /**
-     * An expression that creates an LinkedHashMap
-     */
-    public static Expression mapExpression(String[] pairs) {
-        return new ExpressionAdapter() {
-
-            private final Expression[] keys = new Expression[pairs != null ? pairs.length / 2 : 0];
-            private final Expression[] values = new Expression[pairs != null ? pairs.length / 2 : 0];
-
-            @Override
-            public void init(CamelContext context) {
-                for (int i = 0, j = 0; pairs != null && i < pairs.length - 1; j++) {
-                    String key = pairs[i];
-                    String value = pairs[i + 1];
-                    Expression exp = context.resolveLanguage("simple").createExpression(key);
-                    exp.init(context);
-                    keys[j] = exp;
-                    exp = context.resolveLanguage("simple").createExpression(value);
-                    exp.init(context);
-                    values[j] = exp;
-                    i = i + 2;
-                }
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                Map<String, Object> answer = new LinkedHashMap<>(keys.length);
-                for (int i = 0; i < keys.length; i++) {
-                    String key = keys[i].evaluate(exchange, String.class);
-                    Object val = values[i].evaluate(exchange, Object.class);
-                    answer.put(key, val);
-                }
-                return answer;
-            }
-
-            @Override
-            public String toString() {
-                return "map(" + Arrays.toString(values) + ")";
-            }
-        };
-    }
-
-    /**
-     * Joins together the values from the expression
-     */
-    public static Expression joinExpression(final String expression, final String separator, final String prefix) {
-        return new ExpressionAdapter() {
-            private Expression exp;
-
-            @Override
-            public void init(CamelContext context) {
-                exp = context.resolveLanguage("simple").createExpression(expression);
-                exp.init(context);
-                exp = ExpressionBuilder.joinExpression(exp, separator, prefix);
-                exp.init(context);
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                return exp.evaluate(exchange, Object.class);
-            }
-
-            @Override
-            public String toString() {
-                if (prefix != null) {
-                    return "join(" + expression + "," + separator + "," + prefix + ")";
-                } else {
-                    return "join(" + expression + "," + separator + ")";
-                }
-            }
-        };
-    }
-
-    /**
-     * Split the String values from the expression using the given separator
-     */
-    public static Expression splitStringExpression(final String expression, final String separator) {
-        return new ExpressionAdapter() {
-            private Expression exp;
-
-            @Override
-            public void init(CamelContext context) {
-                exp = context.resolveLanguage("simple").createExpression(expression);
-                exp.init(context);
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                String text = exp.evaluate(exchange, String.class);
-                if (text == null) {
-                    return null;
-                }
-                return text.split(separator);
-            }
-
-            @Override
-            public String toString() {
-                return "split(" + expression + "," + separator + ")";
-            }
-        };
-    }
-
-    /**
-     * Sorts the expression
-     */
-    public static Expression sortExpression(final String expression, final boolean reverse) {
-        return new ExpressionAdapter() {
-            private Expression exp;
-
-            @Override
-            public void init(CamelContext context) {
-                exp = context.resolveLanguage("simple").createExpression(expression);
-                exp.init(context);
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                List answer = new ArrayList<>();
-                Object o = exp.evaluate(exchange, Object.class);
-                // this may be an object that we can iterate
-                Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
-                for (Object i : it) {
-                    answer.add(i);
-                }
-                Collections.sort(answer);
-                if (reverse) {
-                    Collections.reverse(answer);
-                }
-                return answer;
-            }
-
-            @Override
-            public String toString() {
-                return "sort(" + expression + ")";
-            }
-        };
-    }
-
-    /**
-     * For each value in the source expression then apply the function and return a list of responses from each function
-     */
-    public static Expression forEachExpression(final String source, final String function) {
-        return new ExpressionAdapter() {
-            private CamelContext context;
-            private Expression exp1;
-            private Expression exp2;
-
-            @Override
-            public void init(CamelContext context) {
-                this.context = context;
-                exp1 = context.resolveLanguage("simple").createExpression(source);
-                exp1.init(context);
-                exp2 = context.resolveLanguage("simple").createExpression(function);
-                exp2.init(context);
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                List<Object> answer = new ArrayList<>();
-                Object o = exp1.evaluate(exchange, Object.class);
-                Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
-                for (Object i : it) {
-                    // use a dummy exchange as the input is to be the message body
-                    Exchange dummy = ExchangeHelper.createCopy(exchange, true);
-                    dummy.getMessage().setBody(i);
-                    Object out = exp2.evaluate(dummy, Object.class);
-                    if (out != null) {
-                        answer.add(out);
-                    }
-                }
-                return answer;
-            }
-
-            @Override
-            public String toString() {
-                return "forEach(" + source + ", " + function + ")";
-            }
-        };
-    }
-
-    /**
-     * Adds the result of the function to the source list
-     */
-    public static Expression listAddExpression(final String source, final String function) {
-        return new ExpressionAdapter() {
-            private CamelContext context;
-            private Expression exp1;
-            private Expression exp2;
-
-            @Override
-            public void init(CamelContext context) {
-                this.context = context;
-                exp1 = context.resolveLanguage("simple").createExpression(source);
-                exp1.init(context);
-                exp2 = context.resolveLanguage("simple").createExpression(function);
-                exp2.init(context);
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                Collection<Object> col = exp1.evaluate(exchange, Collection.class);
-                if (col != null) {
-                    Object value = exp2.evaluate(exchange, Object.class);
-                    if (value != null) {
-                        col.add(value);
-                    }
-                }
-                return col;
-            }
-
-            @Override
-            public String toString() {
-                return "listAdd(" + source + ", " + function + ")";
-            }
-        };
-    }
-
-    /**
-     * Removes the result of the function from the source list
-     */
-    public static Expression listRemoveExpression(final String source, final String function) {
-        return new ExpressionAdapter() {
-            private CamelContext context;
-            private Expression exp1;
-            private Expression exp2;
-
-            @Override
-            public void init(CamelContext context) {
-                this.context = context;
-                exp1 = context.resolveLanguage("simple").createExpression(source);
-                exp1.init(context);
-                exp2 = context.resolveLanguage("simple").createExpression(function);
-                exp2.init(context);
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                List<Object> list = exp1.evaluate(exchange, List.class);
-                if (list != null) {
-                    Object value = exp2.evaluate(exchange, Object.class);
-                    if (value != null) {
-                        boolean removed = list.remove(value);
-                        if (!removed) {
-                            Integer pos;
-                            // special name to remove last
-                            if ("last".equals(value)) {
-                                pos = list.size() - 1;
-                            } else {
-                                // this may be an integer
-                                pos = context.getTypeConverter().tryConvertTo(int.class, exchange, value);
-                            }
-                            if (pos != null) {
-                                if (pos >= 0 && pos < list.size()) {
-                                    list.remove((int) pos);
-                                }
-                            }
-                        }
-                    }
-                }
-                return list;
-            }
-
-            @Override
-            public String toString() {
-                return "listRemove(" + source + ", " + function + ")";
-            }
-        };
-    }
-
-    /**
-     * Adds the result of the function to the source map
-     */
-    public static Expression mapAddExpression(final String source, final String key, final String function) {
-        return new ExpressionAdapter() {
-            private CamelContext context;
-            private Expression exp1;
-            private Expression exp2;
-
-            @Override
-            public void init(CamelContext context) {
-                this.context = context;
-                exp1 = context.resolveLanguage("simple").createExpression(source);
-                exp1.init(context);
-                exp2 = context.resolveLanguage("simple").createExpression(function);
-                exp2.init(context);
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                Map<String, Object> map = exp1.evaluate(exchange, Map.class);
-                if (map != null) {
-                    Object value = exp2.evaluate(exchange, Object.class);
-                    if (value != null) {
-                        map.put(key, value);
-                    }
-                }
-                return map;
-            }
-
-            @Override
-            public String toString() {
-                return "mapAdd(" + source + ", " + key + ", " + function + ")";
-            }
-        };
-    }
-
-    /**
-     * Removes the result of the function from the source map
-     */
-    public static Expression mapRemoveExpression(final String source, final String key) {
-        return new ExpressionAdapter() {
-            private CamelContext context;
-            private Expression exp1;
-
-            @Override
-            public void init(CamelContext context) {
-                this.context = context;
-                exp1 = context.resolveLanguage("simple").createExpression(source);
-                exp1.init(context);
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                Map<String, Object> map = exp1.evaluate(exchange, Map.class);
-                if (map != null) {
-                    map.remove(key);
-                }
-                return map;
-            }
-
-            @Override
-            public String toString() {
-                return "mapRemove(" + source + ", " + key + ")";
-            }
-        };
-    }
-
-    /**
-     * Filters the values from the source that matches the predicate function
-     */
-    public static Expression filterExpression(final String source, final String function) {
-        return new ExpressionAdapter() {
-            private CamelContext context;
-            private Expression exp1;
-            private Predicate exp2;
-
-            @Override
-            public void init(CamelContext context) {
-                this.context = context;
-                exp1 = context.resolveLanguage("simple").createExpression(source);
-                exp1.init(context);
-                exp2 = context.resolveLanguage("simple").createPredicate(function);
-                exp2.init(context);
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                List<Object> answer = new ArrayList<>();
-                Object o = exp1.evaluate(exchange, Object.class);
-                Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
-                for (Object i : it) {
-                    // use a dummy exchange as the input is to be the message body
-                    Exchange dummy = ExchangeHelper.createCopy(exchange, true);
-                    dummy.getMessage().setBody(i);
-                    if (exp2.matches(dummy)) {
-                        answer.add(i);
-                    }
-                }
-                return answer;
-            }
-
-            @Override
-            public String toString() {
-                return "filter(" + source + ", " + function + ")";
-            }
-        };
-    }
-
-    /**
-     * Sets the message header with the given expression value
-     */
-    public static Expression setHeaderExpression(final String name, final String type, final String expression) {
-        return new ExpressionAdapter() {
-            private ClassResolver classResolver;
-            private Expression exp;
-
-            @Override
-            public void init(CamelContext context) {
-                classResolver = context.getClassResolver();
-                exp = context.resolveLanguage("simple").createExpression(expression);
-                exp.init(context);
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                Class<?> clazz = Object.class;
-                if (type != null) {
-                    try {
-                        clazz = classResolver.resolveMandatoryClass(type);
-                    } catch (ClassNotFoundException e) {
-                        throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
-                    }
-                }
-                Object value = exp.evaluate(exchange, clazz);
-                if (value != null) {
-                    exchange.getMessage().setHeader(name, value);
-                } else {
-                    exchange.getMessage().removeHeader(name);
-                }
-                // does not return anything
-                return null;
-            }
-
-            @Override
-            public String toString() {
-                return "setHeader(" + name + "," + expression + ")";
-            }
-        };
-    }
-
-    /**
-     * Sets the variable with the given expression value
-     */
-    public static Expression setVariableExpression(final String name, final String type, final String expression) {
-        return new ExpressionAdapter() {
-            private ClassResolver classResolver;
-            private Expression exp;
-
-            @Override
-            public void init(CamelContext context) {
-                classResolver = context.getClassResolver();
-                exp = context.resolveLanguage("simple").createExpression(expression);
-                exp.init(context);
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                Class<?> clazz = Object.class;
-                if (type != null) {
-                    try {
-                        clazz = classResolver.resolveMandatoryClass(type);
-                    } catch (ClassNotFoundException e) {
-                        throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
-                    }
-                }
-                Object value = exp.evaluate(exchange, clazz);
-                if (value != null) {
-                    exchange.setVariable(name, value);
-                } else {
-                    exchange.removeVariable(name);
-                }
-                // does not return anything
-                return null;
-            }
-
-            @Override
-            public String toString() {
-                return "setVariable(" + name + "," + expression + ")";
-            }
-        };
-    }
-
-    /**
-     * Replaces string values from the expression
-     */
-    public static Expression replaceExpression(final String expression, final String from, final String to) {
-        return new ExpressionAdapter() {
-            private Expression exp;
-
-            @Override
-            public void init(CamelContext context) {
-                exp = context.resolveLanguage("simple").createExpression(expression);
-                exp.init(context);
-                exp = ExpressionBuilder.replaceAll(exp, from, to);
-                exp.init(context);
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                return exp.evaluate(exchange, Object.class);
-            }
-
-            @Override
-            public String toString() {
-                return "replace(" + expression + "," + from + "," + to + ")";
-            }
-        };
-    }
-
-    /**
-     * Substring string values from the expression
-     */
-    public static Expression substringExpression(final String expression, final String head, final String tail) {
-        return new ExpressionAdapter() {
-            private Expression exp;
-            private Expression exp1;
-            private Expression exp2;
-
-            @Override
-            public void init(CamelContext context) {
-                exp = context.resolveLanguage("simple").createExpression(expression);
-                exp.init(context);
-                exp1 = ExpressionBuilder.simpleExpression(head);
-                exp1.init(context);
-                exp2 = ExpressionBuilder.simpleExpression(tail);
-                exp2.init(context);
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                int num1 = exp1.evaluate(exchange, Integer.class);
-                int num2 = exp2.evaluate(exchange, Integer.class);
-                if (num1 < 0 && num2 == 0) {
-                    // if there is only one value and its negative then we want to clip from tail
-                    num2 = num1;
-                    num1 = 0;
-                }
-                num1 = Math.abs(num1);
-                num2 = Math.abs(num2);
-                return ExpressionBuilder.substring(exp, num1, num2).evaluate(exchange, Object.class);
-            }
-
-            @Override
-            public String toString() {
-                return "substring(" + expression + "," + head + "," + tail + ")";
-            }
-        };
-    }
-
-    /**
-     * Returns the substring from the given expression that comes before
-     */
-    public static Expression substringBeforeExpression(final String expression, final String before) {
-        return new ExpressionAdapter() {
-            private Expression exp;
-            private Expression expBefore;
-
-            @Override
-            public void init(CamelContext context) {
-                exp = context.resolveLanguage("simple").createExpression(expression);
-                exp.init(context);
-                expBefore = ExpressionBuilder.simpleExpression(before);
-                expBefore.init(context);
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                String body = exp.evaluate(exchange, String.class);
-                if (body == null) {
-                    return null;
-                }
-                String bef = expBefore.evaluate(exchange, String.class);
-                return StringHelper.before(body, bef);
-            }
-
-            @Override
-            public String toString() {
-                return "substringBefore(" + expression + "," + before + ")";
-            }
-        };
-    }
-
-    /**
-     * Returns the substring from the given expression that comes after
-     */
-    public static Expression substringAfterExpression(final String expression, final String after) {
-        return new ExpressionAdapter() {
-            private Expression exp;
-            private Expression expAfter;
-
-            @Override
-            public void init(CamelContext context) {
-                exp = context.resolveLanguage("simple").createExpression(expression);
-                exp.init(context);
-                expAfter = ExpressionBuilder.simpleExpression(after);
-                expAfter.init(context);
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                String body = exp.evaluate(exchange, String.class);
-                if (body == null) {
-                    return null;
-                }
-                String aft = expAfter.evaluate(exchange, String.class);
-                return StringHelper.after(body, aft);
-            }
-
-            @Override
-            public String toString() {
-                return "substringAfter(" + expression + "," + after + ")";
-            }
-        };
-    }
-
-    /**
-     * Returns the substring from the given expression that are between after and before
-     */
-    public static Expression substringBetweenExpression(
-            final String expression, final String after,
-            final String before) {
-        return new ExpressionAdapter() {
-            private Expression exp;
-            private Expression expAfter;
-            private Expression expBefore;
-
-            @Override
-            public void init(CamelContext context) {
-                exp = context.resolveLanguage("simple").createExpression(expression);
-                exp.init(context);
-                expAfter = ExpressionBuilder.simpleExpression(after);
-                expAfter.init(context);
-                expBefore = ExpressionBuilder.simpleExpression(before);
-                expBefore.init(context);
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                String body = exp.evaluate(exchange, String.class);
-                if (body == null) {
-                    return null;
-                }
-                String aft = expAfter.evaluate(exchange, String.class);
-                String bef = expBefore.evaluate(exchange, String.class);
-                return StringHelper.between(body, aft, bef);
-            }
-
-            @Override
-            public String toString() {
-                return "substringBetween(" + expression + "," + after + "," + before + ")";
-            }
-        };
-    }
-
-    /**
-     * Whether the expression matches the pattern
-     */
-    public static Expression containsExpression(final String expression, final String pattern) {
-        return new ExpressionAdapter() {
-            private Expression right;
-            private Expression left;
-
-            @Override
-            public void init(CamelContext context) {
-                left = context.resolveLanguage("simple").createExpression(expression);
-                left.init(context);
-                right = context.resolveLanguage("simple").createExpression(pattern);
-                right.init(context);
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                return PredicateBuilder.containsIgnoreCase(left, right).matches(exchange);
-            }
-
-            @Override
-            public String toString() {
-                return "contains(" + expression + "," + pattern + ")";
-            }
-        };
-    }
-
-    /**
-     * Hashes the value using the given algorithm
-     */
-    public static Expression hashExpression(final String expression, final String algorithm) {
-        return new ExpressionAdapter() {
-            private Expression exp;
-
-            @Override
-            public void init(CamelContext context) {
-                exp = context.resolveLanguage("simple").createExpression(expression);
-                exp.init(context);
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                InputStream is = exp.evaluate(exchange, InputStream.class);
-                if (is != null) {
-                    try {
-                        // calculate the hash in chunks in case the payload is big
-                        MessageDigest digest = MessageDigest.getInstance(algorithm);
-                        DigestInputStream dis = new DigestInputStream(is, digest);
-                        IOHelper.copy(dis, new OutputStream() {
-                            @Override
-                            public void write(int b) throws IOException {
-                                // ignore
-                            }
-                        });
-                        return StringHelper.bytesToHex(digest.digest());
-                    } catch (Exception e) {
-                        throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
-                    } finally {
-                        // reset cached streams so they can be read again
-                        MessageHelper.resetStreamCache(exchange.getMessage());
-                    }
-                }
-                return null;
-            }
-
-            @Override
-            public String toString() {
-                return "hash(" + expression + "," + algorithm + ")";
-            }
-        };
-    }
-
-    /**
-     * Returns a random number between min and max (exclusive)
-     */
-    public static Expression randomExpression(final String min, final String max) {
-        return new ExpressionAdapter() {
-            private Expression exp1;
-            private Expression exp2;
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                int num1 = exp1.evaluate(exchange, Integer.class);
-                int num2 = exp2.evaluate(exchange, Integer.class);
-                Random random = new Random(); // NOSONAR
-                return random.nextInt(num2 - num1) + num1;
-            }
-
-            @Override
-            public void init(CamelContext context) {
-                exp1 = ExpressionBuilder.simpleExpression(min);
-                exp1.init(context);
-                exp2 = ExpressionBuilder.simpleExpression(max);
-                exp2.init(context);
-            }
-
-            @Override
-            public String toString() {
-                return "random(" + min + "," + max + ")";
-            }
-        };
-    }
-
-    /**
-     * Returns a range of number between min and max (exclusive)
-     */
-    public static Expression rangeExpression(final String min, final String max) {
-        return new ExpressionAdapter() {
-            private Expression exp1;
-            private Expression exp2;
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                int num1 = exp1.evaluate(exchange, Integer.class);
-                int num2 = exp2.evaluate(exchange, Integer.class);
-                if (num1 >= 0 && num1 <= num2 && num1 != num2) {
-                    List<Integer> answer = new ArrayList<>();
-                    for (int i = num1; i < num2; i++) {
-                        answer.add(i);
-                    }
-                    return answer;
-                }
-                return null;
-            }
-
-            @Override
-            public void init(CamelContext context) {
-                exp1 = ExpressionBuilder.simpleExpression(min);
-                exp1.init(context);
-                exp2 = ExpressionBuilder.simpleExpression(max);
-                exp2.init(context);
-            }
-
-            @Override
-            public String toString() {
-                return "range(" + min + "," + max + ")";
-            }
-        };
-    }
-
-    /**
-     * Returns a random number between 0 and max (exclusive)
-     */
-    public static Expression randomExpression(final int max) {
-        return randomExpression(0, max);
-    }
-
-    /**
-     * Returns a random number between min and max (exclusive)
-     */
-    public static Expression randomExpression(final int min, final int max) {
-        return new ExpressionAdapter() {
-            @Override
-            public Object evaluate(Exchange exchange) {
-                Random random = new Random(); // NOSONAR
-                return random.nextInt(max - min) + min;
-            }
-
-            @Override
-            public String toString() {
-                return "random(" + min + "," + max + ")";
-            }
-        };
-    }
-
-    /**
-     * Returns a new empty object of the given type
-     */
-    public static Expression newEmptyExpression(final String type) {
-
-        return new ExpressionAdapter() {
-            @Override
-            public Object evaluate(Exchange exchange) {
-                if ("map".equalsIgnoreCase(type)) {
-                    return new LinkedHashMap<>();
-                } else if ("string".equalsIgnoreCase(type)) {
-                    return "";
-                } else if ("list".equalsIgnoreCase(type)) {
-                    return new ArrayList<>();
-                } else if ("set".equalsIgnoreCase(type)) {
-                    return new LinkedHashSet<>();
-                }
-                throw new IllegalArgumentException("function empty(%s) has unknown type".formatted(type));
-            }
-
-            @Override
-            public String toString() {
-                return "empty(%s)".formatted(type);
-            }
-        };
-    }
-
-    /**
-     * Returns an uuid string based on the given generator (default, classic, short, simple)
-     */
-    public static Expression uuidExpression(final String generator) {
-        return new ExpressionAdapter() {
-
-            UuidGenerator uuid;
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                return uuid.generateUuid();
-            }
-
-            @Override
-            public void init(CamelContext context) {
-                if ("classic".equalsIgnoreCase(generator)) {
-                    uuid = new ClassicUuidGenerator();
-                } else if ("short".equals(generator)) {
-                    uuid = new ShortUuidGenerator();
-                } else if ("simple".equals(generator)) {
-                    uuid = new SimpleUuidGenerator();
-                } else if ("random".equals(generator)) {
-                    uuid = new RandomUuidGenerator();
-                } else if (generator == null || "default".equals(generator)) {
-                    uuid = new DefaultUuidGenerator();
-                } else {
-                    // lookup custom generator
-                    uuid = CamelContextHelper.mandatoryLookup(context, generator, UuidGenerator.class);
-                }
-            }
-
-            @Override
-            public String toString() {
-                if (generator != null) {
-                    return "uuid(" + generator + ")";
-                } else {
-                    return "uuid";
-                }
-            }
-        };
-    }
-
-    public static Expression fileNameExpression() {
-        return new ExpressionAdapter() {
-            @Override
-            public Object evaluate(Exchange exchange) {
-                return exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
-            }
-
-            @Override
-            public String toString() {
-                return "file:name";
-            }
-        };
-    }
-
-    public static Expression fileOnlyNameExpression() {
-        return new ExpressionAdapter() {
-            @Override
-            public Object evaluate(Exchange exchange) {
-                String answer = exchange.getIn().getHeader(Exchange.FILE_NAME_ONLY, String.class);
-                if (answer == null) {
-                    answer = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
-                    answer = FileUtil.stripPath(answer);
-                }
-                return answer;
-            }
-
-            @Override
-            public String toString() {
-                return "file:onlyname";
-            }
-        };
-    }
-
-    public static Expression fileNameNoExtensionExpression() {
-        return new ExpressionAdapter() {
-            @Override
-            public Object evaluate(Exchange exchange) {
-                String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
-                return FileUtil.stripExt(name);
-            }
-
-            @Override
-            public String toString() {
-                return "file:name.noext";
-            }
-        };
-    }
-
-    public static Expression fileNameNoExtensionSingleExpression() {
-        return new ExpressionAdapter() {
-            @Override
-            public Object evaluate(Exchange exchange) {
-                String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
-                return FileUtil.stripExt(name, true);
-            }
-
-            @Override
-            public String toString() {
-                return "file:name.noext.single";
-            }
-        };
-    }
-
-    public static Expression fileOnlyNameNoExtensionExpression() {
-        return new ExpressionAdapter() {
-            @Override
-            public Object evaluate(Exchange exchange) {
-                String name = fileOnlyNameExpression().evaluate(exchange, String.class);
-                return FileUtil.stripExt(name);
-            }
-
-            @Override
-            public String toString() {
-                return "file:onlyname.noext";
-            }
-        };
-    }
-
-    public static Expression fileOnlyNameNoExtensionSingleExpression() {
-        return new ExpressionAdapter() {
-            @Override
-            public Object evaluate(Exchange exchange) {
-                String name = fileOnlyNameExpression().evaluate(exchange, String.class);
-                return FileUtil.stripExt(name, true);
-            }
-
-            @Override
-            public String toString() {
-                return "file:onlyname.noext.single";
-            }
-        };
-    }
-
-    public static Expression fileExtensionExpression() {
-        return new ExpressionAdapter() {
-            @Override
-            public Object evaluate(Exchange exchange) {
-                String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
-                return FileUtil.onlyExt(name);
-            }
-
-            @Override
-            public String toString() {
-                return "file:ext";
-            }
-        };
-    }
-
-    public static Expression fileExtensionSingleExpression() {
-        return new ExpressionAdapter() {
-            @Override
-            public Object evaluate(Exchange exchange) {
-                String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
-                return FileUtil.onlyExt(name, true);
-            }
-
-            @Override
-            public String toString() {
-                return "file:ext.single";
-            }
-        };
-    }
-
-    public static Expression fileParentExpression() {
-        return new ExpressionAdapter() {
-            @Override
-            public Object evaluate(Exchange exchange) {
-                return exchange.getIn().getHeader("CamelFileParent", String.class);
-            }
-
-            @Override
-            public String toString() {
-                return "file:parent";
-            }
-        };
-    }
-
-    public static Expression filePathExpression() {
-        return new ExpressionAdapter() {
-            @Override
-            public Object evaluate(Exchange exchange) {
-                return exchange.getIn().getHeader("CamelFilePath", String.class);
-            }
-
-            @Override
-            public String toString() {
-                return "file:path";
-            }
-        };
-    }
-
-    public static Expression fileAbsolutePathExpression() {
-        return new ExpressionAdapter() {
-            @Override
-            public Object evaluate(Exchange exchange) {
-                return exchange.getIn().getHeader("CamelFileAbsolutePath", String.class);
-            }
-
-            @Override
-            public String toString() {
-                return "file:absolute.path";
-            }
-        };
-    }
-
-    public static Expression fileAbsoluteExpression() {
-        return new ExpressionAdapter() {
-            @Override
-            public Object evaluate(Exchange exchange) {
-                return exchange.getIn().getHeader("CamelFileAbsolute", Boolean.class);
-            }
-
-            @Override
-            public String toString() {
-                return "file:absolute";
-            }
-        };
-    }
-
-    public static Expression fileSizeExpression() {
-        return new ExpressionAdapter() {
-            @Override
-            public Object evaluate(Exchange exchange) {
-                return exchange.getIn().getHeader(Exchange.FILE_LENGTH, Long.class);
-            }
-
-            @Override
-            public String toString() {
-                return "file:length";
-            }
-        };
-    }
-
-    public static Expression fileLastModifiedExpression() {
-        return new ExpressionAdapter() {
-            @Override
-            public Object evaluate(Exchange exchange) {
-                return exchange.getIn().getHeader(Exchange.FILE_LAST_MODIFIED, Long.class);
-            }
-
-            @Override
-            public String toString() {
-                return "file:modified";
-            }
-        };
-    }
-
-    public static Expression dateExpression(final String command) {
-        return dateExpression(command, null, null);
-    }
-
-    public static Expression dateExpression(final String command, final String pattern) {
-        return dateExpression(command, null, pattern);
-    }
-
-    public static Expression dateExpression(
-            final String commandWithOffsets, final String timezone,
-            final String pattern) {
-        final String command = commandWithOffsets.split("[+-]", 2)[0].trim();
-        final List<Long> offsets = LanguageHelper.captureOffsets(commandWithOffsets, OFFSET_PATTERN);
-
-        return new ExpressionAdapter() {
-            @Override
-            public Object evaluate(Exchange exchange) {
-                Date date = evalDate(exchange, command);
-                Object answer = LanguageHelper.applyDateOffsets(date, offsets, pattern, timezone);
-                if ("millis".equals(command) && answer instanceof Date d) {
-                    answer = d.getTime();
-                }
-                return answer;
-            }
-
-            @Override
-            public String toString() {
-                if (timezone != null && pattern != null) {
-                    return "date(" + commandWithOffsets + ":" + timezone + ":" + pattern + ")";
-                } else if (pattern != null) {
-                    return "date(" + commandWithOffsets + ":" + pattern + ")";
-                } else {
-                    return "date(" + commandWithOffsets + ")";
-                }
-            }
-        };
-    }
-
-    private static Date evalDate(Exchange exchange, String command) {
-        Date date;
-        if ("now".equals(command) || "millis".equals(command)) {
-            date = new Date();
-        } else if ("exchangeCreated".equals(command)) {
-            date = LanguageHelper.dateFromExchangeCreated(exchange);
-        } else if (command.startsWith("header.")) {
-            date = LanguageHelper.dateFromHeader(exchange, command, (e, o) -> tryConvertingAsDate(e, o, command));
-        } else if (command.startsWith("variable.")) {
-            date = LanguageHelper.dateFromVariable(exchange, command, (e, o) -> tryConvertingAsDate(e, o, command));
-        } else if (command.startsWith("exchangeProperty.")) {
-            date = LanguageHelper.dateFromExchangeProperty(exchange, command, (e, o) -> tryConvertingAsDate(e, o, command));
-        } else if ("file".equals(command)) {
-            date = LanguageHelper.dateFromFileLastModified(exchange, command);
-        } else {
-            throw new IllegalArgumentException("Command not supported for dateExpression: " + command);
-        }
-        return date;
-    }
-
-    private static Date tryConvertingAsDate(Exchange exchange, Object obj, String command) {
-        final Date date = exchange.getContext().getTypeConverter().tryConvertTo(Date.class, exchange, obj);
-        if (date == null) {
-            throw new IllegalArgumentException("Cannot find Date/long object at command: " + command);
-        }
-
-        return date;
-    }
-
-    public static Expression skipIteratorExpression(final Expression expression, final int skip) {
-        return new ExpressionAdapter() {
-            @Override
-            public Object evaluate(Exchange exchange) {
-                // evaluate expression as iterator
-                Iterator<?> it = expression.evaluate(exchange, Iterator.class);
-                ObjectHelper.notNull(it,
-                        "expression: " + expression + " evaluated on " + exchange + " must return an java.util.Iterator");
-                return new SkipIterator(it, skip);
-            }
-
-            @Override
-            public String toString() {
-                return "skip " + expression + " " + skip + " times";
-            }
-        };
-    }
-
-    /**
-     * Returns the expression for the {@code null} value
-     */
-    public static Expression nullExpression() {
-        return new ExpressionAdapter() {
-            @Override
-            public Object evaluate(Exchange exchange) {
-                return null;
-            }
-
-            @Override
-            public String toString() {
-                return "null";
-            }
-        };
-    }
-
-    /**
-     * Returns the expression for the exchanges inbound message body converted to the given type and invoking methods on
-     * the converted body defined in a simple OGNL notation
-     */
-    public static Expression mandatoryBodyOgnlExpression(final String name, final String ognl) {
-        return new ExpressionAdapter() {
-            private ClassResolver classResolver;
-            private Expression exp;
-            private Language bean;
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                String text = exp.evaluate(exchange, String.class);
-                Class<?> type;
-                try {
-                    type = classResolver.resolveMandatoryClass(text);
-                } catch (ClassNotFoundException e) {
-                    throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
-                }
-                Object body;
-                try {
-                    body = exchange.getIn().getMandatoryBody(type);
-                } catch (InvalidPayloadException e) {
-                    throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
-                }
-                Expression ognlExp = bean.createExpression(null, new Object[] { null, body, ognl });
-                ognlExp.init(exchange.getContext());
-                return ognlExp.evaluate(exchange, Object.class);
-            }
-
-            @Override
-            public void init(CamelContext context) {
-                classResolver = context.getClassResolver();
-                exp = ExpressionBuilder.simpleExpression(name);
-                exp.init(context);
-                bean = context.resolveLanguage("bean");
-            }
-
-            @Override
-            public String toString() {
-                return "mandatoryBodyAs[" + name + "](" + ognl + ")";
-            }
-        };
-    }
-
-    /**
-     * Returns the expression for the exchanges inbound message body converted to the given type
-     */
-    public static Expression mandatoryBodyExpression(final String name) {
-        return new ExpressionAdapter() {
-            private ClassResolver classResolver;
-            private Expression exp;
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                String text = exp.evaluate(exchange, String.class);
-                Class<?> type;
-                try {
-                    type = classResolver.resolveMandatoryClass(text);
-                } catch (ClassNotFoundException e) {
-                    throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
-                }
-                try {
-                    return exchange.getIn().getMandatoryBody(type);
-                } catch (InvalidPayloadException e) {
-                    throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
-                }
-            }
-
-            @Override
-            public void init(CamelContext context) {
-                classResolver = context.getClassResolver();
-                exp = ExpressionBuilder.simpleExpression(name);
-                exp.init(context);
-            }
-
-            @Override
-            public String toString() {
-                return "mandatoryBodyAs[" + name + "]";
-            }
-        };
-    }
-
-    /**
-     * Returns the expression for the message converted to the given type and invoking methods on the converted message
-     * defined in a simple OGNL notation
-     */
-    public static Expression messageOgnlExpression(final String name, final String ognl) {
-        return new ExpressionAdapter() {
-            private ClassResolver classResolver;
-            private Expression exp;
-            private Language bean;
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                String text = exp.evaluate(exchange, String.class);
-                Class<?> type;
-                try {
-                    type = classResolver.resolveMandatoryClass(text);
-                } catch (ClassNotFoundException e) {
-                    throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
-                }
-                Object msg = exchange.getMessage(type);
-                if (msg != null) {
-                    // ognl is able to evaluate method name if it contains nested functions
-                    // so we should not eager evaluate ognl as a string
-                    Expression ognlExp = bean.createExpression(null, new Object[] { null, msg, ognl });
-                    ognlExp.init(exchange.getContext());
-                    return ognlExp.evaluate(exchange, Object.class);
-                } else {
-                    return null;
-                }
-            }
-
-            @Override
-            public void init(CamelContext context) {
-                classResolver = context.getClassResolver();
-                exp = ExpressionBuilder.simpleExpression(name);
-                exp.init(context);
-                bean = context.resolveLanguage("bean");
-            }
-
-            @Override
-            public String toString() {
-                return "messageOgnlAs[" + name + "](" + ognl + ")";
-            }
-        };
-    }
-
-    /**
-     * Returns the expression for the exchanges inbound message body converted to the given type and invoking methods on
-     * the converted body defined in a simple OGNL notation
-     */
-    public static Expression bodyOgnlExpression(final String name, final String ognl) {
-        return new ExpressionAdapter() {
-            private ClassResolver classResolver;
-            private Expression exp;
-            private Language bean;
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                String text = exp.evaluate(exchange, String.class);
-                Class<?> type;
-                try {
-                    type = classResolver.resolveMandatoryClass(text);
-                } catch (ClassNotFoundException e) {
-                    throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
-                }
-                Object body = exchange.getIn().getBody(type);
-                if (body != null) {
-                    // ognl is able to evaluate method name if it contains nested functions
-                    // so we should not eager evaluate ognl as a string
-                    Expression ognlExp = bean.createExpression(null, new Object[] { null, body, ognl });
-                    ognlExp.init(exchange.getContext());
-                    return ognlExp.evaluate(exchange, Object.class);
-                } else {
-                    return null;
-                }
-            }
-
-            @Override
-            public void init(CamelContext context) {
-                classResolver = context.getClassResolver();
-                exp = ExpressionBuilder.simpleExpression(name);
-                exp.init(context);
-                bean = context.resolveLanguage("bean");
-            }
-
-            @Override
-            public String toString() {
-                return "bodyOgnlAs[" + name + "](" + ognl + ")";
-            }
-        };
-    }
-
-    /**
-     * Returns the expression for the exchange invoking methods defined in a simple OGNL notation
-     *
-     * @param ognl methods to invoke on the exchange in a simple OGNL syntax
-     */
-    public static Expression exchangeOgnlExpression(final String ognl) {
-        return new ExpressionAdapter() {
-            private Language bean;
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                // ognl is able to evaluate method name if it contains nested functions
-                // so we should not eager evaluate ognl as a string
-                Expression ognlExp = bean.createExpression(null, new Object[] { null, exchange, ognl });
-                ognlExp.init(exchange.getContext());
-                return ognlExp.evaluate(exchange, Object.class);
-            }
-
-            @Override
-            public void init(CamelContext context) {
-                bean = context.resolveLanguage("bean");
-            }
-
-            @Override
-            public String toString() {
-                return "exchangeOgnl(" + ognl + ")";
-            }
-        };
-    }
-
-    /**
-     * Returns the expression for the exchanges camelContext invoking methods defined in a simple OGNL notation
-     *
-     * @param ognl methods to invoke on the context in a simple OGNL syntax
-     */
-    public static Expression camelContextOgnlExpression(final String ognl) {
-        return new ExpressionAdapter() {
-            private Expression exp;
-
-            @Override
-            public void init(CamelContext context) {
-                exp = ExpressionBuilder.beanExpression(context, ognl);
-                exp.init(context);
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                // ognl is able to evaluate method name if it contains nested functions
-                // so we should not eager evaluate ognl as a string
-                return exp.evaluate(exchange, Object.class);
-            }
-
-            @Override
-            public String toString() {
-                return "camelContextOgnl(" + ognl + ")";
-            }
-        };
-    }
-
-    /**
-     * Returns the expression for the exchanges inbound message body invoking methods defined in a simple OGNL notation
-     *
-     * @param ognl methods to invoke on the body in a simple OGNL syntax
-     */
-    public static Expression bodyOgnlExpression(final String ognl) {
-        return new ExpressionAdapter() {
-            private Language bean;
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                Object body = exchange.getIn().getBody();
-                if (body == null) {
-                    return null;
-                }
-                Expression ognlExp = bean.createExpression(null, new Object[] { null, body, ognl });
-                ognlExp.init(exchange.getContext());
-                return ognlExp.evaluate(exchange, Object.class);
-            }
-
-            @Override
-            public void init(CamelContext context) {
-                bean = context.resolveLanguage("bean");
-            }
-
-            @Override
-            public String toString() {
-                return "bodyOgnl(" + ognl + ")";
-            }
-        };
-    }
-
-    /**
-     * Returns an expression that caches the evaluation of another expression and returns the cached value, to avoid
-     * re-evaluating the expression.
-     *
-     * @param  expression the target expression to cache
-     * @return            the cached value
-     */
-    public static Expression cacheExpression(final Expression expression) {
-        return new ExpressionAdapter() {
-            private final AtomicReference<Object> cache = new AtomicReference<>();
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                Object answer = cache.get();
-                if (answer == null) {
-                    answer = expression.evaluate(exchange, Object.class);
-                    cache.set(answer);
-                }
-                return answer;
-            }
-
-            @Override
-            public String toString() {
-                return expression.toString();
-            }
-        };
-    }
-
-    /**
-     * Loads the given resource from classpath
-     */
-    public static Expression loadExpression(final String expression) {
-        return new ExpressionAdapter() {
-
-            private Expression exp;
-
-            @Override
-            public void init(CamelContext context) {
-                exp = context.resolveLanguage("simple").createExpression(expression);
-                exp.init(context);
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                String name = exp.evaluate(exchange, String.class);
-                if (name != null) {
-                    name = name.trim();
-                    String part = StringHelper.after(name, ":", name);
-                    boolean optional = part.endsWith("?optional=true");
-                    if (optional) {
-                        part = part.substring(0, part.length() - 14);
-                    }
-                    if (part.endsWith("?optional=false")) {
-                        part = part.substring(0, part.length() - 15);
-                    }
-                    try {
-                        InputStream is;
-                        if (!optional) {
-                            is = ResourceHelper.resolveMandatoryResourceAsInputStream(exchange.getContext(), part);
-                        } else {
-                            is = ResourceHelper.resolveResourceAsInputStream(exchange.getContext(), part);
-                        }
-                        if (is == null) {
-                            return null;
-                        }
-                        return IOHelper.loadText(is, false);
-                    } catch (Exception e) {
-                        throw RuntimeCamelException.wrapRuntimeException(e);
-                    }
-                }
-                return null;
-            }
-
-            @Override
-            public String toString() {
-                return "load(" + expression + ")";
-            }
-        };
-    }
-
-    /**
-     * Returns an expression for a type value
-     *
-     * @param  name the type name
-     * @return      an expression object which will return the type value
-     */
-    public static Expression typeExpression(final String name) {
-        return new ExpressionAdapter() {
-            private ClassResolver classResolver;
-            private Expression exp;
-
-            @Override
-            public void init(CamelContext context) {
-                classResolver = context.getClassResolver();
-                exp = ExpressionBuilder.simpleExpression(name);
-                exp.init(context);
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                // it may refer to a class type
-                String text = exp.evaluate(exchange, String.class);
-                Class<?> type = classResolver.resolveClass(text);
-                if (type != null) {
-                    return type;
-                }
-
-                int pos = text.lastIndexOf('.');
-                if (pos > 0) {
-                    String before = text.substring(0, pos);
-                    String after = text.substring(pos + 1);
-                    type = classResolver.resolveClass(before);
-                    if (type != null) {
-                        // special for enum constants
-                        if (type.isEnum()) {
-                            Class<Enum<?>> enumClass = (Class<Enum<?>>) type;
-                            for (Enum<?> enumValue : enumClass.getEnumConstants()) {
-                                if (enumValue.name().equalsIgnoreCase(after)) {
-                                    return type.cast(enumValue);
-                                }
-                            }
-                            throw CamelExecutionException.wrapCamelExecutionException(exchange,
-                                    new ClassNotFoundException("Cannot find enum: " + after + " on type: " + type));
-                        } else {
-                            // we assume it is a field constant
-                            Object answer = ObjectHelper.lookupConstantFieldValue(type, after);
-                            if (answer != null) {
-                                return answer;
-                            }
-                        }
-                    }
-                }
-
-                throw CamelExecutionException.wrapCamelExecutionException(exchange,
-                        new ClassNotFoundException("Cannot find type: " + text));
-            }
-
-            @Override
-            public String toString() {
-                return "type:" + name;
-            }
-        };
-    }
-
-    /**
-     * Returns an expression for the property value of exchange with the given name invoking methods defined in a simple
-     * OGNL notation
-     *
-     * @param ognl methods to invoke on the property in a simple OGNL syntax
-     */
-    public static Expression propertyOgnlExpression(final String ognl) {
-        return new KeyedOgnlExpressionAdapter(
-                ognl, "propertyOgnl(" + ognl + ")",
-                (exchange, exp) -> {
-                    String text = exp.evaluate(exchange, String.class);
-                    return exchange.getProperty(text);
-                });
-    }
-
-    /**
-     * 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
-     */
-    public static Expression exchangeExceptionOgnlExpression(final String ognl) {
-        return new ExpressionAdapter() {
-            private Language bean;
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                Object exception = exchange.getException();
-                if (exception == null) {
-                    exception = exchange.getProperty(ExchangePropertyKey.EXCEPTION_CAUGHT, Exception.class);
-                }
-
-                if (exception == null) {
-                    return null;
-                }
-
-                // ognl is able to evaluate method name if it contains nested functions
-                // so we should not eager evaluate ognl as a string
-                Expression ognlExp = bean.createExpression(null, new Object[] { null, exception, ognl });
-                ognlExp.init(exchange.getContext());
-                return ognlExp.evaluate(exchange, Object.class);
-            }
-
-            @Override
-            public void init(CamelContext context) {
-                bean = context.resolveLanguage("bean");
-            }
-
-            @Override
-            public String toString() {
-                return "exchangeExceptionOgnl(" + ognl + ")";
-            }
-        };
-    }
-
-    /**
-     * Executes a custom simple function
-     */
-    public static Expression customFunction(final String name, final String parameter) {
-        return new ExpressionAdapter() {
-            private Expression func;
-            private Expression exp;
-
-            @Override
-            public void init(CamelContext context) {
-                super.init(context);
-                SimpleFunctionRegistry registry
-                        = context.getCamelContextExtension().getContextPlugin(SimpleFunctionRegistry.class);
-                func = registry.getFunction(name);
-                if (func == null) {
-                    throw new IllegalArgumentException("No custom simple function with name: " + name);
-                }
-                exp = ExpressionBuilder.simpleExpression(parameter);
-                exp.init(context);
-            }
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                final Object originalBody = exchange.getMessage().getBody();
-                try {
-                    Object input = exp.evaluate(exchange, Object.class);
-                    exchange.getMessage().setBody(input);
-                    return func.evaluate(exchange, Object.class);
-                } finally {
-                    exchange.getMessage().setBody(originalBody);
-                }
-            }
-
-            public String toString() {
-                return "function(" + name + ")";
-            }
-        };
-    }
-
-    /**
-     * Evaluates the simple jsonpath with the source input
-     */
-    public static Expression simpleJsonPathExpression(String source, String path) {
-        return new ExpressionAdapter() {
-            private Expression input;
-
-            @Override
-            public Object evaluate(Exchange exchange) {
-                Jsonable j = input.evaluate(exchange, Jsonable.class);
-                if (j instanceof JsonObject jo) {
-                    return jo.path(path);
-                } else if (j instanceof JsonArray ja) {
-                    // wrap array in pseudo root to leverage json-path here
-                    JsonObject jo = new JsonObject();
-                    jo.put("_root_", ja);
-                    return jo.path("_root_." + path);
-                }
-                return null;
-            }
-
-            @Override
-            public void init(CamelContext context) {
-                super.init(context);
-                input = ExpressionBuilder.singleInputExpression(source);
-                input.init(context);
-            }
-
-            @Override
-            public String toString() {
-                return "simpleJsonpath[" + path + "]";
-            }
-        };
-    }
-
-    /**
-     * Expression adapter for OGNL expression from Message Header or Exchange property
-     */
-    public static class KeyedOgnlExpressionAdapter extends ExpressionAdapter {
-        private final String ognl;
-        private final String toStringValue;
-        private final KeyedEntityRetrievalStrategy keyedEntityRetrievalStrategy;
-        private String key;
-        private final String method;
-        private Expression keyExpression;
-        private Expression ognlExpression;
-        private Language beanLanguage;
-
-        public KeyedOgnlExpressionAdapter(String ognl, String toStringValue,
-                                          KeyedEntityRetrievalStrategy keyedEntityRetrievalStrategy) {
-            this.ognl = ognl;
-            this.toStringValue = toStringValue;
-            this.keyedEntityRetrievalStrategy = keyedEntityRetrievalStrategy;
-
-            // Split ognl except when this is not a Map, Array
-            // and we would like to keep the dots within the key name
-            List<String> methods = OgnlHelper.splitOgnl(ognl);
-
-            key = methods.get(0);
-            String keySuffix = "";
-            // if ognl starts with a key inside brackets (eg: [foo.bar])
-            // remove starting and ending brackets from key
-            if (key.startsWith("[") && key.endsWith("]")) {
-                key = StringHelper.removeLeadingAndEndingQuotes(key.substring(1, key.length() - 1));
-                keySuffix = StringHelper.after(methods.get(0), key);
-            }
-            // remove any OGNL operators so we got the pure key name
-            key = OgnlHelper.removeOperators(key);
-            // and this may be the last remainder method to try as OGNL if there are no exchange properties with those key names
-            method = StringHelper.after(ognl, key + keySuffix);
-        }
-
-        @Override
-        public void init(CamelContext context) {
-            beanLanguage = context.resolveLanguage("bean");
-            ognlExpression = ExpressionBuilder.simpleExpression(ognl);
-            ognlExpression.init(context);
-            // key must be lazy eval as it only used in special situations
-        }
-
-        @Override
-        public Object evaluate(Exchange exchange) {
-            // try with full name first
-            Object property = keyedEntityRetrievalStrategy.getKeyedEntity(exchange, ognlExpression);
-            if (property != null) {
-                return property;
-            }
-
-            // key must be lazy eval as it only used in special situations
-            if (keyExpression == null) {
-                keyExpression = ExpressionBuilder.simpleExpression(key);
-                keyExpression.init(exchange.getContext());
-            }
-
-            property = keyedEntityRetrievalStrategy.getKeyedEntity(exchange, keyExpression);
-            if (property == null) {
-                return null;
-            }
-            if (method != null) {
-                Expression exp = beanLanguage.createExpression(null, new Object[] { null, property, method });
-                exp.init(exchange.getContext());
-                return exp.evaluate(exchange, Object.class);
-            } else {
-                return property;
-            }
-        }
-
-        @Override
-        public String toString() {
-            return toStringValue;
-        }
-
-        /**
-         * Strategy to retrieve the value based on the key
-         */
-        public interface KeyedEntityRetrievalStrategy {
-            Object getKeyedEntity(Exchange exchange, Expression key);
-        }
-    }
-}
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/StringExpressionBuilder.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/StringExpressionBuilder.java
new file mode 100644
index 000000000000..f2b29d8a7f88
--- /dev/null
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/StringExpressionBuilder.java
@@ -0,0 +1,804 @@
+/*
+ * 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;
+
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+import org.apache.camel.Expression;
+import org.apache.camel.StreamCache;
+import org.apache.camel.support.ExpressionAdapter;
+import org.apache.camel.support.builder.ExpressionBuilder;
+import org.apache.camel.support.builder.PredicateBuilder;
+import org.apache.camel.util.IOHelper;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.StringHelper;
+import org.apache.camel.util.StringQuoteHelper;
+
+/**
+ * Expression builder for string-manipulation functions used by the simple language.
+ */
+public final class StringExpressionBuilder {
+
+    private StringExpressionBuilder() {
+    }
+
+    /**
+     * Safe quotes the given expressions (uses message body if expression is null) if necessary.
+     */
+    public static Expression safeQuoteExpression(final String expression) {
+        return new ExpressionAdapter() {
+            private Expression exp;
+
+            @Override
+            public void init(CamelContext context) {
+                if (expression != null) {
+                    exp = context.resolveLanguage("simple").createExpression(expression);
+                    exp.init(context);
+                }
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                Object value;
+                if (exp != null) {
+                    value = exp.evaluate(exchange, Object.class);
+                } else {
+                    value = exchange.getMessage().getBody(Object.class);
+                }
+                if (value != null) {
+                    String type = kindOfType(value);
+                    if ("string".equals(type) || "array".equals(type) || "object".equals(type)) {
+                        String body = exchange.getContext().getTypeConverter().tryConvertTo(String.class, exchange, value);
+                        body = StringHelper.removeLeadingAndEndingQuotes(body);
+                        value = StringQuoteHelper.doubleQuote(body);
+                    }
+                }
+                return value;
+            }
+
+            private String kindOfType(Object value) {
+                Class<?> type = value.getClass();
+                if (ObjectHelper.isNumericType(type)) {
+                    return "number";
+                } else if (boolean.class == type || Boolean.class == type) {
+                    return "boolean";
+                } else if (value instanceof CharSequence) {
+                    return "string";
+                } else if (ObjectHelper.isPrimitiveArrayType(type) || value instanceof Collection
+                        || value instanceof Map<?, ?>) {
+                    return "array";
+                } else {
+                    return "object";
+                }
+            }
+
+            @Override
+            public String toString() {
+                if (expression != null) {
+                    return "safeQuote(" + expression + ")";
+                } else {
+                    return "safeQuote()";
+                }
+            }
+        };
+    }
+
+    /**
+     * Double quotes the given expressions (uses message body if expression is null)
+     */
+    public static Expression quoteExpression(final String expression) {
+        return new ExpressionAdapter() {
+            private Expression exp;
+
+            @Override
+            public void init(CamelContext context) {
+                if (expression != null) {
+                    exp = context.resolveLanguage("simple").createExpression(expression);
+                    exp.init(context);
+                }
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                String value;
+                if (exp != null) {
+                    value = exp.evaluate(exchange, String.class);
+                } else {
+                    value = exchange.getMessage().getBody(String.class);
+                }
+                if (value != null && !StringHelper.isDoubleQuoted(value)) {
+                    value = StringHelper.removeLeadingAndEndingQuotes(value);
+                    value = StringQuoteHelper.doubleQuote(value);
+                }
+                return value;
+            }
+
+            @Override
+            public String toString() {
+                if (expression != null) {
+                    return "quote(" + expression + ")";
+                } else {
+                    return "quote()";
+                }
+            }
+        };
+    }
+
+    /**
+     * Un quotes the given expressions (uses message body if expression is null)
+     */
+    public static Expression unquoteExpression(final String expression) {
+        return new ExpressionAdapter() {
+            private Expression exp;
+
+            @Override
+            public void init(CamelContext context) {
+                if (expression != null) {
+                    exp = context.resolveLanguage("simple").createExpression(expression);
+                    exp.init(context);
+                }
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                String value;
+                if (exp != null) {
+                    value = exp.evaluate(exchange, String.class);
+                } else {
+                    value = exchange.getMessage().getBody(String.class);
+                }
+                if (value != null) {
+                    value = StringHelper.removeLeadingAndEndingQuotes(value);
+                }
+                return value;
+            }
+
+            @Override
+            public String toString() {
+                if (expression != null) {
+                    return "unquote(" + expression + ")";
+                } else {
+                    return "unquote()";
+                }
+            }
+        };
+    }
+
+    /**
+     * Trims the given expressions (uses message body if expression is null)
+     */
+    public static Expression trimExpression(final String expression) {
+        return new ExpressionAdapter() {
+            private Expression exp;
+
+            @Override
+            public void init(CamelContext context) {
+                if (expression != null) {
+                    exp = context.resolveLanguage("simple").createExpression(expression);
+                    exp.init(context);
+                }
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                String value;
+                if (exp != null) {
+                    value = exp.evaluate(exchange, String.class);
+                } else {
+                    value = exchange.getMessage().getBody(String.class);
+                }
+                if (value != null) {
+                    value = value.trim();
+                }
+                return value;
+            }
+
+            @Override
+            public String toString() {
+                if (expression != null) {
+                    return "trim(" + expression + ")";
+                } else {
+                    return "trim()";
+                }
+            }
+        };
+    }
+
+    /**
+     * Capitalizes the given expressions (uses message body if expression is null)
+     */
+    public static Expression capitalizeExpression(final String expression) {
+        return new ExpressionAdapter() {
+            private Expression exp;
+
+            @Override
+            public void init(CamelContext context) {
+                if (expression != null) {
+                    exp = context.resolveLanguage("simple").createExpression(expression);
+                    exp.init(context);
+                }
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                String value;
+                if (exp != null) {
+                    value = exp.evaluate(exchange, String.class);
+                } else {
+                    value = exchange.getMessage().getBody(String.class);
+                }
+                if (value != null) {
+                    value = StringHelper.capitalizeAll(value);
+                }
+                return value;
+            }
+
+            @Override
+            public String toString() {
+                if (expression != null) {
+                    return "capitalize(" + expression + ")";
+                } else {
+                    return "capitalize()";
+                }
+            }
+        };
+    }
+
+    /**
+     * Pad the expression
+     */
+    public static Expression padExpression(final String expression, final String length, final String separator) {
+        return new ExpressionAdapter() {
+            private Expression exp;
+            private Expression len;
+
+            @Override
+            public void init(CamelContext context) {
+                exp = context.resolveLanguage("simple").createExpression(expression);
+                exp.init(context);
+                len = context.resolveLanguage("simple").createExpression(length);
+                len.init(context);
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                String answer = exp.evaluate(exchange, String.class);
+                if (answer == null) {
+                    return null;
+                }
+                Integer width = len.evaluate(exchange, Integer.class);
+                if (width == null) {
+                    throw new IllegalArgumentException("pad length expression evaluated to null: " + length);
+                }
+                String sep = separator;
+                if (sep == null || sep.isEmpty()) {
+                    sep = " ";
+                }
+
+                int max = Math.abs(width);
+                while (max > answer.length()) {
+                    if (width > 0) {
+                        answer = answer + sep;
+                    } else {
+                        answer = sep + answer;
+                    }
+                }
+                return answer;
+            }
+
+            @Override
+            public String toString() {
+                return "pad(" + exp + "," + length + ")";
+            }
+        };
+    }
+
+    /**
+     * String concats the two expressions.
+     */
+    public static Expression concatExpression(final String right, final String left, String separator) {
+        return new ExpressionAdapter() {
+            private Expression exp1;
+            private Expression exp2;
+
+            @Override
+            public void init(CamelContext context) {
+                exp1 = context.resolveLanguage("simple").createExpression(right);
+                exp1.init(context);
+                exp2 = context.resolveLanguage("simple").createExpression(left);
+                exp2.init(context);
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                String value1 = exp1.evaluate(exchange, String.class);
+                String value2 = exp2.evaluate(exchange, String.class);
+                if (value1 != null && value2 != null) {
+                    return value1 + (separator != null ? separator : "") + value2;
+                } else {
+                    return value1 != null ? value1 : value2;
+                }
+            }
+
+            @Override
+            public String toString() {
+                return "concat(" + right + "," + left + ")";
+            }
+        };
+    }
+
+    /**
+     * Uppercases the given expressions (uses message body if expression is null)
+     */
+    public static Expression uppercaseExpression(final String expression) {
+        return new ExpressionAdapter() {
+            private Expression exp;
+
+            @Override
+            public void init(CamelContext context) {
+                if (expression != null) {
+                    exp = context.resolveLanguage("simple").createExpression(expression);
+                    exp.init(context);
+                }
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                String value;
+                if (exp != null) {
+                    value = exp.evaluate(exchange, String.class);
+                } else {
+                    value = exchange.getMessage().getBody(String.class);
+                }
+                if (value != null) {
+                    value = value.toUpperCase(Locale.ENGLISH);
+                }
+                return value;
+            }
+
+            @Override
+            public String toString() {
+                if (expression != null) {
+                    return "uppercase(" + expression + ")";
+                } else {
+                    return "uppercase()";
+                }
+            }
+        };
+    }
+
+    /**
+     * Lowercases the given expressions (uses message body if expression is null)
+     */
+    public static Expression lowercaseExpression(final String expression) {
+        return new ExpressionAdapter() {
+            private Expression exp;
+
+            @Override
+            public void init(CamelContext context) {
+                if (expression != null) {
+                    exp = context.resolveLanguage("simple").createExpression(expression);
+                    exp.init(context);
+                }
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                String value;
+                if (exp != null) {
+                    value = exp.evaluate(exchange, String.class);
+                } else {
+                    value = exchange.getMessage().getBody(String.class);
+                }
+                if (value != null) {
+                    value = value.toLowerCase(Locale.ENGLISH);
+                }
+                return value;
+            }
+
+            @Override
+            public String toString() {
+                if (expression != null) {
+                    return "lowercase(" + expression + ")";
+                } else {
+                    return "lowercase()";
+                }
+            }
+        };
+    }
+
+    /**
+     * Returns the size of the expression (number of elements in collection/map; otherwise 1)
+     */
+    public static Expression sizeExpression(final String expression) {
+        return new ExpressionAdapter() {
+            private Expression exp;
+
+            @Override
+            public void init(CamelContext context) {
+                if (expression != null) {
+                    exp = context.resolveLanguage("simple").createExpression(expression);
+                    exp.init(context);
+                }
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                Object body;
+                if (exp != null) {
+                    body = exp.evaluate(exchange, Object.class);
+                } else {
+                    body = exchange.getMessage().getBody();
+                }
+                if (body != null) {
+                    if (body instanceof byte[] arr) {
+                        return arr.length;
+                    } else if (body instanceof char[] arr) {
+                        return arr.length;
+                    } else if (body instanceof int[] arr) {
+                        return arr.length;
+                    } else if (body instanceof long[] arr) {
+                        return arr.length;
+                    } else if (body instanceof double[] arr) {
+                        return arr.length;
+                    } else if (body instanceof String[] arr) {
+                        return arr.length;
+                    } else if (body instanceof Collection<?> c) {
+                        return c.size();
+                    } else if (body instanceof Map<?, ?> m) {
+                        return m.size();
+                    } else {
+                        return 1;
+                    }
+                }
+                return 0;
+            }
+
+            @Override
+            public String toString() {
+                if (expression != null) {
+                    return "size(" + expression + ")";
+                } else {
+                    return "size()";
+                }
+            }
+        };
+    }
+
+    /**
+     * Returns the length of the expression (length of payload in bytes)
+     */
+    public static Expression lengthExpression(final String expression) {
+        return new ExpressionAdapter() {
+            private Expression exp;
+
+            @Override
+            public void init(CamelContext context) {
+                if (expression != null) {
+                    exp = context.resolveLanguage("simple").createExpression(expression);
+                    exp.init(context);
+                }
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                Object body;
+                if (exp != null) {
+                    body = exp.evaluate(exchange, Object.class);
+                } else {
+                    body = exchange.getMessage().getBody();
+                }
+                try {
+                    if (body instanceof byte[] arr) {
+                        return arr.length;
+                    } else if (body instanceof char[] arr) {
+                        return arr.length;
+                    } else if (body instanceof int[] arr) {
+                        return arr.length;
+                    } else if (body instanceof long[] arr) {
+                        return arr.length;
+                    } else if (body instanceof double[] arr) {
+                        return arr.length;
+                    } else if (body instanceof String[] arr) {
+                        return arr.length;
+                    } else if (body instanceof StreamCache sc) {
+                        return (int) sc.length();
+                    } else {
+                        // first read as stream
+                        InputStream is = null;
+                        try {
+                            is = exchange.getContext().getTypeConverter().tryConvertTo(InputStream.class, exchange, body);
+                            int len = 0;
+                            while (is.read() != -1) {
+                                len++;
+                            }
+                            return len;
+                        } catch (Exception e) {
+                            // ignore
+                        } finally {
+                            IOHelper.close(is);
+                        }
+                        // fallback to use string based
+                        String data = exchange.getContext().getTypeConverter().tryConvertTo(String.class, exchange, body);
+                        if (data != null) {
+                            return data.length();
+                        }
+                    }
+                } finally {
+                    if (body instanceof StreamCache streamCache) {
+                        streamCache.reset();
+                    }
+                }
+                return null;
+            }
+
+            @Override
+            public String toString() {
+                if (expression != null) {
+                    return "length(" + expression + ")";
+                } else {
+                    return "length()";
+                }
+            }
+        };
+    }
+
+    /**
+     * Normalizes the whitespaces in the given expressions (uses message body if expression is null)
+     */
+    public static Expression normalizeWhitespaceExpression(final String expression) {
+        return new ExpressionAdapter() {
+            private Expression exp;
+
+            @Override
+            public void init(CamelContext context) {
+                if (expression != null) {
+                    exp = context.resolveLanguage("simple").createExpression(expression);
+                    exp.init(context);
+                }
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                String value;
+                if (exp != null) {
+                    value = exp.evaluate(exchange, String.class);
+                } else {
+                    value = exchange.getMessage().getBody(String.class);
+                }
+                if (value != null) {
+                    value = StringHelper.normalizeWhitespace(value);
+                }
+                return value;
+            }
+
+            @Override
+            public String toString() {
+                if (expression != null) {
+                    return "normalizeWhitespace(" + expression + ")";
+                } else {
+                    return "normalizeWhitespace()";
+                }
+            }
+        };
+    }
+
+    /**
+     * Replaces string values from the expression
+     */
+    public static Expression replaceExpression(final String expression, final String from, final String to) {
+        return new ExpressionAdapter() {
+            private Expression exp;
+
+            @Override
+            public void init(CamelContext context) {
+                exp = context.resolveLanguage("simple").createExpression(expression);
+                exp.init(context);
+                exp = ExpressionBuilder.replaceAll(exp, from, to);
+                exp.init(context);
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                return exp.evaluate(exchange, Object.class);
+            }
+
+            @Override
+            public String toString() {
+                return "replace(" + expression + "," + from + "," + to + ")";
+            }
+        };
+    }
+
+    /**
+     * Substring string values from the expression
+     */
+    public static Expression substringExpression(final String expression, final String head, final String tail) {
+        return new ExpressionAdapter() {
+            private Expression exp;
+            private Expression exp1;
+            private Expression exp2;
+
+            @Override
+            public void init(CamelContext context) {
+                exp = context.resolveLanguage("simple").createExpression(expression);
+                exp.init(context);
+                exp1 = ExpressionBuilder.simpleExpression(head);
+                exp1.init(context);
+                exp2 = ExpressionBuilder.simpleExpression(tail);
+                exp2.init(context);
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                int num1 = exp1.evaluate(exchange, Integer.class);
+                int num2 = exp2.evaluate(exchange, Integer.class);
+                if (num1 < 0 && num2 == 0) {
+                    // if there is only one value and its negative then we want to clip from tail
+                    num2 = num1;
+                    num1 = 0;
+                }
+                num1 = Math.abs(num1);
+                num2 = Math.abs(num2);
+                return ExpressionBuilder.substring(exp, num1, num2).evaluate(exchange, Object.class);
+            }
+
+            @Override
+            public String toString() {
+                return "substring(" + expression + "," + head + "," + tail + ")";
+            }
+        };
+    }
+
+    /**
+     * Returns the substring from the given expression that comes before
+     */
+    public static Expression substringBeforeExpression(final String expression, final String before) {
+        return new ExpressionAdapter() {
+            private Expression exp;
+            private Expression expBefore;
+
+            @Override
+            public void init(CamelContext context) {
+                exp = context.resolveLanguage("simple").createExpression(expression);
+                exp.init(context);
+                expBefore = ExpressionBuilder.simpleExpression(before);
+                expBefore.init(context);
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                String body = exp.evaluate(exchange, String.class);
+                if (body == null) {
+                    return null;
+                }
+                String bef = expBefore.evaluate(exchange, String.class);
+                return StringHelper.before(body, bef);
+            }
+
+            @Override
+            public String toString() {
+                return "substringBefore(" + expression + "," + before + ")";
+            }
+        };
+    }
+
+    /**
+     * Returns the substring from the given expression that comes after
+     */
+    public static Expression substringAfterExpression(final String expression, final String after) {
+        return new ExpressionAdapter() {
+            private Expression exp;
+            private Expression expAfter;
+
+            @Override
+            public void init(CamelContext context) {
+                exp = context.resolveLanguage("simple").createExpression(expression);
+                exp.init(context);
+                expAfter = ExpressionBuilder.simpleExpression(after);
+                expAfter.init(context);
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                String body = exp.evaluate(exchange, String.class);
+                if (body == null) {
+                    return null;
+                }
+                String aft = expAfter.evaluate(exchange, String.class);
+                return StringHelper.after(body, aft);
+            }
+
+            @Override
+            public String toString() {
+                return "substringAfter(" + expression + "," + after + ")";
+            }
+        };
+    }
+
+    /**
+     * Returns the substring from the given expression that are between after and before
+     */
+    public static Expression substringBetweenExpression(
+            final String expression, final String after,
+            final String before) {
+        return new ExpressionAdapter() {
+            private Expression exp;
+            private Expression expAfter;
+            private Expression expBefore;
+
+            @Override
+            public void init(CamelContext context) {
+                exp = context.resolveLanguage("simple").createExpression(expression);
+                exp.init(context);
+                expAfter = ExpressionBuilder.simpleExpression(after);
+                expAfter.init(context);
+                expBefore = ExpressionBuilder.simpleExpression(before);
+                expBefore.init(context);
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                String body = exp.evaluate(exchange, String.class);
+                if (body == null) {
+                    return null;
+                }
+                String aft = expAfter.evaluate(exchange, String.class);
+                String bef = expBefore.evaluate(exchange, String.class);
+                return StringHelper.between(body, aft, bef);
+            }
+
+            @Override
+            public String toString() {
+                return "substringBetween(" + expression + "," + after + "," + before + ")";
+            }
+        };
+    }
+
+    /**
+     * Whether the expression matches the pattern
+     */
+    public static Expression containsExpression(final String expression, final String pattern) {
+        return new ExpressionAdapter() {
+            private Expression right;
+            private Expression left;
+
+            @Override
+            public void init(CamelContext context) {
+                left = context.resolveLanguage("simple").createExpression(expression);
+                left.init(context);
+                right = context.resolveLanguage("simple").createExpression(pattern);
+                right.init(context);
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                return PredicateBuilder.containsIgnoreCase(left, right).matches(exchange);
+            }
+
+            @Override
+            public String toString() {
+                return "contains(" + expression + "," + pattern + ")";
+            }
+        };
+    }
+}
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 5ee6064093e7..d7bcd6a14313 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
@@ -21,7 +21,7 @@ import java.util.Map;
 import org.apache.camel.CamelContext;
 import org.apache.camel.Expression;
 import org.apache.camel.language.simple.BaseSimpleParser;
-import org.apache.camel.language.simple.SimpleExpressionBuilder;
+import org.apache.camel.language.simple.FileExpressionBuilder;
 import org.apache.camel.language.simple.SimpleFunctionDispatcher;
 import org.apache.camel.language.simple.SimpleFunctionHelper;
 import org.apache.camel.language.simple.SimplePredicateParser;
@@ -157,33 +157,33 @@ public class SimpleFunctionExpression extends LiteralExpression {
 
     private Expression createSimpleFileExpression(String remainder, boolean strict) {
         if (ObjectHelper.equal(remainder, "name")) {
-            return SimpleExpressionBuilder.fileNameExpression();
+            return FileExpressionBuilder.fileNameExpression();
         } else if (ObjectHelper.equal(remainder, "name.noext")) {
-            return SimpleExpressionBuilder.fileNameNoExtensionExpression();
+            return FileExpressionBuilder.fileNameNoExtensionExpression();
         } else if (ObjectHelper.equal(remainder, "name.noext.single")) {
-            return SimpleExpressionBuilder.fileNameNoExtensionSingleExpression();
+            return FileExpressionBuilder.fileNameNoExtensionSingleExpression();
         } else if (ObjectHelper.equal(remainder, "name.ext") || ObjectHelper.equal(remainder, "ext")) {
-            return SimpleExpressionBuilder.fileExtensionExpression();
+            return FileExpressionBuilder.fileExtensionExpression();
         } else if (ObjectHelper.equal(remainder, "name.ext.single")) {
-            return SimpleExpressionBuilder.fileExtensionSingleExpression();
+            return FileExpressionBuilder.fileExtensionSingleExpression();
         } else if (ObjectHelper.equal(remainder, "onlyname")) {
-            return SimpleExpressionBuilder.fileOnlyNameExpression();
+            return FileExpressionBuilder.fileOnlyNameExpression();
         } else if (ObjectHelper.equal(remainder, "onlyname.noext")) {
-            return SimpleExpressionBuilder.fileOnlyNameNoExtensionExpression();
+            return FileExpressionBuilder.fileOnlyNameNoExtensionExpression();
         } else if (ObjectHelper.equal(remainder, "onlyname.noext.single")) {
-            return SimpleExpressionBuilder.fileOnlyNameNoExtensionSingleExpression();
+            return FileExpressionBuilder.fileOnlyNameNoExtensionSingleExpression();
         } else if (ObjectHelper.equal(remainder, "parent")) {
-            return SimpleExpressionBuilder.fileParentExpression();
+            return FileExpressionBuilder.fileParentExpression();
         } else if (ObjectHelper.equal(remainder, "path")) {
-            return SimpleExpressionBuilder.filePathExpression();
+            return FileExpressionBuilder.filePathExpression();
         } else if (ObjectHelper.equal(remainder, "absolute")) {
-            return SimpleExpressionBuilder.fileAbsoluteExpression();
+            return FileExpressionBuilder.fileAbsoluteExpression();
         } else if (ObjectHelper.equal(remainder, "absolute.path")) {
-            return SimpleExpressionBuilder.fileAbsolutePathExpression();
+            return FileExpressionBuilder.fileAbsolutePathExpression();
         } else if (ObjectHelper.equal(remainder, "length") || ObjectHelper.equal(remainder, "size")) {
-            return SimpleExpressionBuilder.fileSizeExpression();
+            return FileExpressionBuilder.fileSizeExpression();
         } else if (ObjectHelper.equal(remainder, "modified")) {
-            return SimpleExpressionBuilder.fileLastModifiedExpression();
+            return FileExpressionBuilder.fileLastModifiedExpression();
         }
         if (strict) {
             throw new SimpleParserException("Unknown file language syntax: " + remainder, token.getIndex());
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/BodyFunctionFactory.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/BodyFunctionFactory.java
index 76b59ee26d20..4c0b14bde767 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/BodyFunctionFactory.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/BodyFunctionFactory.java
@@ -20,7 +20,7 @@ import java.util.List;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.Expression;
-import org.apache.camel.language.simple.SimpleExpressionBuilder;
+import org.apache.camel.language.simple.OgnlExpressionBuilder;
 import org.apache.camel.language.simple.types.SimpleParserException;
 import org.apache.camel.spi.SimpleLanguageFunctionFactory;
 import org.apache.camel.support.builder.ExpressionBuilder;
@@ -71,7 +71,7 @@ public final class BodyFunctionFactory implements SimpleLanguageFunctionFactory
                 if (invalid) {
                     throw new SimpleParserException("Valid syntax: ${bodyAs(type).OGNL} was: " + function, index);
                 }
-                return SimpleExpressionBuilder.bodyOgnlExpression(type, remainder);
+                return OgnlExpressionBuilder.bodyOgnlExpression(type, remainder);
             } else {
                 return ExpressionBuilder.bodyExpression(type);
             }
@@ -92,9 +92,9 @@ public final class BodyFunctionFactory implements SimpleLanguageFunctionFactory
                     throw new SimpleParserException(
                             "Valid syntax: ${mandatoryBodyAs(type).OGNL} was: " + function, index);
                 }
-                return SimpleExpressionBuilder.mandatoryBodyOgnlExpression(type, remainder);
+                return OgnlExpressionBuilder.mandatoryBodyOgnlExpression(type, remainder);
             } else {
-                return SimpleExpressionBuilder.mandatoryBodyExpression(type);
+                return OgnlExpressionBuilder.mandatoryBodyExpression(type);
             }
         }
 
@@ -109,7 +109,7 @@ public final class BodyFunctionFactory implements SimpleLanguageFunctionFactory
             if (invalid) {
                 throw new SimpleParserException("Valid syntax: ${body.OGNL} was: " + function, index);
             }
-            return SimpleExpressionBuilder.bodyOgnlExpression(remainder);
+            return OgnlExpressionBuilder.bodyOgnlExpression(remainder);
         }
 
         return null;
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/CollateFunctionFactory.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/CollateFunctionFactory.java
index e469426bbe61..7600703539c5 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/CollateFunctionFactory.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/CollateFunctionFactory.java
@@ -18,7 +18,7 @@ package org.apache.camel.language.simple.functions;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.Expression;
-import org.apache.camel.language.simple.SimpleExpressionBuilder;
+import org.apache.camel.language.simple.MiscExpressionBuilder;
 import org.apache.camel.language.simple.types.SimpleParserException;
 import org.apache.camel.spi.SimpleLanguageFunctionFactory;
 import org.apache.camel.util.ObjectHelper;
@@ -41,7 +41,7 @@ public final class CollateFunctionFactory implements SimpleLanguageFunctionFacto
         if (values == null || ObjectHelper.isEmpty(values)) {
             throw new SimpleParserException("Valid syntax: ${collate(group)} was: " + function, index);
         }
-        return SimpleExpressionBuilder.collateExpression("${body}", values.trim());
+        return MiscExpressionBuilder.collateExpression("${body}", values.trim());
     }
 
     @Override
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/CollectionFunctionFactory.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/CollectionFunctionFactory.java
index f6a381413784..0f979e86e86b 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/CollectionFunctionFactory.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/CollectionFunctionFactory.java
@@ -22,7 +22,7 @@ import java.util.stream.Collectors;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.Expression;
-import org.apache.camel.language.simple.SimpleExpressionBuilder;
+import org.apache.camel.language.simple.CollectionExpressionBuilder;
 import org.apache.camel.language.simple.types.SimpleParserException;
 import org.apache.camel.spi.SimpleLanguageFunctionFactory;
 import org.apache.camel.util.ObjectHelper;
@@ -67,7 +67,7 @@ public final class CollectionFunctionFactory implements SimpleLanguageFunctionFa
                 exp = tokens[1];
             }
             type = StringHelper.removeQuotes(type);
-            return SimpleExpressionBuilder.setHeaderExpression(name, type, exp);
+            return CollectionExpressionBuilder.setHeaderExpression(name, type, exp);
         }
 
         remainder = ifStartsWithReturnRemainder("setVariable(", function);
@@ -92,7 +92,7 @@ public final class CollectionFunctionFactory implements SimpleLanguageFunctionFa
                 exp = tokens[1];
             }
             type = StringHelper.removeQuotes(type);
-            return SimpleExpressionBuilder.setVariableExpression(name, type, exp);
+            return CollectionExpressionBuilder.setVariableExpression(name, type, exp);
         }
 
         remainder = ifStartsWithReturnRemainder("range(", function);
@@ -108,9 +108,9 @@ public final class CollectionFunctionFactory implements SimpleLanguageFunctionFa
                     throw new SimpleParserException(
                             "Valid syntax: ${range(min,max)} or ${range(max)} was: " + function, index);
                 }
-                return SimpleExpressionBuilder.rangeExpression(tokens[0].trim(), tokens[1].trim());
+                return CollectionExpressionBuilder.rangeExpression(tokens[0].trim(), tokens[1].trim());
             } else {
-                return SimpleExpressionBuilder.rangeExpression("1", values.trim());
+                return CollectionExpressionBuilder.rangeExpression("1", values.trim());
             }
         }
 
@@ -123,7 +123,7 @@ public final class CollectionFunctionFactory implements SimpleLanguageFunctionFa
             } else {
                 tokens = new String[] { "${body}" };
             }
-            return SimpleExpressionBuilder.distinctExpression(tokens);
+            return CollectionExpressionBuilder.distinctExpression(tokens);
         }
 
         remainder = ifStartsWithReturnRemainder("reverse(", function);
@@ -135,7 +135,7 @@ public final class CollectionFunctionFactory implements SimpleLanguageFunctionFa
             } else {
                 tokens = new String[] { "${body}" };
             }
-            return SimpleExpressionBuilder.reverseExpression(tokens);
+            return CollectionExpressionBuilder.reverseExpression(tokens);
         }
 
         remainder = ifStartsWithReturnRemainder("shuffle(", function);
@@ -147,7 +147,7 @@ public final class CollectionFunctionFactory implements SimpleLanguageFunctionFa
             } else {
                 tokens = new String[] { "${body}" };
             }
-            return SimpleExpressionBuilder.shuffleExpression(tokens);
+            return CollectionExpressionBuilder.shuffleExpression(tokens);
         }
 
         remainder = ifStartsWithReturnRemainder("split(", function);
@@ -170,7 +170,7 @@ public final class CollectionFunctionFactory implements SimpleLanguageFunctionFa
             } else if ("\n".equals(values)) {
                 separator = values;
             }
-            return SimpleExpressionBuilder.splitStringExpression(exp, separator);
+            return CollectionExpressionBuilder.splitStringExpression(exp, separator);
         }
 
         remainder = ifStartsWithReturnRemainder("sort(", function);
@@ -191,7 +191,7 @@ public final class CollectionFunctionFactory implements SimpleLanguageFunctionFa
                     reverse = Boolean.parseBoolean(tokens[0]);
                 }
             }
-            return SimpleExpressionBuilder.sortExpression(exp, reverse);
+            return CollectionExpressionBuilder.sortExpression(exp, reverse);
         }
 
         remainder = ifStartsWithReturnRemainder("forEach(", function);
@@ -208,7 +208,7 @@ public final class CollectionFunctionFactory implements SimpleLanguageFunctionFa
             }
             String exp1 = tokens[0];
             String exp2 = Arrays.stream(tokens).skip(1).collect(Collectors.joining(","));
-            return SimpleExpressionBuilder.forEachExpression(exp1, exp2);
+            return CollectionExpressionBuilder.forEachExpression(exp1, exp2);
         }
 
         remainder = ifStartsWithReturnRemainder("filter(", function);
@@ -225,7 +225,7 @@ public final class CollectionFunctionFactory implements SimpleLanguageFunctionFa
             }
             String exp1 = tokens[0];
             String exp2 = Arrays.stream(tokens).skip(1).collect(Collectors.joining(","));
-            return SimpleExpressionBuilder.filterExpression(exp1, exp2);
+            return CollectionExpressionBuilder.filterExpression(exp1, exp2);
         }
 
         remainder = ifStartsWithReturnRemainder("listAdd(", function);
@@ -243,7 +243,7 @@ public final class CollectionFunctionFactory implements SimpleLanguageFunctionFa
                 exp1 = tokens[0];
             }
             String exp2 = Arrays.stream(tokens).skip(skip).collect(Collectors.joining(","));
-            return SimpleExpressionBuilder.listAddExpression(exp1, exp2);
+            return CollectionExpressionBuilder.listAddExpression(exp1, exp2);
         }
 
         remainder = ifStartsWithReturnRemainder("listRemove(", function);
@@ -261,7 +261,7 @@ public final class CollectionFunctionFactory implements SimpleLanguageFunctionFa
                 exp1 = tokens[0];
             }
             String exp2 = Arrays.stream(tokens).skip(skip).collect(Collectors.joining(","));
-            return SimpleExpressionBuilder.listRemoveExpression(exp1, exp2);
+            return CollectionExpressionBuilder.listRemoveExpression(exp1, exp2);
         }
 
         remainder = ifStartsWithReturnRemainder("mapAdd(", function);
@@ -287,7 +287,7 @@ public final class CollectionFunctionFactory implements SimpleLanguageFunctionFa
                         "Valid syntax: ${mapAdd(key,exp)} or ${mapAdd(exp,key,exp)} was: " + function, index);
             }
             String exp2 = Arrays.stream(tokens).skip(skip).collect(Collectors.joining(","));
-            return SimpleExpressionBuilder.mapAddExpression(exp1, key, exp2);
+            return CollectionExpressionBuilder.mapAddExpression(exp1, key, exp2);
         }
 
         remainder = ifStartsWithReturnRemainder("mapRemove(", function);
@@ -310,7 +310,7 @@ public final class CollectionFunctionFactory implements SimpleLanguageFunctionFa
             } else {
                 key = tokens[0];
             }
-            return SimpleExpressionBuilder.mapRemoveExpression(exp, key);
+            return CollectionExpressionBuilder.mapRemoveExpression(exp, key);
         }
 
         remainder = ifStartsWithReturnRemainder("list(", function);
@@ -320,7 +320,7 @@ public final class CollectionFunctionFactory implements SimpleLanguageFunctionFa
             if (ObjectHelper.isNotEmpty(values)) {
                 tokens = StringQuoteHelper.splitSafeQuote(values, ',', true, false);
             }
-            return SimpleExpressionBuilder.listExpression(tokens);
+            return CollectionExpressionBuilder.listExpression(tokens);
         }
 
         remainder = ifStartsWithReturnRemainder("map(", function);
@@ -334,7 +334,7 @@ public final class CollectionFunctionFactory implements SimpleLanguageFunctionFa
                 throw new SimpleParserException(
                         "Map function must have an even number of values, was: " + tokens.length + " values.", index);
             }
-            return SimpleExpressionBuilder.mapExpression(tokens);
+            return CollectionExpressionBuilder.mapExpression(tokens);
         }
 
         return null;
@@ -485,7 +485,7 @@ public final class CollectionFunctionFactory implements SimpleLanguageFunctionFa
                 }
                 return "rangeList(exchange, " + before + ", " + after + ")";
             } else {
-                return "rangeList(exchange, 0, " + values.trim() + ")";
+                return "rangeList(exchange, 1, " + values.trim() + ")";
             }
         }
 
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/CustomFunctionFactory.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/CustomFunctionFactory.java
index a98f278103a1..856d8a35f92e 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/CustomFunctionFactory.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/CustomFunctionFactory.java
@@ -18,7 +18,7 @@ package org.apache.camel.language.simple.functions;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.Expression;
-import org.apache.camel.language.simple.SimpleExpressionBuilder;
+import org.apache.camel.language.simple.MiscExpressionBuilder;
 import org.apache.camel.language.simple.types.SimpleParserException;
 import org.apache.camel.spi.SimpleLanguageFunctionFactory;
 import org.apache.camel.util.ObjectHelper;
@@ -61,7 +61,7 @@ public final class CustomFunctionFactory implements SimpleLanguageFunctionFactor
         if (param == null) {
             param = "${body}";
         }
-        return SimpleExpressionBuilder.customFunction(key, param);
+        return MiscExpressionBuilder.customFunction(key, param);
     }
 
     @Override
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/DateFunctionFactory.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/DateFunctionFactory.java
index 25a41f131a67..22157c15b21b 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/DateFunctionFactory.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/DateFunctionFactory.java
@@ -18,7 +18,7 @@ package org.apache.camel.language.simple.functions;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.Expression;
-import org.apache.camel.language.simple.SimpleExpressionBuilder;
+import org.apache.camel.language.simple.DateExpressionBuilder;
 import org.apache.camel.language.simple.types.SimpleParserException;
 import org.apache.camel.spi.SimpleLanguageFunctionFactory;
 
@@ -39,16 +39,16 @@ public final class DateFunctionFactory implements SimpleLanguageFunctionFactory
                 throw new SimpleParserException(
                         "Valid syntax: ${date-with-timezone:command:timezone:pattern} was: " + function, index);
             }
-            return SimpleExpressionBuilder.dateExpression(parts[0], parts[1], parts[2]);
+            return DateExpressionBuilder.dateExpression(parts[0], parts[1], parts[2]);
         }
 
         remainder = ifStartsWithReturnRemainder("date:", function);
         if (remainder != null) {
             String[] parts = remainder.split(":", 2);
             if (parts.length == 1) {
-                return SimpleExpressionBuilder.dateExpression(parts[0]);
+                return DateExpressionBuilder.dateExpression(parts[0]);
             } else {
-                return SimpleExpressionBuilder.dateExpression(parts[0], parts[1]);
+                return DateExpressionBuilder.dateExpression(parts[0], parts[1]);
             }
         }
 
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/DirectFunctionFactory.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/DirectFunctionFactory.java
index 54dd9290de12..fc2f0c044162 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/DirectFunctionFactory.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/DirectFunctionFactory.java
@@ -18,7 +18,7 @@ package org.apache.camel.language.simple.functions;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.Expression;
-import org.apache.camel.language.simple.SimpleExpressionBuilder;
+import org.apache.camel.language.simple.MiscExpressionBuilder;
 import org.apache.camel.spi.SimpleLanguageFunctionFactory;
 import org.apache.camel.support.builder.ExpressionBuilder;
 import org.apache.camel.util.ObjectHelper;
@@ -72,7 +72,7 @@ public final class DirectFunctionFactory implements SimpleLanguageFunctionFactor
         } else if (ObjectHelper.equal(function, "stepId")) {
             return ExpressionBuilder.stepIdExpression();
         } else if (ObjectHelper.equal(function, "null")) {
-            return SimpleExpressionBuilder.nullExpression();
+            return MiscExpressionBuilder.nullExpression();
         }
 
         return null;
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/ExchangeFunctionFactory.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/ExchangeFunctionFactory.java
index e6d9f2f88f40..e12cc05433d5 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/ExchangeFunctionFactory.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/ExchangeFunctionFactory.java
@@ -18,7 +18,7 @@ package org.apache.camel.language.simple.functions;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.Expression;
-import org.apache.camel.language.simple.SimpleExpressionBuilder;
+import org.apache.camel.language.simple.OgnlExpressionBuilder;
 import org.apache.camel.language.simple.types.SimpleParserException;
 import org.apache.camel.spi.SimpleLanguageFunctionFactory;
 import org.apache.camel.support.builder.ExpressionBuilder;
@@ -47,7 +47,7 @@ public final class ExchangeFunctionFactory implements SimpleLanguageFunctionFact
             if (invalid) {
                 throw new SimpleParserException("Valid syntax: ${camelContext.OGNL} was: " + function, index);
             }
-            return SimpleExpressionBuilder.camelContextOgnlExpression(remainder);
+            return OgnlExpressionBuilder.camelContextOgnlExpression(remainder);
         }
 
         // Exception OGNL — exchangeProperty/exchange checked separately, no prefix clash here
@@ -57,7 +57,7 @@ public final class ExchangeFunctionFactory implements SimpleLanguageFunctionFact
             if (invalid) {
                 throw new SimpleParserException("Valid syntax: ${exception.OGNL} was: " + function, index);
             }
-            return SimpleExpressionBuilder.exchangeExceptionOgnlExpression(remainder);
+            return OgnlExpressionBuilder.exchangeExceptionOgnlExpression(remainder);
         }
 
         // exchangeProperty must be checked before exchange to avoid prefix clash
@@ -78,7 +78,7 @@ public final class ExchangeFunctionFactory implements SimpleLanguageFunctionFact
             }
 
             if (OgnlHelper.isValidOgnlExpression(remainder)) {
-                return SimpleExpressionBuilder.propertyOgnlExpression(remainder);
+                return OgnlExpressionBuilder.propertyOgnlExpression(remainder);
             } else {
                 return ExpressionBuilder.exchangePropertyExpression(remainder);
             }
@@ -91,7 +91,7 @@ public final class ExchangeFunctionFactory implements SimpleLanguageFunctionFact
             if (invalid) {
                 throw new SimpleParserException("Valid syntax: ${exchange.OGNL} was: " + function, index);
             }
-            return SimpleExpressionBuilder.exchangeOgnlExpression(remainder);
+            return OgnlExpressionBuilder.exchangeOgnlExpression(remainder);
         }
 
         return null;
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/HeaderFunctionFactory.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/HeaderFunctionFactory.java
index f1068ea009c1..661d97ca5719 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/HeaderFunctionFactory.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/HeaderFunctionFactory.java
@@ -20,7 +20,7 @@ import java.util.List;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.Expression;
-import org.apache.camel.language.simple.SimpleExpressionBuilder;
+import org.apache.camel.language.simple.OgnlExpressionBuilder;
 import org.apache.camel.language.simple.types.SimpleParserException;
 import org.apache.camel.spi.SimpleLanguageFunctionFactory;
 import org.apache.camel.support.builder.ExpressionBuilder;
@@ -85,7 +85,7 @@ public final class HeaderFunctionFactory implements SimpleLanguageFunctionFactor
             }
 
             if (OgnlHelper.isValidOgnlExpression(key)) {
-                return SimpleExpressionBuilder.headersOgnlExpression(key);
+                return OgnlExpressionBuilder.headersOgnlExpression(key);
             } else {
                 return ExpressionBuilder.headerExpression(key);
             }
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/JoinFunctionFactory.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/JoinFunctionFactory.java
index 7032da44246d..865ad431f5f9 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/JoinFunctionFactory.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/JoinFunctionFactory.java
@@ -18,7 +18,7 @@ package org.apache.camel.language.simple.functions;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.Expression;
-import org.apache.camel.language.simple.SimpleExpressionBuilder;
+import org.apache.camel.language.simple.CollectionExpressionBuilder;
 import org.apache.camel.language.simple.SimpleFunctionHelper;
 import org.apache.camel.language.simple.types.SimpleParserException;
 import org.apache.camel.spi.SimpleLanguageFunctionFactory;
@@ -61,7 +61,7 @@ public final class JoinFunctionFactory implements SimpleLanguageFunctionFactory
                 separator = tokens[0];
             }
         }
-        return SimpleExpressionBuilder.joinExpression(exp, separator, prefix);
+        return CollectionExpressionBuilder.joinExpression(exp, separator, prefix);
     }
 
     @Override
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/MathFunctionFactory.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/MathFunctionFactory.java
index bbd3e1e97f4f..0aa72c65ccf4 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/MathFunctionFactory.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/MathFunctionFactory.java
@@ -20,7 +20,7 @@ import java.util.StringJoiner;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.Expression;
-import org.apache.camel.language.simple.SimpleExpressionBuilder;
+import org.apache.camel.language.simple.MathExpressionBuilder;
 import org.apache.camel.language.simple.types.SimpleParserException;
 import org.apache.camel.spi.SimpleLanguageFunctionFactory;
 import org.apache.camel.util.ObjectHelper;
@@ -43,36 +43,36 @@ public final class MathFunctionFactory implements SimpleLanguageFunctionFactory
         remainder = ifStartsWithReturnRemainder("abs(", function);
         if (remainder != null) {
             String value = StringHelper.beforeLast(remainder, ")");
-            return SimpleExpressionBuilder
-                    .absExpression(ObjectHelper.isNotEmpty(value) ? StringHelper.removeQuotes(value) : null);
+            return MathExpressionBuilder.absExpression(
+                    ObjectHelper.isNotEmpty(value) ? StringHelper.removeQuotes(value) : null);
         }
         remainder = ifStartsWithReturnRemainder("floor(", function);
         if (remainder != null) {
             String value = StringHelper.beforeLast(remainder, ")");
-            return SimpleExpressionBuilder.floorExpression(
+            return MathExpressionBuilder.floorExpression(
                     ObjectHelper.isNotEmpty(value) ? StringHelper.removeQuotes(value) : null);
         }
         remainder = ifStartsWithReturnRemainder("ceil(", function);
         if (remainder != null) {
             String value = StringHelper.beforeLast(remainder, ")");
-            return SimpleExpressionBuilder.ceilExpression(
+            return MathExpressionBuilder.ceilExpression(
                     ObjectHelper.isNotEmpty(value) ? StringHelper.removeQuotes(value) : null);
         }
         remainder = ifStartsWithReturnRemainder("sum(", function);
         if (remainder != null) {
-            return SimpleExpressionBuilder.sumExpression(parseVariadicTokens(remainder));
+            return MathExpressionBuilder.sumExpression(parseVariadicTokens(remainder));
         }
         remainder = ifStartsWithReturnRemainder("max(", function);
         if (remainder != null) {
-            return SimpleExpressionBuilder.maxExpression(parseVariadicTokens(remainder));
+            return MathExpressionBuilder.maxExpression(parseVariadicTokens(remainder));
         }
         remainder = ifStartsWithReturnRemainder("min(", function);
         if (remainder != null) {
-            return SimpleExpressionBuilder.minExpression(parseVariadicTokens(remainder));
+            return MathExpressionBuilder.minExpression(parseVariadicTokens(remainder));
         }
         remainder = ifStartsWithReturnRemainder("average(", function);
         if (remainder != null) {
-            return SimpleExpressionBuilder.averageExpression(parseVariadicTokens(remainder));
+            return MathExpressionBuilder.averageExpression(parseVariadicTokens(remainder));
         }
 
         return null;
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/MessageFunctionFactory.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/MessageFunctionFactory.java
index 9d6c0ff6e7cd..1afa17c3088e 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/MessageFunctionFactory.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/MessageFunctionFactory.java
@@ -18,7 +18,7 @@ package org.apache.camel.language.simple.functions;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.Expression;
-import org.apache.camel.language.simple.SimpleExpressionBuilder;
+import org.apache.camel.language.simple.OgnlExpressionBuilder;
 import org.apache.camel.language.simple.types.SimpleParserException;
 import org.apache.camel.spi.SimpleLanguageFunctionFactory;
 import org.apache.camel.support.builder.ExpressionBuilder;
@@ -48,7 +48,7 @@ public final class MessageFunctionFactory implements SimpleLanguageFunctionFacto
                 if (invalid) {
                     throw new SimpleParserException("Valid syntax: ${messageAs(type).OGNL} was: " + function, index);
                 }
-                return SimpleExpressionBuilder.messageOgnlExpression(type, remainder);
+                return OgnlExpressionBuilder.messageOgnlExpression(type, remainder);
             } else {
                 return ExpressionBuilder.messageExpression(type);
             }
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/MiscFunctionFactory.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/MiscFunctionFactory.java
index a173f147621e..ba72ad68ca3e 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/MiscFunctionFactory.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/MiscFunctionFactory.java
@@ -18,7 +18,7 @@ package org.apache.camel.language.simple.functions;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.Expression;
-import org.apache.camel.language.simple.SimpleExpressionBuilder;
+import org.apache.camel.language.simple.MiscExpressionBuilder;
 import org.apache.camel.language.simple.types.SimpleParserException;
 import org.apache.camel.spi.SimpleLanguageFunctionFactory;
 import org.apache.camel.util.ObjectHelper;
@@ -54,7 +54,7 @@ public final class MiscFunctionFactory implements SimpleLanguageFunctionFactory
             if (ObjectHelper.isNotEmpty(value)) {
                 exp = StringHelper.removeQuotes(value);
             }
-            return SimpleExpressionBuilder.isEmptyExpression(exp);
+            return MiscExpressionBuilder.isEmptyExpression(exp);
         }
 
         remainder = ifStartsWithReturnRemainder("isAlpha(", function);
@@ -64,7 +64,7 @@ public final class MiscFunctionFactory implements SimpleLanguageFunctionFactory
             if (ObjectHelper.isNotEmpty(value)) {
                 exp = StringHelper.removeQuotes(value);
             }
-            return SimpleExpressionBuilder.isAlphaExpression(exp);
+            return MiscExpressionBuilder.isAlphaExpression(exp);
         }
 
         remainder = ifStartsWithReturnRemainder("isAlphaNumeric(", function);
@@ -74,7 +74,7 @@ public final class MiscFunctionFactory implements SimpleLanguageFunctionFactory
             if (ObjectHelper.isNotEmpty(value)) {
                 exp = StringHelper.removeQuotes(value);
             }
-            return SimpleExpressionBuilder.isAlphaNumericExpression(exp);
+            return MiscExpressionBuilder.isAlphaNumericExpression(exp);
         }
 
         remainder = ifStartsWithReturnRemainder("isNumeric(", function);
@@ -84,7 +84,7 @@ public final class MiscFunctionFactory implements SimpleLanguageFunctionFactory
             if (ObjectHelper.isNotEmpty(value)) {
                 exp = StringHelper.removeQuotes(value);
             }
-            return SimpleExpressionBuilder.isNumericExpression(exp);
+            return MiscExpressionBuilder.isNumericExpression(exp);
         }
 
         remainder = ifStartsWithReturnRemainder("not(", function);
@@ -94,7 +94,7 @@ public final class MiscFunctionFactory implements SimpleLanguageFunctionFactory
             if (ObjectHelper.isNotEmpty(value)) {
                 exp = value;
             }
-            return SimpleExpressionBuilder.isNotPredicate(exp);
+            return MiscExpressionBuilder.isNotPredicate(exp);
         }
 
         remainder = ifStartsWithReturnRemainder("kindOfType(", function);
@@ -104,7 +104,7 @@ public final class MiscFunctionFactory implements SimpleLanguageFunctionFactory
             if (ObjectHelper.isNotEmpty(value)) {
                 exp = StringHelper.removeQuotes(value);
             }
-            return SimpleExpressionBuilder.kindOfTypeExpression(exp);
+            return MiscExpressionBuilder.kindOfTypeExpression(exp);
         }
 
         remainder = ifStartsWithReturnRemainder("throwException(", function);
@@ -127,7 +127,7 @@ public final class MiscFunctionFactory implements SimpleLanguageFunctionFactory
             } else {
                 msg = StringHelper.removeQuotes(values.trim());
             }
-            return SimpleExpressionBuilder.throwExceptionExpression(msg, type);
+            return MiscExpressionBuilder.throwExceptionExpression(msg, type);
         }
 
         remainder = ifStartsWithReturnRemainder("assert(", function);
@@ -140,7 +140,7 @@ public final class MiscFunctionFactory implements SimpleLanguageFunctionFactory
             if (tokens.length != 2) {
                 throw new SimpleParserException("Valid syntax: ${assert(exp,msg)} was: " + function, index);
             }
-            return SimpleExpressionBuilder.assertExpression(tokens[0], StringHelper.removeQuotes(tokens[1]));
+            return MiscExpressionBuilder.assertExpression(tokens[0], StringHelper.removeQuotes(tokens[1]));
         }
 
         remainder = ifStartsWithReturnRemainder("convertTo(", function);
@@ -170,9 +170,9 @@ public final class MiscFunctionFactory implements SimpleLanguageFunctionFactory
                     throw new SimpleParserException(
                             "Valid syntax: ${convertTo(type).OGNL} or ${convertTo(exp,type).OGNL} was: " + function, index);
                 }
-                return SimpleExpressionBuilder.convertToOgnlExpression(exp, type, remainder);
+                return MiscExpressionBuilder.convertToOgnlExpression(exp, type, remainder);
             } else {
-                return SimpleExpressionBuilder.convertToExpression(exp, type);
+                return MiscExpressionBuilder.convertToExpression(exp, type);
             }
         }
 
@@ -185,17 +185,17 @@ public final class MiscFunctionFactory implements SimpleLanguageFunctionFactory
             } else {
                 detailed = Boolean.parseBoolean(values);
             }
-            return SimpleExpressionBuilder.messageHistoryExpression(detailed);
+            return MiscExpressionBuilder.messageHistoryExpression(detailed);
         } else if (ObjectHelper.equal(function, "messageHistory")) {
-            return SimpleExpressionBuilder.messageHistoryExpression(true);
+            return MiscExpressionBuilder.messageHistoryExpression(true);
         }
 
         remainder = ifStartsWithReturnRemainder("uuid", function);
         if (remainder != null) {
             String values = StringHelper.between(remainder, "(", ")");
-            return SimpleExpressionBuilder.uuidExpression(values);
+            return MiscExpressionBuilder.uuidExpression(values);
         } else if (ObjectHelper.equal(function, "uuid")) {
-            return SimpleExpressionBuilder.uuidExpression(null);
+            return MiscExpressionBuilder.uuidExpression(null);
         }
 
         remainder = ifStartsWithReturnRemainder("hash(", function);
@@ -211,9 +211,9 @@ public final class MiscFunctionFactory implements SimpleLanguageFunctionFactory
                     throw new SimpleParserException(
                             "Valid syntax: ${hash(value,algorithm)} or ${hash(value)} was: " + function, index);
                 }
-                return SimpleExpressionBuilder.hashExpression(tokens[0].trim(), tokens[1].trim());
+                return MiscExpressionBuilder.hashExpression(tokens[0].trim(), tokens[1].trim());
             } else {
-                return SimpleExpressionBuilder.hashExpression(values.trim(), "SHA-256");
+                return MiscExpressionBuilder.hashExpression(values.trim(), "SHA-256");
             }
         }
 
@@ -223,7 +223,7 @@ public final class MiscFunctionFactory implements SimpleLanguageFunctionFactory
             if (ObjectHelper.isEmpty(value)) {
                 throw new SimpleParserException("Valid syntax: ${empty(<type>)} but was: " + function, index);
             }
-            return SimpleExpressionBuilder.newEmptyExpression(value);
+            return MiscExpressionBuilder.newEmptyExpression(value);
         }
 
         remainder = ifStartsWithReturnRemainder("newEmpty(", function);
@@ -232,7 +232,7 @@ public final class MiscFunctionFactory implements SimpleLanguageFunctionFactory
             if (ObjectHelper.isEmpty(value)) {
                 throw new SimpleParserException("Valid syntax: ${newEmpty(<type>)} but was: " + function, index);
             }
-            return SimpleExpressionBuilder.newEmptyExpression(value);
+            return MiscExpressionBuilder.newEmptyExpression(value);
         }
 
         remainder = ifStartsWithReturnRemainder("iif(", function);
@@ -247,7 +247,7 @@ public final class MiscFunctionFactory implements SimpleLanguageFunctionFactory
                 throw new SimpleParserException(
                         "Valid syntax: ${iif(predicate,trueExpression,falseExpression)} was: " + function, index);
             }
-            return SimpleExpressionBuilder.iifExpression(tokens[0].trim(), tokens[1].trim(), tokens[2].trim());
+            return MiscExpressionBuilder.iifExpression(tokens[0].trim(), tokens[1].trim(), tokens[2].trim());
         }
 
         remainder = ifStartsWithReturnRemainder("load(", function);
@@ -256,7 +256,7 @@ public final class MiscFunctionFactory implements SimpleLanguageFunctionFactory
             if (ObjectHelper.isEmpty(value)) {
                 throw new SimpleParserException("Valid syntax: ${load(name)} but was: " + function, index);
             }
-            return SimpleExpressionBuilder.loadExpression(StringHelper.removeQuotes(value));
+            return MiscExpressionBuilder.loadExpression(StringHelper.removeQuotes(value));
         }
 
         return null;
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/QueryLanguageFunctionFactory.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/QueryLanguageFunctionFactory.java
index fe681c3a0c8c..9a6b4b67fc6e 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/QueryLanguageFunctionFactory.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/QueryLanguageFunctionFactory.java
@@ -18,7 +18,7 @@ package org.apache.camel.language.simple.functions;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.Expression;
-import org.apache.camel.language.simple.SimpleExpressionBuilder;
+import org.apache.camel.language.simple.MiscExpressionBuilder;
 import org.apache.camel.language.simple.types.SimpleParserException;
 import org.apache.camel.spi.SimpleLanguageFunctionFactory;
 import org.apache.camel.support.builder.ExpressionBuilder;
@@ -101,7 +101,7 @@ public final class QueryLanguageFunctionFactory implements SimpleLanguageFunctio
                 exp = exp.trim();
             }
         }
-        return SimpleExpressionBuilder.simpleJsonPathExpression(input, exp);
+        return MiscExpressionBuilder.simpleJsonPathExpression(input, exp);
     }
 
     private static boolean hasInputSource(String exp) {
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/RandomFunctionFactory.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/RandomFunctionFactory.java
index 136d1d48eb5a..70b8ed47985e 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/RandomFunctionFactory.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/RandomFunctionFactory.java
@@ -18,7 +18,7 @@ package org.apache.camel.language.simple.functions;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.Expression;
-import org.apache.camel.language.simple.SimpleExpressionBuilder;
+import org.apache.camel.language.simple.MiscExpressionBuilder;
 import org.apache.camel.language.simple.types.SimpleParserException;
 import org.apache.camel.spi.SimpleLanguageFunctionFactory;
 import org.apache.camel.util.ObjectHelper;
@@ -48,9 +48,9 @@ public final class RandomFunctionFactory implements SimpleLanguageFunctionFactor
                 throw new SimpleParserException(
                         "Valid syntax: ${random(min,max)} or ${random(max)} was: " + function, index);
             }
-            return SimpleExpressionBuilder.randomExpression(tokens[0].trim(), tokens[1].trim());
+            return MiscExpressionBuilder.randomExpression(tokens[0].trim(), tokens[1].trim());
         }
-        return SimpleExpressionBuilder.randomExpression("0", values.trim());
+        return MiscExpressionBuilder.randomExpression("0", values.trim());
     }
 
     @Override
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/SkipFunctionFactory.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/SkipFunctionFactory.java
index 25d8aa2862d3..0f5fede0d819 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/SkipFunctionFactory.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/SkipFunctionFactory.java
@@ -18,7 +18,7 @@ package org.apache.camel.language.simple.functions;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.Expression;
-import org.apache.camel.language.simple.SimpleExpressionBuilder;
+import org.apache.camel.language.simple.MiscExpressionBuilder;
 import org.apache.camel.language.simple.types.SimpleParserException;
 import org.apache.camel.spi.SimpleLanguageFunctionFactory;
 import org.apache.camel.util.ObjectHelper;
@@ -41,7 +41,7 @@ public final class SkipFunctionFactory implements SimpleLanguageFunctionFactory
         if (values == null || ObjectHelper.isEmpty(values)) {
             throw new SimpleParserException("Valid syntax: ${skip(number)} was: " + function, index);
         }
-        return SimpleExpressionBuilder.skipExpression("${body}", values.trim());
+        return MiscExpressionBuilder.skipExpression("${body}", values.trim());
     }
 
     @Override
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/StringFunctionFactory.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/StringFunctionFactory.java
index 442c9be46086..7090b4b488a8 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/StringFunctionFactory.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/StringFunctionFactory.java
@@ -18,7 +18,7 @@ package org.apache.camel.language.simple.functions;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.Expression;
-import org.apache.camel.language.simple.SimpleExpressionBuilder;
+import org.apache.camel.language.simple.StringExpressionBuilder;
 import org.apache.camel.language.simple.types.SimpleParserException;
 import org.apache.camel.spi.SimpleLanguageFunctionFactory;
 import org.apache.camel.support.builder.ExpressionBuilder;
@@ -62,7 +62,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
             if (tokens.length == 3) {
                 exp = tokens[2];
             }
-            return SimpleExpressionBuilder.replaceExpression(exp, from, to);
+            return StringExpressionBuilder.replaceExpression(exp, from, to);
         }
 
         remainder = ifStartsWithReturnRemainder("substring(", function);
@@ -88,7 +88,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
             if (tokens.length == 3) {
                 exp = tokens[2];
             }
-            return SimpleExpressionBuilder.substringExpression(exp, num1, num2);
+            return StringExpressionBuilder.substringExpression(exp, num1, num2);
         }
 
         remainder = ifStartsWithReturnRemainder("substringBefore(", function);
@@ -111,7 +111,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
             } else {
                 before = tokens[0];
             }
-            return SimpleExpressionBuilder.substringBeforeExpression(exp1, before);
+            return StringExpressionBuilder.substringBeforeExpression(exp1, before);
         }
 
         remainder = ifStartsWithReturnRemainder("substringAfter(", function);
@@ -134,7 +134,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
             } else {
                 after = tokens[0];
             }
-            return SimpleExpressionBuilder.substringAfterExpression(exp1, after);
+            return StringExpressionBuilder.substringAfterExpression(exp1, after);
         }
 
         remainder = ifStartsWithReturnRemainder("substringBetween(", function);
@@ -164,7 +164,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
                 after = tokens[0];
                 before = tokens[1];
             }
-            return SimpleExpressionBuilder.substringBetweenExpression(exp1, after, before);
+            return StringExpressionBuilder.substringBetweenExpression(exp1, after, before);
         }
 
         remainder = ifStartsWithReturnRemainder("contains(", function);
@@ -187,7 +187,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
                 exp = tokens[0];
                 pattern = tokens[1];
             }
-            return SimpleExpressionBuilder.containsExpression(exp, pattern);
+            return StringExpressionBuilder.containsExpression(exp, pattern);
         }
 
         remainder = ifStartsWithReturnRemainder("trim(", function);
@@ -197,7 +197,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
             if (ObjectHelper.isNotEmpty(value)) {
                 exp = StringHelper.removeQuotes(value);
             }
-            return SimpleExpressionBuilder.trimExpression(exp);
+            return StringExpressionBuilder.trimExpression(exp);
         }
 
         remainder = ifStartsWithReturnRemainder("val(", function);
@@ -217,7 +217,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
             if (ObjectHelper.isNotEmpty(value)) {
                 exp = StringHelper.removeQuotes(value);
             }
-            return SimpleExpressionBuilder.capitalizeExpression(exp);
+            return StringExpressionBuilder.capitalizeExpression(exp);
         }
 
         remainder = ifStartsWithReturnRemainder("pad(", function);
@@ -241,7 +241,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
             if (tokens.length == 3) {
                 separator = StringHelper.removeQuotes(tokens[2]);
             }
-            return SimpleExpressionBuilder.padExpression(exp, len, separator);
+            return StringExpressionBuilder.padExpression(exp, len, separator);
         }
 
         remainder = ifStartsWithReturnRemainder("concat(", function);
@@ -272,7 +272,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
             } else {
                 exp2 = StringHelper.removeQuotes(values.trim());
             }
-            return SimpleExpressionBuilder.concatExpression(exp1, exp2, separator);
+            return StringExpressionBuilder.concatExpression(exp1, exp2, separator);
         }
 
         remainder = ifStartsWithReturnRemainder("quote(", function);
@@ -282,7 +282,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
             if (ObjectHelper.isNotEmpty(value)) {
                 exp = StringHelper.removeQuotes(value);
             }
-            return SimpleExpressionBuilder.quoteExpression(exp);
+            return StringExpressionBuilder.quoteExpression(exp);
         }
 
         remainder = ifStartsWithReturnRemainder("safeQuote(", function);
@@ -292,7 +292,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
             if (ObjectHelper.isNotEmpty(value)) {
                 exp = StringHelper.removeQuotes(value);
             }
-            return SimpleExpressionBuilder.safeQuoteExpression(exp);
+            return StringExpressionBuilder.safeQuoteExpression(exp);
         }
 
         remainder = ifStartsWithReturnRemainder("unquote(", function);
@@ -302,7 +302,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
             if (ObjectHelper.isNotEmpty(value)) {
                 exp = StringHelper.removeQuotes(value);
             }
-            return SimpleExpressionBuilder.unquoteExpression(exp);
+            return StringExpressionBuilder.unquoteExpression(exp);
         }
 
         remainder = ifStartsWithReturnRemainder("uppercase(", function);
@@ -312,7 +312,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
             if (ObjectHelper.isNotEmpty(value)) {
                 exp = StringHelper.removeQuotes(value);
             }
-            return SimpleExpressionBuilder.uppercaseExpression(exp);
+            return StringExpressionBuilder.uppercaseExpression(exp);
         }
 
         remainder = ifStartsWithReturnRemainder("lowercase(", function);
@@ -322,7 +322,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
             if (ObjectHelper.isNotEmpty(value)) {
                 exp = StringHelper.removeQuotes(value);
             }
-            return SimpleExpressionBuilder.lowercaseExpression(exp);
+            return StringExpressionBuilder.lowercaseExpression(exp);
         }
 
         remainder = ifStartsWithReturnRemainder("length(", function);
@@ -332,7 +332,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
             if (ObjectHelper.isNotEmpty(value)) {
                 exp = StringHelper.removeQuotes(value);
             }
-            return SimpleExpressionBuilder.lengthExpression(exp);
+            return StringExpressionBuilder.lengthExpression(exp);
         }
 
         remainder = ifStartsWithReturnRemainder("size(", function);
@@ -342,7 +342,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
             if (ObjectHelper.isNotEmpty(value)) {
                 exp = StringHelper.removeQuotes(value);
             }
-            return SimpleExpressionBuilder.sizeExpression(exp);
+            return StringExpressionBuilder.sizeExpression(exp);
         }
 
         remainder = ifStartsWithReturnRemainder("normalizeWhitespace(", function);
@@ -352,7 +352,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
             if (ObjectHelper.isNotEmpty(value)) {
                 exp = StringHelper.removeQuotes(value);
             }
-            return SimpleExpressionBuilder.normalizeWhitespaceExpression(exp);
+            return StringExpressionBuilder.normalizeWhitespaceExpression(exp);
         }
 
         return null;
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/TypeFunctionFactory.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/TypeFunctionFactory.java
index 09151c9f63a1..20ba9c5ceb19 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/TypeFunctionFactory.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/TypeFunctionFactory.java
@@ -18,7 +18,7 @@ package org.apache.camel.language.simple.functions;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.Expression;
-import org.apache.camel.language.simple.SimpleExpressionBuilder;
+import org.apache.camel.language.simple.MiscExpressionBuilder;
 import org.apache.camel.spi.SimpleLanguageFunctionFactory;
 
 import static org.apache.camel.language.simple.SimpleFunctionHelper.appendClass;
@@ -36,9 +36,9 @@ public final class TypeFunctionFactory implements SimpleLanguageFunctionFactory
     public Expression createFunction(CamelContext camelContext, String function, int index) {
         String remainder = ifStartsWithReturnRemainder("type:", function);
         if (remainder != null) {
-            Expression exp = SimpleExpressionBuilder.typeExpression(remainder);
+            Expression exp = MiscExpressionBuilder.typeExpression(remainder);
             exp.init(camelContext);
-            return SimpleExpressionBuilder.cacheExpression(exp);
+            return MiscExpressionBuilder.cacheExpression(exp);
         }
         return null;
     }
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/VariableFunctionFactory.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/VariableFunctionFactory.java
index 368368a55885..f3cc90574dcf 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/VariableFunctionFactory.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/VariableFunctionFactory.java
@@ -18,7 +18,7 @@ package org.apache.camel.language.simple.functions;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.Expression;
-import org.apache.camel.language.simple.SimpleExpressionBuilder;
+import org.apache.camel.language.simple.OgnlExpressionBuilder;
 import org.apache.camel.language.simple.types.SimpleParserException;
 import org.apache.camel.spi.SimpleLanguageFunctionFactory;
 import org.apache.camel.support.builder.ExpressionBuilder;
@@ -82,7 +82,7 @@ public final class VariableFunctionFactory implements SimpleLanguageFunctionFact
             }
 
             if (OgnlHelper.isValidOgnlExpression(key)) {
-                return SimpleExpressionBuilder.variablesOgnlExpression(key);
+                return OgnlExpressionBuilder.variablesOgnlExpression(key);
             } else {
                 return ExpressionBuilder.variableExpression(key);
             }
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 5f55e5515849..980f182d453d 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
@@ -3680,15 +3680,20 @@ public class SimpleTest extends LanguageTestSupport {
 
     @Test
     public void testThrowException() {
+        // RuntimeException subclasses must propagate unwrapped so that typed onException handlers match.
+        // Pre-fix: the catch(Exception) block re-wrapped the just-thrown RuntimeException in a second
+        // RuntimeException, so e.getCause() held the intended exception instead of e itself.
         try {
             Expression expression = context.resolveLanguage("simple").createExpression("${throwException('Forced error')}");
             expression.evaluate(exchange, Object.class);
             fail();
         } catch (Exception e) {
-            assertIsInstanceOf(IllegalArgumentException.class, e.getCause());
-            assertEquals("Forced error", e.getCause().getMessage());
+            assertIsInstanceOf(IllegalArgumentException.class, e);
+            assertEquals("Forced error", e.getMessage());
         }
 
+        // Checked exceptions are wrapped in RuntimeCamelException (one layer, not two).
+        // Pre-fix: they were wrapped twice: RuntimeException(RuntimeCamelException(IOException)).
         try {
             Expression expression
                     = context.resolveLanguage("simple")
@@ -3696,8 +3701,8 @@ public class SimpleTest extends LanguageTestSupport {
             expression.evaluate(exchange, Object.class);
             fail();
         } catch (Exception e) {
-            assertIsInstanceOf(IOException.class, e.getCause().getCause());
-            assertEquals("Some IO error", e.getCause().getCause().getMessage());
+            assertIsInstanceOf(IOException.class, e.getCause());
+            assertEquals("Some IO error", e.getCause().getMessage());
         }
     }
 
diff --git a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/CollateFunctionFactoryTest.java b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/CollateFunctionFactoryTest.java
index 65d959725568..67f5cc0e80ad 100644
--- a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/CollateFunctionFactoryTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/CollateFunctionFactoryTest.java
@@ -120,6 +120,32 @@ public class CollateFunctionFactoryTest extends AbstractSimpleFunctionFactoryTes
         assertEquals("G", chunk3.get(0));
     }
 
+    @Test
+    public void testCollateDynamicEvaluatedTwiceProducesCorrectChunks() {
+        // pre-fix: collateExpression(String, String) overwrote the 'exp' field inside evaluate(),
+        // so a second call wrapped an already-grouped iterator instead of the raw body expression,
+        // producing iterators-of-iterators instead of list chunks.
+        List<Object> data = new ArrayList<>();
+        data.add("A");
+        data.add("B");
+        data.add("C");
+        data.add("D");
+        exchange.getIn().setBody(data);
+        exchange.getIn().setHeader("num", 2);
+
+        Iterator<?> first = (Iterator<?>) evaluate("collate(${header.num})");
+        assertEquals(List.of("A", "B"), first.next());
+        assertEquals(List.of("C", "D"), first.next());
+        assertFalse(first.hasNext());
+
+        // second evaluation on the same route exchange must produce the same shape
+        exchange.getIn().setBody(data);
+        Iterator<?> second = (Iterator<?>) evaluate("collate(${header.num})");
+        Object chunk = second.next();
+        assertFalse(chunk instanceof Iterator, "chunk must be a List of elements, not a nested iterator");
+        assertEquals(List.of("A", "B"), chunk);
+    }
+
     @Test
     public void testCreateCode() {
         assertEquals("collate(exchange, 10)", createCode("collate(10)"));
diff --git a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/CollectionFunctionFactoryTest.java b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/CollectionFunctionFactoryTest.java
index 87158d5fd1ca..b7d92467a6d6 100644
--- a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/CollectionFunctionFactoryTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/CollectionFunctionFactoryTest.java
@@ -25,6 +25,7 @@ import org.apache.camel.spi.SimpleLanguageFunctionFactory;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -93,7 +94,40 @@ public class CollectionFunctionFactoryTest extends AbstractSimpleFunctionFactory
 
     @Test
     public void testCreateCodeRangeMax() {
-        assertEquals("rangeList(exchange, 0, 3)", createCode("range(3)"));
+        // pre-fix: csimple generated rangeList(exchange, 0, N) while simple used min=1,
+        // causing ${range(N)} to produce different results in the two execution modes.
+        assertEquals("rangeList(exchange, 1, 3)", createCode("range(3)"));
+    }
+
+    // --- shuffle toString ---
+
+    @Test
+    public void testShuffleToStringReportsShuffleNotReverse() {
+        // pre-fix: shuffleExpression.toString() returned "reverse(...)" due to a copy-paste
+        // error; any error message or log showing the expression would be completely misleading.
+        org.apache.camel.Expression expr
+                = context.resolveLanguage("simple").createExpression("${shuffle(${body})}");
+        expr.init(context);
+        String repr = expr.toString();
+        assertTrue(repr.contains("shuffle"), "toString should contain 'shuffle', got: " + repr);
+        assertFalse(repr.startsWith("reverse"), "toString must not start with 'reverse', got: " + repr);
+    }
+
+    // --- map toString ---
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testMapToStringShowsSourcePairsNotCompiledExpressions() {
+        // pre-fix: mapExpression.toString() printed the compiled Expression[] array (JVM object refs)
+        // instead of the original String[] pairs, making diagnostic output useless.
+        org.apache.camel.Expression expr
+                = context.resolveLanguage("simple").createExpression("${map('k1','v1')}");
+        expr.init(context);
+        String repr = expr.toString();
+        assertTrue(repr.contains("k1"), "toString should contain source key 'k1', got: " + repr);
+        assertTrue(repr.contains("v1"), "toString should contain source value 'v1', got: " + repr);
+        assertFalse(repr.contains("ExpressionAdapter"),
+                "toString must not expose compiled objects, got: " + repr);
     }
 
     // --- distinct ---
diff --git a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/MathFunctionFactoryTest.java b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/MathFunctionFactoryTest.java
index f14dfc7a0d93..ad510eecdb13 100644
--- a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/MathFunctionFactoryTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/MathFunctionFactoryTest.java
@@ -45,12 +45,26 @@ public class MathFunctionFactoryTest extends AbstractSimpleFunctionFactoryTestSu
         assertEquals(3L, evaluate("floor(${body})", Long.class));
     }
 
+    @Test
+    public void testFloorLargeValueNoOverflow() {
+        // pre-fix: (int) Math.floor(3_000_000_000.9) silently overflowed to a negative int
+        exchange.getIn().setBody(3_000_000_000.9);
+        assertEquals(3_000_000_000L, evaluate("floor(${body})", Long.class));
+    }
+
     @Test
     public void testCeil() {
         exchange.getIn().setBody(3.2);
         assertEquals(4L, evaluate("ceil(${body})", Long.class));
     }
 
+    @Test
+    public void testCeilLargeValueNoOverflow() {
+        // pre-fix: (int) Math.ceil(2_147_483_648.0) silently overflowed to Integer.MIN_VALUE
+        exchange.getIn().setBody(2_147_483_648.0);
+        assertEquals(2_147_483_648L, evaluate("ceil(${body})", Long.class));
+    }
+
     @Test
     public void testSum() {
         assertEquals(6.0, evaluate("sum(1, 2, 3)", Double.class));
diff --git a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/MiscFunctionFactoryTest.java b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/MiscFunctionFactoryTest.java
index a5be49e28d1b..fd54f3d5e68b 100644
--- a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/MiscFunctionFactoryTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/MiscFunctionFactoryTest.java
@@ -27,6 +27,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertInstanceOf;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 public class MiscFunctionFactoryTest extends AbstractSimpleFunctionFactoryTestSupport {
@@ -153,6 +154,50 @@ public class MiscFunctionFactoryTest extends AbstractSimpleFunctionFactoryTestSu
         assertTrue(((Map<?, ?>) result).isEmpty());
     }
 
+    // --- throwException ---
+
+    @Test
+    public void testThrowExceptionDefaultTypeIsIllegalArgumentException() {
+        // pre-fix: catch(Exception e) around the entire body caught the RuntimeException that was
+        // just thrown and re-wrapped it in new RuntimeException(e), producing
+        // RuntimeException(IllegalArgumentException) instead of a bare IllegalArgumentException.
+        // onException(IllegalArgumentException.class) handlers would never match.
+        IllegalArgumentException ex = assertThrows(
+                IllegalArgumentException.class,
+                () -> evaluate("throwException('boom')"));
+        assertEquals("boom", ex.getMessage());
+    }
+
+    @Test
+    public void testThrowExceptionCustomRuntimeTypeIsNotDoubleWrapped() {
+        // A custom RuntimeException subclass must arrive unwrapped so that typed onException
+        // handlers can catch it. Pre-fix: it arrived wrapped in RuntimeException(CustomEx).
+        exchange.setProperty("type", "java.lang.IllegalStateException");
+        IllegalStateException ex = assertThrows(
+                IllegalStateException.class,
+                () -> evaluate("throwException('state failure',java.lang.IllegalStateException)"));
+        assertEquals("state failure", ex.getMessage());
+    }
+
+    // --- pad ---
+
+    @Test
+    public void testPadNullBodyReturnsNull() {
+        // pre-fix: answer.length() threw NullPointerException when the body was null
+        exchange.getIn().setBody(null);
+        assertNull(evaluate("pad(${body},5)"));
+    }
+
+    @Test
+    public void testPadNullWidthThrowsIllegalArgument() {
+        // pre-fix: int width = null unboxed to NullPointerException with no useful message
+        exchange.getIn().setBody("hi");
+        // header absent -> width expression evaluates to null
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> evaluate("pad(${body},${header.missingWidth})"));
+    }
+
     // --- iif ---
 
     @Test
diff --git a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/SkipFunctionFactoryTest.java b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/SkipFunctionFactoryTest.java
index 59e5a8a51d83..fa384bd304ed 100644
--- a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/SkipFunctionFactoryTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/SkipFunctionFactoryTest.java
@@ -70,6 +70,20 @@ public class SkipFunctionFactoryTest extends AbstractSimpleFunctionFactoryTestSu
         assertFalse(it.hasNext());
     }
 
+    @Test
+    public void testSkipDynamicNullNumberThrowsIllegalArgument() {
+        // pre-fix: int n = num.evaluate(…, Integer.class) unboxed null and threw NullPointerException
+        // with no useful message when the header was absent.
+        List<Object> data = new ArrayList<>();
+        data.add("A");
+        data.add("B");
+        exchange.getIn().setBody(data);
+        // header intentionally absent so the expression resolves to null
+        org.junit.jupiter.api.Assertions.assertThrows(
+                IllegalArgumentException.class,
+                () -> evaluate("skip(${header.missingNum})"));
+    }
+
     @Test
     public void testCreateCode() {
         assertEquals("skip(exchange, 10)", createCode("skip(10)"));
diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc
index a75f995c5473..702d78ff39a8 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc
@@ -20,6 +20,40 @@ It implies small differences listed https://github.com/whatap/java-grok#what-is-
 
 === camel-core
 
+==== Simple language: internal builder classes reorganized
+
+The internal class `org.apache.camel.language.simple.SimpleExpressionBuilder`
+(in `camel-core-languages`) has been removed. Its methods have been split across
+seven domain-aligned builder classes:
+
+- `CollectionExpressionBuilder`
+- `DateExpressionBuilder`
+- `FileExpressionBuilder`
+- `MathExpressionBuilder`
+- `MiscExpressionBuilder`
+- `OgnlExpressionBuilder`
+- `StringExpressionBuilder`
+
+These classes are all in the `org.apache.camel.language.simple` package and are
+internal API (not part of `camel-api`). Applications and custom DSLs that import
+`SimpleExpressionBuilder` directly must update their imports to the appropriate
+replacement class.
+
+==== Simple language: `floor`/`ceil` return `long` instead of `int`
+
+The `floor` and `ceil` functions in the Simple language previously silently
+overflowed for input values larger than `Integer.MAX_VALUE` because the result
+was cast to `int`. They now return `long`. Routes that pass the result of
+`floor`/`ceil` to a method that expects `int` may require an explicit cast or
+a converter step.
+
+==== Simple language: `range(N)` starts at 1, not 0
+
+In csimple, `range(N)` previously generated a range starting at 0 (i.e., 0..N-1).
+It now starts at 1 (i.e., 1..N), consistent with the `simple` language behaviour.
+Routes using csimple with `range(N)` that relied on the range starting at 0 must
+be updated.
+
 The `camel-api` module now has an optional dependency on `org.jspecify:jspecify` for null safety annotations
 (`@NullMarked`, `@Nullable`). The dependency is `<optional>true</optional>`, so it is not pulled transitively
 into your application. The annotations are used to document nullability contracts across the Camel API surface