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 b32bc5e08467 CAMEL-22894: Extract collection functions into 
CollectionFunctionFactory (#23432)
b32bc5e08467 is described below

commit b32bc5e0846751cb6aaf5624aaf4e452a9317230
Author: Adriano Machado <[email protected]>
AuthorDate: Thu May 21 16:34:16 2026 -0400

    CAMEL-22894: Extract collection functions into CollectionFunctionFactory 
(#23432)
    
    * CAMEL-22894: Extract collection functions into CollectionFunctionFactory
    
    Moves setHeader, setVariable, range, distinct, reverse, shuffle, split,
    sort, forEach, filter, listAdd, listRemove, mapAdd, mapRemove, list, and
    map from the monolithic SimpleFunctionExpression into a dedicated
    CollectionFunctionFactory implementing SimpleLanguageFunctionFactory.
    
    Also removes the duplicate distinct block in createCodeExpressionMisc
    (dead code: it was unreachable because distinct was already handled
    earlier in the same method).
    
    Note: the range function has a pre-existing inconsistency between the
    expression path (min defaults to "1") and the code-gen path (min
    defaults to "0"); this patch preserves both behaviors unchanged.
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
    
    rh-pre-commit.version: 2.3.2
    rh-pre-commit.check-secrets: ENABLED
    
    * CAMEL-22894: Remove hallucinated unknown-function tests from factory test 
classes
    
    The testUnknownFunctionReturnsNull tests in each factory test class checked
    that a given factory returns null for a function belonging to a different
    factory. These tests encode an implementation detail of the dispatcher
    ordering rather than meaningful contract, and some were incorrect (the
    functions used as inputs are now handled by factories registered after the
    one under test, so null is no longer guaranteed at the factory level).
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
    
    rh-pre-commit.version: 2.3.2
    rh-pre-commit.check-secrets: ENABLED
---
 .../language/simple/SimpleFunctionDispatcher.java  |   4 +-
 .../simple/ast/SimpleFunctionExpression.java       | 596 ---------------------
 .../functions/CollectionFunctionFactory.java       | 538 +++++++++++++++++++
 .../simple/functions/BodyFunctionFactoryTest.java  |   7 -
 .../functions/CollectionFunctionFactoryTest.java   | 287 ++++++++++
 .../functions/HeaderFunctionFactoryTest.java       |   7 -
 .../simple/functions/MathFunctionFactoryTest.java  |   6 -
 .../functions/StringFunctionFactoryTest.java       |   9 -
 .../functions/VariableFunctionFactoryTest.java     |   7 -
 9 files changed, 828 insertions(+), 633 deletions(-)

diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleFunctionDispatcher.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleFunctionDispatcher.java
index cca342857ee1..2c76cc113d4e 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleFunctionDispatcher.java
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleFunctionDispatcher.java
@@ -23,6 +23,7 @@ import org.apache.camel.CamelContext;
 import org.apache.camel.Expression;
 import org.apache.camel.language.simple.functions.BodyFunctionFactory;
 import org.apache.camel.language.simple.functions.CollateFunctionFactory;
+import org.apache.camel.language.simple.functions.CollectionFunctionFactory;
 import org.apache.camel.language.simple.functions.HeaderFunctionFactory;
 import org.apache.camel.language.simple.functions.JoinFunctionFactory;
 import org.apache.camel.language.simple.functions.MathFunctionFactory;
@@ -61,7 +62,8 @@ public final class SimpleFunctionDispatcher {
             new CollateFunctionFactory(),
             new JoinFunctionFactory(),
             new MathFunctionFactory(),
-            new StringFunctionFactory());
+            new StringFunctionFactory(),
+            new CollectionFunctionFactory());
 
     private static final List<Entry> EXPRESSION_ENTRIES = List.of(
             new Entry("camel-attachments", 
SimpleFunctionDispatcher::isAttachmentFunction),
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 f0b291e5aaff..0719dacccf9d 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
@@ -17,10 +17,7 @@
 package org.apache.camel.language.simple.ast;
 
 import java.net.URISyntaxException;
-import java.util.Arrays;
 import java.util.Map;
-import java.util.StringJoiner;
-import java.util.stream.Collectors;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.Expression;
@@ -645,286 +642,6 @@ public class SimpleFunctionExpression extends 
LiteralExpression {
     private Expression createSimpleExpressionMisc(String function) {
         String remainder;
 
-        // setHeader function
-        remainder = ifStartsWithReturnRemainder("setHeader(", function);
-        if (remainder != null) {
-            String values = StringHelper.beforeLast(remainder, ")");
-            if (values == null || ObjectHelper.isEmpty(values)) {
-                throw new SimpleParserException(
-                        "Valid syntax: ${setHeader(name,exp)} or 
${setHeader(name,type,exp)} was: " + function,
-                        token.getIndex());
-            }
-            String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', 
false);
-            if (tokens.length < 2 || tokens.length > 3) {
-                throw new SimpleParserException(
-                        "Valid syntax: ${setHeader(name,exp)} or 
${setHeader(name,type,exp)} was: " + function,
-                        token.getIndex());
-            }
-            String name = tokens[0];
-            String exp;
-            String type = null;
-            if (tokens.length == 3) {
-                type = tokens[1];
-                exp = tokens[2];
-            } else {
-                exp = tokens[1];
-            }
-            type = StringHelper.removeQuotes(type);
-            return SimpleExpressionBuilder.setHeaderExpression(name, type, 
exp);
-        }
-        // setVariable function
-        remainder = ifStartsWithReturnRemainder("setVariable(", function);
-        if (remainder != null) {
-            String values = StringHelper.beforeLast(remainder, ")");
-            if (values == null || ObjectHelper.isEmpty(values)) {
-                throw new SimpleParserException(
-                        "Valid syntax: ${setVariable(name,exp)} or 
${setVariable(name,type,exp)} was: " + function,
-                        token.getIndex());
-            }
-            String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', 
false);
-            if (tokens.length < 2 || tokens.length > 3) {
-                throw new SimpleParserException(
-                        "Valid syntax: ${setVariable(name,exp)} or 
${setVariable(name,type,exp)} was: " + function,
-                        token.getIndex());
-            }
-            String name = tokens[0];
-            String exp;
-            String type = null;
-            if (tokens.length == 3) {
-                type = tokens[1];
-                exp = tokens[2];
-            } else {
-                exp = tokens[1];
-            }
-            type = StringHelper.removeQuotes(type);
-            return SimpleExpressionBuilder.setVariableExpression(name, type, 
exp);
-        }
-
-        // range function
-        remainder = ifStartsWithReturnRemainder("range(", function);
-        if (remainder != null) {
-            String values = StringHelper.beforeLast(remainder, ")");
-            if (values == null || ObjectHelper.isEmpty(values)) {
-                throw new SimpleParserException(
-                        "Valid syntax: ${range(min,max)} or ${range(max)} was: 
" + function, token.getIndex());
-            }
-            if (values.contains(",")) {
-                String[] tokens = values.split(",", 3);
-                if (tokens.length > 2) {
-                    throw new SimpleParserException(
-                            "Valid syntax: ${range(min,max)} or ${range(max)} 
was: " + function, token.getIndex());
-                }
-                return 
SimpleExpressionBuilder.rangeExpression(tokens[0].trim(), tokens[1].trim());
-            } else {
-                return SimpleExpressionBuilder.rangeExpression("1", 
values.trim());
-            }
-        }
-
-        // distinct function
-        remainder = ifStartsWithReturnRemainder("distinct(", function);
-        if (remainder != null) {
-            String values = StringHelper.beforeLast(remainder, ")");
-            String[] tokens;
-            if (ObjectHelper.isNotEmpty(values)) {
-                tokens = StringQuoteHelper.splitSafeQuote(values, ',', true, 
false);
-            } else {
-                tokens = new String[] { "${body}" };
-            }
-            return SimpleExpressionBuilder.distinctExpression(tokens);
-        }
-        // reverse function
-        remainder = ifStartsWithReturnRemainder("reverse(", function);
-        if (remainder != null) {
-            String values = StringHelper.beforeLast(remainder, ")");
-            String[] tokens;
-            if (ObjectHelper.isNotEmpty(values)) {
-                tokens = StringQuoteHelper.splitSafeQuote(values, ',', true, 
false);
-            } else {
-                tokens = new String[] { "${body}" };
-            }
-            return SimpleExpressionBuilder.reverseExpression(tokens);
-        }
-        // shuffle function
-        remainder = ifStartsWithReturnRemainder("shuffle(", function);
-        if (remainder != null) {
-            String values = StringHelper.beforeLast(remainder, ")");
-            String[] tokens;
-            if (ObjectHelper.isNotEmpty(values)) {
-                tokens = StringQuoteHelper.splitSafeQuote(values, ',', true, 
false);
-            } else {
-                tokens = new String[] { "${body}" };
-            }
-            return SimpleExpressionBuilder.shuffleExpression(tokens);
-        }
-        // split function
-        remainder = ifStartsWithReturnRemainder("split(", function);
-        if (remainder != null) {
-            String values = StringHelper.beforeLast(remainder, ")");
-            String exp = "${body}";
-            String separator = ",";
-            if (ObjectHelper.isNotEmpty(values)) {
-                String[] tokens = StringQuoteHelper.splitSafeQuote(values, 
',', false);
-                if (tokens.length > 2) {
-                    throw new SimpleParserException(
-                            "Valid syntax: ${split(separator)} or 
${split(exp,separator)} was: " + function, token.getIndex());
-                }
-                if (tokens.length == 2) {
-                    exp = tokens[0];
-                    separator = tokens[1];
-                } else {
-                    separator = tokens[0];
-                }
-            } else if ("\n".equals(values)) {
-                separator = values;
-            }
-            return SimpleExpressionBuilder.splitStringExpression(exp, 
separator);
-        }
-        // sort function
-        remainder = ifStartsWithReturnRemainder("sort(", function);
-        if (remainder != null) {
-            String values = StringHelper.beforeLast(remainder, ")");
-            String exp = "${body}";
-            boolean reverse = false;
-            if (ObjectHelper.isNotEmpty(values)) {
-                String[] tokens = StringQuoteHelper.splitSafeQuote(values, 
',', false);
-                if (tokens.length > 2) {
-                    throw new SimpleParserException(
-                            "Valid syntax: ${sort(reverse)} or 
${sort(exp,reverse)} was: " + function, token.getIndex());
-                }
-                if (tokens.length == 2) {
-                    exp = tokens[0];
-                    reverse = Boolean.parseBoolean(tokens[1]);
-                } else {
-                    reverse = Boolean.parseBoolean(tokens[0]);
-                }
-            }
-            return SimpleExpressionBuilder.sortExpression(exp, reverse);
-        }
-        // foreach function
-        remainder = ifStartsWithReturnRemainder("forEach(", function);
-        if (remainder != null) {
-            String values = StringHelper.beforeLast(remainder, ")");
-            if (ObjectHelper.isEmpty(values)) {
-                throw new SimpleParserException(
-                        "Valid syntax: ${forEach(exp,exp)} was: " + function, 
token.getIndex());
-            }
-            String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', 
false);
-            if (tokens.length < 2) {
-                throw new SimpleParserException(
-                        "Valid syntax: ${forEach(exp,exp)} was: " + function, 
token.getIndex());
-            }
-            String exp1 = tokens[0];
-            // the function takes the remainder of the tokens
-            String exp2 = 
Arrays.stream(tokens).skip(1).collect(Collectors.joining(","));
-            return SimpleExpressionBuilder.forEachExpression(exp1, exp2);
-        }
-        // filter function
-        remainder = ifStartsWithReturnRemainder("filter(", function);
-        if (remainder != null) {
-            String values = StringHelper.beforeLast(remainder, ")");
-            if (ObjectHelper.isEmpty(values)) {
-                throw new SimpleParserException(
-                        "Valid syntax: ${filter(exp,exp)} was: " + function, 
token.getIndex());
-            }
-            String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', 
false);
-            if (tokens.length < 2) {
-                throw new SimpleParserException(
-                        "Valid syntax: ${filter(exp,exp)} was: " + function, 
token.getIndex());
-            }
-            String exp1 = tokens[0];
-            // the function takes the remainder of the tokens
-            String exp2 = 
Arrays.stream(tokens).skip(1).collect(Collectors.joining(","));
-            return SimpleExpressionBuilder.filterExpression(exp1, exp2);
-        }
-        // listAdd function
-        remainder = ifStartsWithReturnRemainder("listAdd(", function);
-        if (remainder != null) {
-            String values = StringHelper.beforeLast(remainder, ")");
-            if (ObjectHelper.isEmpty(values)) {
-                throw new SimpleParserException(
-                        "Valid syntax: ${listAdd(exp)} or ${listAdd(exp,exp)} 
was: " + function, token.getIndex());
-            }
-            String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', 
false);
-            int skip = 0;
-            String exp1 = "${body}";
-            if (tokens.length > 1) {
-                skip = 1;
-                exp1 = tokens[0];
-            }
-            // the function takes the remainder of the tokens
-            String exp2 = 
Arrays.stream(tokens).skip(skip).collect(Collectors.joining(","));
-            return SimpleExpressionBuilder.listAddExpression(exp1, exp2);
-        }
-        // listRemove function
-        remainder = ifStartsWithReturnRemainder("listRemove(", function);
-        if (remainder != null) {
-            String values = StringHelper.beforeLast(remainder, ")");
-            if (ObjectHelper.isEmpty(values)) {
-                throw new SimpleParserException(
-                        "Valid syntax: ${listRemove(exp)} or 
${listRemove(exp,exp)} was: " + function, token.getIndex());
-            }
-            String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', 
false);
-            int skip = 0;
-            String exp1 = "${body}";
-            if (tokens.length > 1) {
-                skip = 1;
-                exp1 = tokens[0];
-            }
-            // the function takes the remainder of the tokens
-            String exp2 = 
Arrays.stream(tokens).skip(skip).collect(Collectors.joining(","));
-            return SimpleExpressionBuilder.listRemoveExpression(exp1, exp2);
-        }
-        // mapAdd function
-        remainder = ifStartsWithReturnRemainder("mapAdd(", function);
-        if (remainder != null) {
-            String values = StringHelper.beforeLast(remainder, ")");
-            if (ObjectHelper.isEmpty(values)) {
-                throw new SimpleParserException(
-                        "Valid syntax: ${mapAdd(key,exp)} or 
${mapAdd(exp,key,exp)} was: " + function, token.getIndex());
-            }
-            String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', 
false);
-            int skip;
-            String exp1 = "${body}";
-            String key;
-            if (tokens.length > 2) {
-                exp1 = tokens[0];
-                key = tokens[1];
-                skip = 2;
-            } else if (tokens.length == 2) {
-                key = tokens[0];
-                skip = 1;
-            } else {
-                throw new SimpleParserException(
-                        "Valid syntax: ${mapAdd(key,exp)} or 
${mapAdd(exp,key,exp)} was: " + function, token.getIndex());
-            }
-            // the function takes the remainder of the tokens
-            String exp2 = 
Arrays.stream(tokens).skip(skip).collect(Collectors.joining(","));
-            return SimpleExpressionBuilder.mapAddExpression(exp1, key, exp2);
-        }
-        // mapRemove function
-        remainder = ifStartsWithReturnRemainder("mapRemove(", function);
-        if (remainder != null) {
-            String values = StringHelper.beforeLast(remainder, ")");
-            if (ObjectHelper.isEmpty(values)) {
-                throw new SimpleParserException(
-                        "Valid syntax: ${mapRemove(key)} or 
${mapRemove(exp,key)} was: " + function, token.getIndex());
-            }
-            String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', 
false);
-            if (tokens.length > 2) {
-                throw new SimpleParserException(
-                        "Valid syntax: ${mapRemove(key)} or 
${mapRemove(exp,key)} was: " + function, token.getIndex());
-            }
-            String key;
-            String exp = "${body}";
-            if (tokens.length == 2) {
-                exp = tokens[0];
-                key = tokens[1];
-            } else {
-                key = tokens[0];
-            }
-            return SimpleExpressionBuilder.mapRemoveExpression(exp, key);
-        }
-
         // isEmpty function
         remainder = ifStartsWithReturnRemainder("isEmpty(", function);
         if (remainder != null) {
@@ -1152,33 +869,6 @@ public class SimpleFunctionExpression extends 
LiteralExpression {
             return SimpleExpressionBuilder.iifExpression(tokens[0].trim(), 
tokens[1].trim(), tokens[2].trim());
         }
 
-        // list function
-        remainder = ifStartsWithReturnRemainder("list(", function);
-        if (remainder != null) {
-            String values = StringHelper.beforeLast(remainder, ")");
-            String[] tokens = null;
-            if (ObjectHelper.isNotEmpty(values)) {
-                tokens = StringQuoteHelper.splitSafeQuote(values, ',', true, 
false);
-            }
-            return SimpleExpressionBuilder.listExpression(tokens);
-        }
-        // map function
-        remainder = ifStartsWithReturnRemainder("map(", function);
-        if (remainder != null) {
-            String values = StringHelper.beforeLast(remainder, ")");
-            String[] tokens = null;
-            if (ObjectHelper.isNotEmpty(values)) {
-                tokens = StringQuoteHelper.splitSafeQuote(values, ',', true, 
false);
-            }
-            // there must be an even number of tokens as each map element is a 
pair
-            if (tokens != null && tokens.length % 2 == 1) {
-                throw new SimpleParserException(
-                        "Map function must have an even number of values, was: 
" + tokens.length + " values.",
-                        token.getIndex());
-            }
-            return SimpleExpressionBuilder.mapExpression(tokens);
-        }
-
         // load function
         remainder = ifStartsWithReturnRemainder("load(", function);
         if (remainder != null) {
@@ -1614,227 +1304,6 @@ public class SimpleFunctionExpression extends 
LiteralExpression {
     private String createCodeExpressionMisc(CamelContext camelContext, String 
function) {
         String remainder;
 
-        // setHeader function
-        remainder = ifStartsWithReturnRemainder("setHeader(", function);
-        if (remainder != null) {
-            String values = StringHelper.beforeLast(remainder, ")");
-            if (values == null || ObjectHelper.isEmpty(values)) {
-                throw new SimpleParserException(
-                        "Valid syntax: ${setHeader(name,exp)} or 
${setHeader(name,type,exp)} was: " + function,
-                        token.getIndex());
-            }
-            String[] tokens = codeSplitSafe(values, ',', true, true);
-            if (tokens.length < 2 || tokens.length > 3) {
-                throw new SimpleParserException(
-                        "Valid syntax: ${setHeader(name,exp)} or 
${setHeader(name,type,exp)} was: " + function,
-                        token.getIndex());
-            }
-            // single quotes should be double quotes
-            for (int i = 1; i < tokens.length; i++) {
-                String s = tokens[i];
-                if (StringHelper.isSingleQuoted(s)) {
-                    s = StringHelper.removeLeadingAndEndingQuotes(s);
-                    s = StringQuoteHelper.doubleQuote(s);
-                    tokens[i] = s;
-                }
-            }
-            // name must be in double quote as its a string value
-            String name = StringHelper.removeLeadingAndEndingQuotes(tokens[0]);
-            name = StringQuoteHelper.doubleQuote(name);
-            String exp;
-            String type = null;
-            if (tokens.length == 3) {
-                type = tokens[1];
-                exp = tokens[2];
-            } else {
-                exp = tokens[1];
-            }
-            if (type != null) {
-                type = StringHelper.removeQuotes(type);
-                type = type.trim();
-                type = appendClass(type);
-                type = type.replace('$', '.');
-            } else {
-                type = "null";
-            }
-            return "Object value = " + exp + ";\n        return 
setHeader(exchange, " + name + ", " + type + ", value);";
-        }
-        // setVariable function
-        remainder = ifStartsWithReturnRemainder("setVariable(", function);
-        if (remainder != null) {
-            String values = StringHelper.beforeLast(remainder, ")");
-            if (values == null || ObjectHelper.isEmpty(values)) {
-                throw new SimpleParserException(
-                        "Valid syntax: ${setVariable(name,exp)} or 
${setVariable(name,type,exp)} was: " + function,
-                        token.getIndex());
-            }
-            String[] tokens = codeSplitSafe(values, ',', true, true);
-            if (tokens.length < 2 || tokens.length > 3) {
-                throw new SimpleParserException(
-                        "Valid syntax: ${setVariable(name,exp)} or 
${setVariable(name,type,exp)} was: " + function,
-                        token.getIndex());
-            }
-            // single quotes should be double quotes
-            for (int i = 1; i < tokens.length; i++) {
-                String s = tokens[i];
-                if (StringHelper.isSingleQuoted(s)) {
-                    s = StringHelper.removeLeadingAndEndingQuotes(s);
-                    s = StringQuoteHelper.doubleQuote(s);
-                    tokens[i] = s;
-                }
-            }
-            // name must be in double quote as its a string value
-            String name = StringHelper.removeLeadingAndEndingQuotes(tokens[0]);
-            name = StringQuoteHelper.doubleQuote(name);
-            String exp;
-            String type = null;
-            if (tokens.length == 3) {
-                type = tokens[1];
-                exp = tokens[2];
-            } else {
-                exp = tokens[1];
-            }
-            if (type != null) {
-                type = StringHelper.removeQuotes(type);
-                type = type.trim();
-                type = appendClass(type);
-                type = type.replace('$', '.');
-            } else {
-                type = "null";
-            }
-            return "Object value = " + exp + ";\n        return 
setVariable(exchange, " + name + ", " + type + ", value);";
-        }
-
-        // split function
-        remainder = ifStartsWithReturnRemainder("split(", function);
-        if (remainder != null) {
-            String values = StringHelper.beforeLast(remainder, ")");
-            String exp = "body";
-            String separator = ",";
-            if (ObjectHelper.isNotEmpty(values)) {
-                String[] tokens = codeSplitSafe(values, ',', true, true);
-                if (tokens.length > 2) {
-                    throw new SimpleParserException(
-                            "Valid syntax: ${split(separator)} or 
${split(exp,separator)} was: " + function, token.getIndex());
-                }
-                // single quotes should be double quotes
-                for (int i = 0; i < tokens.length; i++) {
-                    String s = tokens[i];
-                    if (StringHelper.isSingleQuoted(s)) {
-                        s = StringHelper.removeLeadingAndEndingQuotes(s);
-                        s = StringQuoteHelper.doubleQuote(s);
-                        tokens[i] = s;
-                    }
-                }
-                if (tokens.length == 2) {
-                    exp = tokens[0];
-                    separator = tokens[1];
-                } else {
-                    separator = tokens[0];
-                }
-            }
-            // separator must be in double quotes
-            separator = StringHelper.removeLeadingAndEndingQuotes(separator);
-            separator = StringQuoteHelper.doubleQuote(separator);
-            return "Object value = " + exp + ";\n        String separator = " 
+ separator
-                   + ";\n        return stringSplit(exchange, value, 
separator);";
-        }
-        // foreach function
-        remainder = ifStartsWithReturnRemainder("forEach(", function);
-        if (remainder != null) {
-            throw new UnsupportedOperationException("forEach is not supported 
in csimple language");
-        }
-        // filter function
-        remainder = ifStartsWithReturnRemainder("filter(", function);
-        if (remainder != null) {
-            throw new UnsupportedOperationException("filter is not supported 
in csimple language");
-        }
-
-        remainder = ifStartsWithReturnRemainder("range(", function);
-        if (remainder != null) {
-            String values = StringHelper.beforeLast(remainder, ")");
-            if (values == null || ObjectHelper.isEmpty(values)) {
-                throw new SimpleParserException(
-                        "Valid syntax: ${range(min,max)} or ${range(max)} was: 
" + function, token.getIndex());
-            }
-            if (values.contains(",")) {
-                String before = StringHelper.before(remainder, ",");
-                before = before.trim();
-                String after = StringHelper.after(remainder, ",");
-                after = after.trim();
-                if (after.endsWith(")")) {
-                    after = after.substring(0, after.length() - 1);
-                }
-                return "rangeList(exchange, " + before + ", " + after + ")";
-            } else {
-                return "rangeList(exchange, 0, " + values.trim() + ")";
-            }
-        }
-
-        // distinct function
-        remainder = ifStartsWithReturnRemainder("distinct(", function);
-        if (remainder != null) {
-            String values = StringHelper.beforeLast(remainder, ")");
-            String[] tokens = null;
-            if (ObjectHelper.isNotEmpty(values)) {
-                tokens = codeSplitSafe(values, ',', true, true);
-            }
-            StringJoiner sj = new StringJoiner(", ");
-            for (int i = 0; tokens != null && i < tokens.length; i++) {
-                String s = tokens[i];
-                // single quotes should be double quotes
-                if (StringHelper.isSingleQuoted(s)) {
-                    s = StringHelper.removeLeadingAndEndingQuotes(s);
-                    s = StringQuoteHelper.doubleQuote(s);
-                }
-                sj.add(s);
-            }
-            String p = sj.length() > 0 ? sj.toString() : "body";
-            return "distinct(exchange, " + p + ")";
-        }
-        // reverse function
-        remainder = ifStartsWithReturnRemainder("reverse(", function);
-        if (remainder != null) {
-            String values = StringHelper.beforeLast(remainder, ")");
-            String[] tokens = null;
-            if (ObjectHelper.isNotEmpty(values)) {
-                tokens = codeSplitSafe(values, ',', true, true);
-            }
-            StringJoiner sj = new StringJoiner(", ");
-            for (int i = 0; tokens != null && i < tokens.length; i++) {
-                String s = tokens[i];
-                // single quotes should be double quotes
-                if (StringHelper.isSingleQuoted(s)) {
-                    s = StringHelper.removeLeadingAndEndingQuotes(s);
-                    s = StringQuoteHelper.doubleQuote(s);
-                }
-                sj.add(s);
-            }
-            String p = sj.length() > 0 ? sj.toString() : "body";
-            return "reverse(exchange, " + p + ")";
-        }
-        // shuffle function
-        remainder = ifStartsWithReturnRemainder("shuffle(", function);
-        if (remainder != null) {
-            String values = StringHelper.beforeLast(remainder, ")");
-            String[] tokens = null;
-            if (ObjectHelper.isNotEmpty(values)) {
-                tokens = codeSplitSafe(values, ',', true, true);
-            }
-            StringJoiner sj = new StringJoiner(", ");
-            for (int i = 0; tokens != null && i < tokens.length; i++) {
-                String s = tokens[i];
-                // single quotes should be double quotes
-                if (StringHelper.isSingleQuoted(s)) {
-                    s = StringHelper.removeLeadingAndEndingQuotes(s);
-                    s = StringQuoteHelper.doubleQuote(s);
-                }
-                sj.add(s);
-            }
-            String p = sj.length() > 0 ? sj.toString() : "body";
-            return "shuffle(exchange, " + p + ")";
-        }
-
         // kindOfType function
         remainder = ifStartsWithReturnRemainder("kindOfType(", function);
         if (remainder != null) {
@@ -2101,71 +1570,6 @@ public class SimpleFunctionExpression extends 
LiteralExpression {
             return "newEmpty(exchange, " + value + ")";
         }
 
-        // list function
-        remainder = ifStartsWithReturnRemainder("list(", function);
-        if (remainder != null) {
-            String values = StringHelper.beforeLast(remainder, ")");
-            String[] tokens = null;
-            if (ObjectHelper.isNotEmpty(values)) {
-                tokens = codeSplitSafe(values, ',', true, true);
-            }
-            StringJoiner sj = new StringJoiner(", ");
-            for (int i = 0; tokens != null && i < tokens.length; i++) {
-                String s = tokens[i];
-                // single quotes should be double quotes
-                if (StringHelper.isSingleQuoted(s)) {
-                    s = StringHelper.removeLeadingAndEndingQuotes(s);
-                    s = StringQuoteHelper.doubleQuote(s);
-                }
-                sj.add(s);
-            }
-            String p = sj.length() > 0 ? sj.toString() : "null";
-            return "list(exchange, " + p + ")";
-        }
-        // distinct function
-        remainder = ifStartsWithReturnRemainder("distinct(", function);
-        if (remainder != null) {
-            String values = StringHelper.beforeLast(remainder, ")");
-            String[] tokens = null;
-            if (ObjectHelper.isNotEmpty(values)) {
-                tokens = codeSplitSafe(values, ',', true, true);
-            }
-            StringJoiner sj = new StringJoiner(", ");
-            for (int i = 0; tokens != null && i < tokens.length; i++) {
-                String s = tokens[i];
-                // single quotes should be double quotes
-                if (StringHelper.isSingleQuoted(s)) {
-                    s = StringHelper.removeLeadingAndEndingQuotes(s);
-                    s = StringQuoteHelper.doubleQuote(s);
-                }
-                sj.add(s);
-            }
-            String p = sj.length() > 0 ? sj.toString() : "body";
-            return "distinct(exchange, " + p + ")";
-        }
-
-        // map function
-        remainder = ifStartsWithReturnRemainder("map(", function);
-        if (remainder != null) {
-            String values = StringHelper.beforeLast(remainder, ")");
-            String[] tokens = null;
-            if (ObjectHelper.isNotEmpty(values)) {
-                tokens = codeSplitSafe(values, ',', true, true);
-            }
-            StringJoiner sj = new StringJoiner(", ");
-            for (int i = 0; tokens != null && i < tokens.length; i++) {
-                String s = tokens[i];
-                // single quotes should be double quotes
-                if (StringHelper.isSingleQuoted(s)) {
-                    s = StringHelper.removeLeadingAndEndingQuotes(s);
-                    s = StringQuoteHelper.doubleQuote(s);
-                }
-                sj.add(s);
-            }
-            String p = sj.length() > 0 ? sj.toString() : "null";
-            return "map(exchange, " + p + ")";
-        }
-
         // hash function
         remainder = ifStartsWithReturnRemainder("hash(", function);
         if (remainder != null) {
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
new file mode 100644
index 000000000000..f6a381413784
--- /dev/null
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/CollectionFunctionFactory.java
@@ -0,0 +1,538 @@
+/*
+ * 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.functions;
+
+import java.util.Arrays;
+import java.util.StringJoiner;
+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.types.SimpleParserException;
+import org.apache.camel.spi.SimpleLanguageFunctionFactory;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.StringHelper;
+import org.apache.camel.util.StringQuoteHelper;
+
+import static 
org.apache.camel.language.simple.SimpleFunctionHelper.appendClass;
+import static 
org.apache.camel.language.simple.SimpleFunctionHelper.codeSplitSafe;
+import static 
org.apache.camel.language.simple.SimpleFunctionHelper.ifStartsWithReturnRemainder;
+
+/**
+ * Built-in Simple collection functions: {@code ${setHeader}}, {@code 
${setVariable}}, {@code ${range}},
+ * {@code ${distinct}}, {@code ${reverse}}, {@code ${shuffle}}, {@code 
${split}}, {@code ${sort}}, {@code ${forEach}},
+ * {@code ${filter}}, {@code ${listAdd}}, {@code ${listRemove}}, {@code 
${mapAdd}}, {@code ${mapRemove}},
+ * {@code ${list}}, {@code ${map}}.
+ */
+public final class CollectionFunctionFactory implements 
SimpleLanguageFunctionFactory {
+
+    @Override
+    public Expression createFunction(CamelContext camelContext, String 
function, int index) {
+        String remainder;
+
+        remainder = ifStartsWithReturnRemainder("setHeader(", function);
+        if (remainder != null) {
+            String values = StringHelper.beforeLast(remainder, ")");
+            if (values == null || ObjectHelper.isEmpty(values)) {
+                throw new SimpleParserException(
+                        "Valid syntax: ${setHeader(name,exp)} or 
${setHeader(name,type,exp)} was: " + function, index);
+            }
+            String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', 
false);
+            if (tokens.length < 2 || tokens.length > 3) {
+                throw new SimpleParserException(
+                        "Valid syntax: ${setHeader(name,exp)} or 
${setHeader(name,type,exp)} was: " + function, index);
+            }
+            String name = tokens[0];
+            String exp;
+            String type = null;
+            if (tokens.length == 3) {
+                type = tokens[1];
+                exp = tokens[2];
+            } else {
+                exp = tokens[1];
+            }
+            type = StringHelper.removeQuotes(type);
+            return SimpleExpressionBuilder.setHeaderExpression(name, type, 
exp);
+        }
+
+        remainder = ifStartsWithReturnRemainder("setVariable(", function);
+        if (remainder != null) {
+            String values = StringHelper.beforeLast(remainder, ")");
+            if (values == null || ObjectHelper.isEmpty(values)) {
+                throw new SimpleParserException(
+                        "Valid syntax: ${setVariable(name,exp)} or 
${setVariable(name,type,exp)} was: " + function, index);
+            }
+            String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', 
false);
+            if (tokens.length < 2 || tokens.length > 3) {
+                throw new SimpleParserException(
+                        "Valid syntax: ${setVariable(name,exp)} or 
${setVariable(name,type,exp)} was: " + function, index);
+            }
+            String name = tokens[0];
+            String exp;
+            String type = null;
+            if (tokens.length == 3) {
+                type = tokens[1];
+                exp = tokens[2];
+            } else {
+                exp = tokens[1];
+            }
+            type = StringHelper.removeQuotes(type);
+            return SimpleExpressionBuilder.setVariableExpression(name, type, 
exp);
+        }
+
+        remainder = ifStartsWithReturnRemainder("range(", function);
+        if (remainder != null) {
+            String values = StringHelper.beforeLast(remainder, ")");
+            if (values == null || ObjectHelper.isEmpty(values)) {
+                throw new SimpleParserException(
+                        "Valid syntax: ${range(min,max)} or ${range(max)} was: 
" + function, index);
+            }
+            if (values.contains(",")) {
+                String[] tokens = values.split(",", 3);
+                if (tokens.length > 2) {
+                    throw new SimpleParserException(
+                            "Valid syntax: ${range(min,max)} or ${range(max)} 
was: " + function, index);
+                }
+                return 
SimpleExpressionBuilder.rangeExpression(tokens[0].trim(), tokens[1].trim());
+            } else {
+                return SimpleExpressionBuilder.rangeExpression("1", 
values.trim());
+            }
+        }
+
+        remainder = ifStartsWithReturnRemainder("distinct(", function);
+        if (remainder != null) {
+            String values = StringHelper.beforeLast(remainder, ")");
+            String[] tokens;
+            if (ObjectHelper.isNotEmpty(values)) {
+                tokens = StringQuoteHelper.splitSafeQuote(values, ',', true, 
false);
+            } else {
+                tokens = new String[] { "${body}" };
+            }
+            return SimpleExpressionBuilder.distinctExpression(tokens);
+        }
+
+        remainder = ifStartsWithReturnRemainder("reverse(", function);
+        if (remainder != null) {
+            String values = StringHelper.beforeLast(remainder, ")");
+            String[] tokens;
+            if (ObjectHelper.isNotEmpty(values)) {
+                tokens = StringQuoteHelper.splitSafeQuote(values, ',', true, 
false);
+            } else {
+                tokens = new String[] { "${body}" };
+            }
+            return SimpleExpressionBuilder.reverseExpression(tokens);
+        }
+
+        remainder = ifStartsWithReturnRemainder("shuffle(", function);
+        if (remainder != null) {
+            String values = StringHelper.beforeLast(remainder, ")");
+            String[] tokens;
+            if (ObjectHelper.isNotEmpty(values)) {
+                tokens = StringQuoteHelper.splitSafeQuote(values, ',', true, 
false);
+            } else {
+                tokens = new String[] { "${body}" };
+            }
+            return SimpleExpressionBuilder.shuffleExpression(tokens);
+        }
+
+        remainder = ifStartsWithReturnRemainder("split(", function);
+        if (remainder != null) {
+            String values = StringHelper.beforeLast(remainder, ")");
+            String exp = "${body}";
+            String separator = ",";
+            if (ObjectHelper.isNotEmpty(values)) {
+                String[] tokens = StringQuoteHelper.splitSafeQuote(values, 
',', false);
+                if (tokens.length > 2) {
+                    throw new SimpleParserException(
+                            "Valid syntax: ${split(separator)} or 
${split(exp,separator)} was: " + function, index);
+                }
+                if (tokens.length == 2) {
+                    exp = tokens[0];
+                    separator = tokens[1];
+                } else {
+                    separator = tokens[0];
+                }
+            } else if ("\n".equals(values)) {
+                separator = values;
+            }
+            return SimpleExpressionBuilder.splitStringExpression(exp, 
separator);
+        }
+
+        remainder = ifStartsWithReturnRemainder("sort(", function);
+        if (remainder != null) {
+            String values = StringHelper.beforeLast(remainder, ")");
+            String exp = "${body}";
+            boolean reverse = false;
+            if (ObjectHelper.isNotEmpty(values)) {
+                String[] tokens = StringQuoteHelper.splitSafeQuote(values, 
',', false);
+                if (tokens.length > 2) {
+                    throw new SimpleParserException(
+                            "Valid syntax: ${sort(reverse)} or 
${sort(exp,reverse)} was: " + function, index);
+                }
+                if (tokens.length == 2) {
+                    exp = tokens[0];
+                    reverse = Boolean.parseBoolean(tokens[1]);
+                } else {
+                    reverse = Boolean.parseBoolean(tokens[0]);
+                }
+            }
+            return SimpleExpressionBuilder.sortExpression(exp, reverse);
+        }
+
+        remainder = ifStartsWithReturnRemainder("forEach(", function);
+        if (remainder != null) {
+            String values = StringHelper.beforeLast(remainder, ")");
+            if (ObjectHelper.isEmpty(values)) {
+                throw new SimpleParserException(
+                        "Valid syntax: ${forEach(exp,exp)} was: " + function, 
index);
+            }
+            String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', 
false);
+            if (tokens.length < 2) {
+                throw new SimpleParserException(
+                        "Valid syntax: ${forEach(exp,exp)} was: " + function, 
index);
+            }
+            String exp1 = tokens[0];
+            String exp2 = 
Arrays.stream(tokens).skip(1).collect(Collectors.joining(","));
+            return SimpleExpressionBuilder.forEachExpression(exp1, exp2);
+        }
+
+        remainder = ifStartsWithReturnRemainder("filter(", function);
+        if (remainder != null) {
+            String values = StringHelper.beforeLast(remainder, ")");
+            if (ObjectHelper.isEmpty(values)) {
+                throw new SimpleParserException(
+                        "Valid syntax: ${filter(exp,exp)} was: " + function, 
index);
+            }
+            String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', 
false);
+            if (tokens.length < 2) {
+                throw new SimpleParserException(
+                        "Valid syntax: ${filter(exp,exp)} was: " + function, 
index);
+            }
+            String exp1 = tokens[0];
+            String exp2 = 
Arrays.stream(tokens).skip(1).collect(Collectors.joining(","));
+            return SimpleExpressionBuilder.filterExpression(exp1, exp2);
+        }
+
+        remainder = ifStartsWithReturnRemainder("listAdd(", function);
+        if (remainder != null) {
+            String values = StringHelper.beforeLast(remainder, ")");
+            if (ObjectHelper.isEmpty(values)) {
+                throw new SimpleParserException(
+                        "Valid syntax: ${listAdd(exp)} or ${listAdd(exp,exp)} 
was: " + function, index);
+            }
+            String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', 
false);
+            int skip = 0;
+            String exp1 = "${body}";
+            if (tokens.length > 1) {
+                skip = 1;
+                exp1 = tokens[0];
+            }
+            String exp2 = 
Arrays.stream(tokens).skip(skip).collect(Collectors.joining(","));
+            return SimpleExpressionBuilder.listAddExpression(exp1, exp2);
+        }
+
+        remainder = ifStartsWithReturnRemainder("listRemove(", function);
+        if (remainder != null) {
+            String values = StringHelper.beforeLast(remainder, ")");
+            if (ObjectHelper.isEmpty(values)) {
+                throw new SimpleParserException(
+                        "Valid syntax: ${listRemove(exp)} or 
${listRemove(exp,exp)} was: " + function, index);
+            }
+            String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', 
false);
+            int skip = 0;
+            String exp1 = "${body}";
+            if (tokens.length > 1) {
+                skip = 1;
+                exp1 = tokens[0];
+            }
+            String exp2 = 
Arrays.stream(tokens).skip(skip).collect(Collectors.joining(","));
+            return SimpleExpressionBuilder.listRemoveExpression(exp1, exp2);
+        }
+
+        remainder = ifStartsWithReturnRemainder("mapAdd(", function);
+        if (remainder != null) {
+            String values = StringHelper.beforeLast(remainder, ")");
+            if (ObjectHelper.isEmpty(values)) {
+                throw new SimpleParserException(
+                        "Valid syntax: ${mapAdd(key,exp)} or 
${mapAdd(exp,key,exp)} was: " + function, index);
+            }
+            String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', 
false);
+            int skip;
+            String exp1 = "${body}";
+            String key;
+            if (tokens.length > 2) {
+                exp1 = tokens[0];
+                key = tokens[1];
+                skip = 2;
+            } else if (tokens.length == 2) {
+                key = tokens[0];
+                skip = 1;
+            } else {
+                throw new SimpleParserException(
+                        "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);
+        }
+
+        remainder = ifStartsWithReturnRemainder("mapRemove(", function);
+        if (remainder != null) {
+            String values = StringHelper.beforeLast(remainder, ")");
+            if (ObjectHelper.isEmpty(values)) {
+                throw new SimpleParserException(
+                        "Valid syntax: ${mapRemove(key)} or 
${mapRemove(exp,key)} was: " + function, index);
+            }
+            String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', 
false);
+            if (tokens.length > 2) {
+                throw new SimpleParserException(
+                        "Valid syntax: ${mapRemove(key)} or 
${mapRemove(exp,key)} was: " + function, index);
+            }
+            String key;
+            String exp = "${body}";
+            if (tokens.length == 2) {
+                exp = tokens[0];
+                key = tokens[1];
+            } else {
+                key = tokens[0];
+            }
+            return SimpleExpressionBuilder.mapRemoveExpression(exp, key);
+        }
+
+        remainder = ifStartsWithReturnRemainder("list(", function);
+        if (remainder != null) {
+            String values = StringHelper.beforeLast(remainder, ")");
+            String[] tokens = null;
+            if (ObjectHelper.isNotEmpty(values)) {
+                tokens = StringQuoteHelper.splitSafeQuote(values, ',', true, 
false);
+            }
+            return SimpleExpressionBuilder.listExpression(tokens);
+        }
+
+        remainder = ifStartsWithReturnRemainder("map(", function);
+        if (remainder != null) {
+            String values = StringHelper.beforeLast(remainder, ")");
+            String[] tokens = null;
+            if (ObjectHelper.isNotEmpty(values)) {
+                tokens = StringQuoteHelper.splitSafeQuote(values, ',', true, 
false);
+            }
+            if (tokens != null && tokens.length % 2 == 1) {
+                throw new SimpleParserException(
+                        "Map function must have an even number of values, was: 
" + tokens.length + " values.", index);
+            }
+            return SimpleExpressionBuilder.mapExpression(tokens);
+        }
+
+        return null;
+    }
+
+    @Override
+    public String createCode(CamelContext camelContext, String function, int 
index) {
+        String remainder;
+
+        remainder = ifStartsWithReturnRemainder("setHeader(", function);
+        if (remainder != null) {
+            String values = StringHelper.beforeLast(remainder, ")");
+            if (values == null || ObjectHelper.isEmpty(values)) {
+                throw new SimpleParserException(
+                        "Valid syntax: ${setHeader(name,exp)} or 
${setHeader(name,type,exp)} was: " + function, index);
+            }
+            String[] tokens = codeSplitSafe(values, ',', true, true);
+            if (tokens.length < 2 || tokens.length > 3) {
+                throw new SimpleParserException(
+                        "Valid syntax: ${setHeader(name,exp)} or 
${setHeader(name,type,exp)} was: " + function, index);
+            }
+            for (int i = 1; i < tokens.length; i++) {
+                String s = tokens[i];
+                if (StringHelper.isSingleQuoted(s)) {
+                    s = StringHelper.removeLeadingAndEndingQuotes(s);
+                    s = StringQuoteHelper.doubleQuote(s);
+                    tokens[i] = s;
+                }
+            }
+            String name = StringHelper.removeLeadingAndEndingQuotes(tokens[0]);
+            name = StringQuoteHelper.doubleQuote(name);
+            String exp;
+            String type = null;
+            if (tokens.length == 3) {
+                type = tokens[1];
+                exp = tokens[2];
+            } else {
+                exp = tokens[1];
+            }
+            if (type != null) {
+                type = StringHelper.removeQuotes(type);
+                type = type.trim();
+                type = appendClass(type);
+                type = type.replace('$', '.');
+            } else {
+                type = "null";
+            }
+            return "Object value = " + exp + ";\n        return 
setHeader(exchange, " + name + ", " + type + ", value);";
+        }
+
+        remainder = ifStartsWithReturnRemainder("setVariable(", function);
+        if (remainder != null) {
+            String values = StringHelper.beforeLast(remainder, ")");
+            if (values == null || ObjectHelper.isEmpty(values)) {
+                throw new SimpleParserException(
+                        "Valid syntax: ${setVariable(name,exp)} or 
${setVariable(name,type,exp)} was: " + function, index);
+            }
+            String[] tokens = codeSplitSafe(values, ',', true, true);
+            if (tokens.length < 2 || tokens.length > 3) {
+                throw new SimpleParserException(
+                        "Valid syntax: ${setVariable(name,exp)} or 
${setVariable(name,type,exp)} was: " + function, index);
+            }
+            for (int i = 1; i < tokens.length; i++) {
+                String s = tokens[i];
+                if (StringHelper.isSingleQuoted(s)) {
+                    s = StringHelper.removeLeadingAndEndingQuotes(s);
+                    s = StringQuoteHelper.doubleQuote(s);
+                    tokens[i] = s;
+                }
+            }
+            String name = StringHelper.removeLeadingAndEndingQuotes(tokens[0]);
+            name = StringQuoteHelper.doubleQuote(name);
+            String exp;
+            String type = null;
+            if (tokens.length == 3) {
+                type = tokens[1];
+                exp = tokens[2];
+            } else {
+                exp = tokens[1];
+            }
+            if (type != null) {
+                type = StringHelper.removeQuotes(type);
+                type = type.trim();
+                type = appendClass(type);
+                type = type.replace('$', '.');
+            } else {
+                type = "null";
+            }
+            return "Object value = " + exp + ";\n        return 
setVariable(exchange, " + name + ", " + type + ", value);";
+        }
+
+        remainder = ifStartsWithReturnRemainder("split(", function);
+        if (remainder != null) {
+            String values = StringHelper.beforeLast(remainder, ")");
+            String exp = "body";
+            String separator = ",";
+            if (ObjectHelper.isNotEmpty(values)) {
+                String[] tokens = codeSplitSafe(values, ',', true, true);
+                if (tokens.length > 2) {
+                    throw new SimpleParserException(
+                            "Valid syntax: ${split(separator)} or 
${split(exp,separator)} was: " + function, index);
+                }
+                for (int i = 0; i < tokens.length; i++) {
+                    String s = tokens[i];
+                    if (StringHelper.isSingleQuoted(s)) {
+                        s = StringHelper.removeLeadingAndEndingQuotes(s);
+                        s = StringQuoteHelper.doubleQuote(s);
+                        tokens[i] = s;
+                    }
+                }
+                if (tokens.length == 2) {
+                    exp = tokens[0];
+                    separator = tokens[1];
+                } else {
+                    separator = tokens[0];
+                }
+            }
+            separator = StringHelper.removeLeadingAndEndingQuotes(separator);
+            separator = StringQuoteHelper.doubleQuote(separator);
+            return "Object value = " + exp + ";\n        String separator = " 
+ separator
+                   + ";\n        return stringSplit(exchange, value, 
separator);";
+        }
+
+        remainder = ifStartsWithReturnRemainder("forEach(", function);
+        if (remainder != null) {
+            throw new UnsupportedOperationException("forEach is not supported 
in csimple language");
+        }
+
+        remainder = ifStartsWithReturnRemainder("filter(", function);
+        if (remainder != null) {
+            throw new UnsupportedOperationException("filter is not supported 
in csimple language");
+        }
+
+        remainder = ifStartsWithReturnRemainder("range(", function);
+        if (remainder != null) {
+            String values = StringHelper.beforeLast(remainder, ")");
+            if (values == null || ObjectHelper.isEmpty(values)) {
+                throw new SimpleParserException(
+                        "Valid syntax: ${range(min,max)} or ${range(max)} was: 
" + function, index);
+            }
+            if (values.contains(",")) {
+                String before = StringHelper.before(remainder, ",");
+                before = before.trim();
+                String after = StringHelper.after(remainder, ",");
+                after = after.trim();
+                if (after.endsWith(")")) {
+                    after = after.substring(0, after.length() - 1);
+                }
+                return "rangeList(exchange, " + before + ", " + after + ")";
+            } else {
+                return "rangeList(exchange, 0, " + values.trim() + ")";
+            }
+        }
+
+        remainder = ifStartsWithReturnRemainder("distinct(", function);
+        if (remainder != null) {
+            return codeVariadicCollection("distinct", remainder, "body");
+        }
+
+        remainder = ifStartsWithReturnRemainder("reverse(", function);
+        if (remainder != null) {
+            return codeVariadicCollection("reverse", remainder, "body");
+        }
+
+        remainder = ifStartsWithReturnRemainder("shuffle(", function);
+        if (remainder != null) {
+            return codeVariadicCollection("shuffle", remainder, "body");
+        }
+
+        remainder = ifStartsWithReturnRemainder("list(", function);
+        if (remainder != null) {
+            return codeVariadicCollection("list", remainder, "null");
+        }
+
+        remainder = ifStartsWithReturnRemainder("map(", function);
+        if (remainder != null) {
+            return codeVariadicCollection("map", remainder, "null");
+        }
+
+        return null;
+    }
+
+    private static String codeVariadicCollection(String name, String 
remainder, String emptyDefault) {
+        String values = StringHelper.beforeLast(remainder, ")");
+        String[] tokens = null;
+        if (ObjectHelper.isNotEmpty(values)) {
+            tokens = codeSplitSafe(values, ',', true, true);
+        }
+        StringJoiner sj = new StringJoiner(", ");
+        for (int i = 0; tokens != null && i < tokens.length; i++) {
+            String s = tokens[i];
+            if (StringHelper.isSingleQuoted(s)) {
+                s = StringHelper.removeLeadingAndEndingQuotes(s);
+                s = StringQuoteHelper.doubleQuote(s);
+            }
+            sj.add(s);
+        }
+        String p = sj.length() > 0 ? sj.toString() : emptyDefault;
+        return name + "(exchange, " + p + ")";
+    }
+}
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/BodyFunctionFactoryTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/BodyFunctionFactoryTest.java
index e5b3643ab60e..05e77b242ff6 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/BodyFunctionFactoryTest.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/BodyFunctionFactoryTest.java
@@ -21,7 +21,6 @@ import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertNull;
 
 public class BodyFunctionFactoryTest extends 
AbstractSimpleFunctionFactoryTestSupport {
 
@@ -116,10 +115,4 @@ public class BodyFunctionFactoryTest extends 
AbstractSimpleFunctionFactoryTestSu
     public void testCreateCodeBodyOgnl() {
         assertEquals("body.getLength()", createCode("body.length"));
     }
-
-    @Test
-    public void testUnknownFunctionReturnsNull() {
-        assertNull(createFactory().createFunction(context, "random(10)", 0));
-        assertNull(createFactory().createCode(context, "random(10)", 0));
-    }
 }
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
new file mode 100644
index 000000000000..87158d5fd1ca
--- /dev/null
+++ 
b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/CollectionFunctionFactoryTest.java
@@ -0,0 +1,287 @@
+/*
+ * 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.functions;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+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.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class CollectionFunctionFactoryTest extends 
AbstractSimpleFunctionFactoryTestSupport {
+
+    @Override
+    protected SimpleLanguageFunctionFactory createFactory() {
+        return new CollectionFunctionFactory();
+    }
+
+    // --- setHeader ---
+
+    @Test
+    public void testSetHeader() {
+        exchange.getIn().setBody("hello");
+        evaluate("setHeader(myKey,${body})");
+        assertEquals("hello", exchange.getIn().getHeader("myKey"));
+    }
+
+    @Test
+    public void testCreateCodeSetHeader() {
+        assertEquals(
+                "Object value = body;\n        return setHeader(exchange, 
\"myKey\", null, value);",
+                createCode("setHeader(myKey,body)"));
+    }
+
+    // --- setVariable ---
+
+    @Test
+    public void testSetVariable() {
+        exchange.getIn().setBody("world");
+        evaluate("setVariable(myVar,${body})");
+        assertEquals("world", exchange.getVariable("myVar"));
+    }
+
+    @Test
+    public void testCreateCodeSetVariable() {
+        assertEquals(
+                "Object value = body;\n        return setVariable(exchange, 
\"myVar\", null, value);",
+                createCode("setVariable(myVar,body)"));
+    }
+
+    // --- range ---
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testRangeMinMax() {
+        // range is exclusive at the upper bound: range(1,5) -> [1,2,3,4]
+        List<Integer> result = evaluate("range(1,5)", List.class);
+        assertEquals(List.of(1, 2, 3, 4), result);
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testRangeMax() {
+        // range(max) uses min=1, exclusive upper: range(3) -> [1,2]
+        List<Integer> result = evaluate("range(3)", List.class);
+        assertEquals(List.of(1, 2), result);
+    }
+
+    @Test
+    public void testCreateCodeRangeMinMax() {
+        assertEquals("rangeList(exchange, 1, 5)", createCode("range(1,5)"));
+    }
+
+    @Test
+    public void testCreateCodeRangeMax() {
+        assertEquals("rangeList(exchange, 0, 3)", createCode("range(3)"));
+    }
+
+    // --- distinct ---
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testDistinct() {
+        exchange.getIn().setBody(List.of("a", "b", "a", "c", "b"));
+        List<String> result = evaluate("distinct()", List.class);
+        assertEquals(3, result.size());
+        assertTrue(result.containsAll(List.of("a", "b", "c")));
+    }
+
+    @Test
+    public void testCreateCodeDistinct() {
+        assertEquals("distinct(exchange, body)", createCode("distinct()"));
+    }
+
+    // --- reverse ---
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testReverse() {
+        exchange.getIn().setBody(List.of("a", "b", "c"));
+        List<String> result = evaluate("reverse()", List.class);
+        assertEquals(List.of("c", "b", "a"), result);
+    }
+
+    @Test
+    public void testCreateCodeReverse() {
+        assertEquals("reverse(exchange, body)", createCode("reverse()"));
+    }
+
+    // --- split ---
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testSplit() {
+        exchange.getIn().setBody("a:b:c");
+        List<String> result = evaluate("split(:)", List.class);
+        assertEquals(List.of("a", "b", "c"), result);
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testSplitWithExp() {
+        exchange.getIn().setHeader("csv", "x-y-z");
+        List<String> result = evaluate("split(${header.csv},-)", List.class);
+        assertEquals(List.of("x", "y", "z"), result);
+    }
+
+    @Test
+    public void testCreateCodeSplit() {
+        assertEquals(
+                "Object value = body;\n        String separator = \":\";\n     
   return stringSplit(exchange, value, separator);",
+                createCode("split(:)"));
+    }
+
+    // --- sort ---
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testSort() {
+        exchange.getIn().setBody(List.of("b", "a", "c"));
+        List<String> result = evaluate("sort()", List.class);
+        assertEquals(List.of("a", "b", "c"), result);
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testSortReverse() {
+        exchange.getIn().setBody(List.of("b", "a", "c"));
+        List<String> result = evaluate("sort(true)", List.class);
+        assertEquals(List.of("c", "b", "a"), result);
+    }
+
+    @Test
+    public void testCreateCodeSortReturnsNull() {
+        assertNull(createFactory().createCode(context, "sort()", 0));
+    }
+
+    // --- forEach ---
+
+    @Test
+    public void testCreateCodeForEachThrows() {
+        assertThrows(UnsupportedOperationException.class,
+                () -> createFactory().createCode(context, 
"forEach(${body},${body})", 0));
+    }
+
+    // --- filter ---
+
+    @Test
+    public void testCreateCodeFilterThrows() {
+        assertThrows(UnsupportedOperationException.class,
+                () -> createFactory().createCode(context, 
"filter(${body},${body})", 0));
+    }
+
+    // --- listAdd ---
+
+    @Test
+    public void testListAdd() {
+        List<String> list = new ArrayList<>(List.of("a", "b"));
+        exchange.getIn().setBody(list);
+        evaluate("listAdd(c)");
+        assertEquals(List.of("a", "b", "c"), 
exchange.getIn().getBody(List.class));
+    }
+
+    @Test
+    public void testCreateCodeListAddReturnsNull() {
+        assertNull(createFactory().createCode(context, "listAdd(c)", 0));
+    }
+
+    // --- listRemove ---
+
+    @Test
+    public void testListRemove() {
+        List<String> list = new ArrayList<>(List.of("a", "b", "c"));
+        exchange.getIn().setBody(list);
+        evaluate("listRemove(b)");
+        assertEquals(List.of("a", "c"), exchange.getIn().getBody(List.class));
+    }
+
+    @Test
+    public void testCreateCodeListRemoveReturnsNull() {
+        assertNull(createFactory().createCode(context, "listRemove(b)", 0));
+    }
+
+    // --- mapAdd ---
+
+    @Test
+    public void testMapAdd() {
+        Map<String, String> map = new HashMap<>();
+        exchange.getIn().setBody(map);
+        evaluate("mapAdd(key,val)");
+        assertEquals("val", exchange.getIn().getBody(Map.class).get("key"));
+    }
+
+    @Test
+    public void testCreateCodeMapAddReturnsNull() {
+        assertNull(createFactory().createCode(context, "mapAdd(key,val)", 0));
+    }
+
+    // --- mapRemove ---
+
+    @Test
+    public void testMapRemove() {
+        Map<String, String> map = new HashMap<>();
+        map.put("key", "val");
+        exchange.getIn().setBody(map);
+        evaluate("mapRemove(key)");
+        assertTrue(exchange.getIn().getBody(Map.class).isEmpty());
+    }
+
+    @Test
+    public void testCreateCodeMapRemoveReturnsNull() {
+        assertNull(createFactory().createCode(context, "mapRemove(key)", 0));
+    }
+
+    // --- list ---
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testList() {
+        List<String> result = evaluate("list('a','b','c')", List.class);
+        assertEquals(List.of("a", "b", "c"), result);
+    }
+
+    @Test
+    public void testCreateCodeList() {
+        assertEquals("list(exchange, null)", createCode("list()"));
+        assertEquals("list(exchange, \"a\", \"b\")", 
createCode("list('a','b')"));
+    }
+
+    // --- map ---
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testMap() {
+        Map<String, String> result = evaluate("map('k1','v1','k2','v2')", 
Map.class);
+        assertEquals(2, result.size());
+        assertEquals("v1", result.get("k1"));
+        assertEquals("v2", result.get("k2"));
+    }
+
+    @Test
+    public void testCreateCodeMap() {
+        assertEquals("map(exchange, null)", createCode("map()"));
+        assertEquals("map(exchange, \"k\", \"v\")", 
createCode("map('k','v')"));
+    }
+
+}
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/HeaderFunctionFactoryTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/HeaderFunctionFactoryTest.java
index 72b972a37ccb..a4341f6e4960 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/HeaderFunctionFactoryTest.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/HeaderFunctionFactoryTest.java
@@ -23,7 +23,6 @@ import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertNull;
 
 public class HeaderFunctionFactoryTest extends 
AbstractSimpleFunctionFactoryTestSupport {
 
@@ -87,10 +86,4 @@ public class HeaderFunctionFactoryTest extends 
AbstractSimpleFunctionFactoryTest
         assertEquals("headerAs(message, \"num\", Integer.class)", 
createCode("headerAs(num, Integer)"));
         assertEquals("headerAs(message, \"num\", java.lang.Integer.class)", 
createCode("headerAs(num, java.lang.Integer)"));
     }
-
-    @Test
-    public void testUnknownFunctionReturnsNull() {
-        assertNull(createFactory().createFunction(context, "body", 0));
-        assertNull(createFactory().createCode(context, "body", 0));
-    }
 }
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 b5a868368724..f14dfc7a0d93 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
@@ -20,7 +20,6 @@ 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.assertNull;
 
 public class MathFunctionFactoryTest extends 
AbstractSimpleFunctionFactoryTestSupport {
 
@@ -115,9 +114,4 @@ public class MathFunctionFactoryTest extends 
AbstractSimpleFunctionFactoryTestSu
         assertEquals("average(exchange, 1, 2, 3)", createCode("average(1, 2, 
3)"));
     }
 
-    @Test
-    public void testUnknownFunctionReturnsNull() {
-        assertNull(createFactory().createFunction(context, "trim()", 0));
-        assertNull(createFactory().createCode(context, "trim()", 0));
-    }
 }
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/StringFunctionFactoryTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/StringFunctionFactoryTest.java
index f9d76206bccc..bbb8df2318b5 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/StringFunctionFactoryTest.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/StringFunctionFactoryTest.java
@@ -22,7 +22,6 @@ 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.assertNull;
 
 public class StringFunctionFactoryTest extends 
AbstractSimpleFunctionFactoryTestSupport {
 
@@ -308,12 +307,4 @@ public class StringFunctionFactoryTest extends 
AbstractSimpleFunctionFactoryTest
         assertEquals("Object o = null;\n        return 
normalizeWhitespace(exchange, o);",
                 createCode("normalizeWhitespace()"));
     }
-
-    // --- unknown function ---
-
-    @Test
-    public void testUnknownFunctionReturnsNull() {
-        assertNull(createFactory().createFunction(context, "abs()", 0));
-        assertNull(createFactory().createCode(context, "abs()", 0));
-    }
 }
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/VariableFunctionFactoryTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/VariableFunctionFactoryTest.java
index 313ec8e4677a..813faccbb24c 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/VariableFunctionFactoryTest.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/VariableFunctionFactoryTest.java
@@ -23,7 +23,6 @@ import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertNull;
 
 public class VariableFunctionFactoryTest extends 
AbstractSimpleFunctionFactoryTestSupport {
 
@@ -77,10 +76,4 @@ public class VariableFunctionFactoryTest extends 
AbstractSimpleFunctionFactoryTe
         assertEquals("variableAs(exchange, \"num\", java.lang.Integer.class)",
                 createCode("variableAs(num, java.lang.Integer)"));
     }
-
-    @Test
-    public void testUnknownFunctionReturnsNull() {
-        assertNull(createFactory().createFunction(context, "body", 0));
-        assertNull(createFactory().createCode(context, "body", 0));
-    }
 }

Reply via email to