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 26aff3ce211 CAMEL-21818: camel-core: Add list/map functions to 
simple/csimple languages
26aff3ce211 is described below

commit 26aff3ce211c73b634955b20d73a34b49b198e29
Author: Claus Ibsen <claus.ib...@gmail.com>
AuthorDate: Fri Feb 28 20:46:50 2025 +0100

    CAMEL-21818: camel-core: Add list/map functions to simple/csimple languages
---
 .../org/apache/camel/catalog/languages/simple.json | 20 +++---
 .../language/csimple/joor/OriginalSimpleTest.java  | 17 +++++
 .../org/apache/camel/language/simple/simple.json   | 20 +++---
 .../modules/languages/pages/simple-language.adoc   |  6 +-
 .../camel/language/csimple/CSimpleHelper.java      | 19 ++++++
 .../camel/language/simple/SimpleConstants.java     |  8 ++-
 .../language/simple/SimpleExpressionBuilder.java   | 78 ++++++++++++++++++++++
 .../simple/ast/SimpleFunctionExpression.java       | 60 +++++++++++++++++
 .../apache/camel/language/simple/SimpleTest.java   | 17 +++++
 9 files changed, 225 insertions(+), 20 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/simple.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/simple.json
index 86bacbf7bdd..dc32cebe45e 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/simple.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/simple.json
@@ -75,15 +75,17 @@
     "messageHistory(boolean)": { "index": 50, "kind": "function", 
"displayName": "Print Message History", "group": "function", "label": 
"function", "required": false, "javaType": "String", "prefix": "${", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The message history of the current exchange (how it has 
been routed). This is similar to the route stack-trace message history the 
error handler logs in case of an unhandled exception. The b [...]
     "uuid(type)": { "index": 51, "kind": "function", "displayName": "Generate 
UUID", "group": "function", "label": "function", "required": false, "javaType": 
"String", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns a UUID using the 
Camel `UuidGenerator`. You can choose between `default`, `classic`, `short` and 
`simple` as the type. If no type is given, the default is used. It is also 
possible to use a custom `UuidG [...]
     "hash(exp,algorithm)": { "index": 52, "kind": "function", "displayName": 
"Compute Hash Value", "group": "function", "label": "function", "required": 
false, "javaType": "String", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Returns a hashed value (string in hex decimal) of the message body\/expression 
using JDK MessageDigest. The algorithm can be SHA-256 (default) or SHA3-256.", 
"ognl": false, "suffix": "}" },
-    "empty(type)": { "index": 53, "kind": "function", "displayName": "Create 
Empty Object", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Creates a new empty 
object (decided by type). Use `string` to create an empty String. Use `list` to 
create an empty `java.util.ArrayList`. Use `map` to create an empty 
`java.util.HashMap`.", "ognl": false [...]
+    "empty(type)": { "index": 53, "kind": "function", "displayName": "Create 
Empty Object", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Creates a new empty 
object (decided by type). Use `string` to create an empty String. Use `list` to 
create an empty `java.util.ArrayList`. Use `map` to create an empty 
`java.util.LinkedHashMap`.", "ognl": [...]
     "iif(predicate,trueExp,falseExp)": { "index": 54, "kind": "function", 
"displayName": "If Then Else", "group": "function", "label": "function", 
"required": false, "javaType": "Object", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Evaluates the predicate and returns the value of trueExp or falseExp. This 
function is similar to the ternary operator in Java.", "ognl": false, "suffix": 
"}" },
-    "attachments": { "index": 55, "kind": "function", "displayName": 
"Attachments", "group": "function", "label": "function", "required": false, 
"javaType": "java.util.Map", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": "All 
the attachments as a Map<String,DataHandler.", "ognl": false, "suffix": "}" },
-    "attachments.size": { "index": 56, "kind": "function", "displayName": 
"Attachments", "group": "function", "label": "function", "required": false, 
"javaType": "int", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "The number of attachments. 
Is 0 if there are no attachments.", "ognl": false, "suffix": "}" },
-    "attachmentContentAsText": { "index": 57, "kind": "function", 
"displayName": "Attachment Content As Text", "group": "function", "label": 
"function", "required": false, "javaType": "String", "prefix": "${", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The content of the attachment as text (ie String).", 
"ognl": false, "suffix": "}" },
-    "attachmentContent": { "index": 58, "kind": "function", "displayName": 
"Attachment Content", "group": "function", "label": "function", "required": 
false, "javaType": "Object", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": "The 
content of the attachment", "ognl": false, "suffix": "}" },
-    "attachmentContentAs(type)": { "index": 59, "kind": "function", 
"displayName": "Attachment Content As", "group": "function", "label": 
"function", "required": false, "javaType": "Object", "prefix": "${", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The content of the attachment, converted to the given 
type.", "ognl": false, "suffix": "}" },
-    "attachmentHeader(key,name)": { "index": 60, "kind": "function", 
"displayName": "Attachment Header", "group": "function", "label": "function", 
"required": false, "javaType": "String", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": "The 
attachment header with the given name.", "ognl": false, "suffix": "}" },
-    "attachmentHeader(key,name,type)": { "index": 61, "kind": "function", 
"displayName": "Attachment Header", "group": "function", "label": "function", 
"required": false, "javaType": "Object", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": "The 
attachment header with the given name, converted to the given type.", "ognl": 
false, "suffix": "}" },
-    "attachment(key)": { "index": 62, "kind": "function", "displayName": 
"Attachment", "group": "function", "label": "function", "required": false, 
"javaType": "jakarta.activation.DataHandler", "prefix": "${", "deprecated": 
false, "deprecationNote": "", "autowired": false, "secret": false, 
"description": "The DataHandler for the given attachment.", "ognl": true, 
"suffix": "}" }
+    "list(val...)": { "index": 55, "kind": "function", "displayName": "Create 
List of values", "group": "function", "label": "function", "required": false, 
"javaType": "java.util.ArrayList", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": "The 
list function creates an ArrayList with the given set of values.", "ognl": 
false, "suffix": "}" },
+    "map(key1,value1,...)": { "index": 56, "kind": "function", "displayName": 
"Create Map of pairs", "group": "function", "label": "function", "required": 
false, "javaType": "java.util.LinkedHashMap", "prefix": "${", "deprecated": 
false, "deprecationNote": "", "autowired": false, "secret": false, 
"description": "The map function creates a LinkedHashMap with the given set of 
pairs.", "ognl": false, "suffix": "}" },
+    "attachments": { "index": 57, "kind": "function", "displayName": 
"Attachments", "group": "function", "label": "function", "required": false, 
"javaType": "java.util.Map", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": "All 
the attachments as a Map<String,DataHandler.", "ognl": false, "suffix": "}" },
+    "attachments.size": { "index": 58, "kind": "function", "displayName": 
"Attachments", "group": "function", "label": "function", "required": false, 
"javaType": "int", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "The number of attachments. 
Is 0 if there are no attachments.", "ognl": false, "suffix": "}" },
+    "attachmentContentAsText": { "index": 59, "kind": "function", 
"displayName": "Attachment Content As Text", "group": "function", "label": 
"function", "required": false, "javaType": "String", "prefix": "${", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The content of the attachment as text (ie String).", 
"ognl": false, "suffix": "}" },
+    "attachmentContent": { "index": 60, "kind": "function", "displayName": 
"Attachment Content", "group": "function", "label": "function", "required": 
false, "javaType": "Object", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": "The 
content of the attachment", "ognl": false, "suffix": "}" },
+    "attachmentContentAs(type)": { "index": 61, "kind": "function", 
"displayName": "Attachment Content As", "group": "function", "label": 
"function", "required": false, "javaType": "Object", "prefix": "${", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The content of the attachment, converted to the given 
type.", "ognl": false, "suffix": "}" },
+    "attachmentHeader(key,name)": { "index": 62, "kind": "function", 
"displayName": "Attachment Header", "group": "function", "label": "function", 
"required": false, "javaType": "String", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": "The 
attachment header with the given name.", "ognl": false, "suffix": "}" },
+    "attachmentHeader(key,name,type)": { "index": 63, "kind": "function", 
"displayName": "Attachment Header", "group": "function", "label": "function", 
"required": false, "javaType": "Object", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": "The 
attachment header with the given name, converted to the given type.", "ognl": 
false, "suffix": "}" },
+    "attachment(key)": { "index": 64, "kind": "function", "displayName": 
"Attachment", "group": "function", "label": "function", "required": false, 
"javaType": "jakarta.activation.DataHandler", "prefix": "${", "deprecated": 
false, "deprecationNote": "", "autowired": false, "secret": false, 
"description": "The DataHandler for the given attachment.", "ognl": true, 
"suffix": "}" }
   }
 }
diff --git 
a/components/camel-csimple-joor/src/test/java/org/apache/camel/language/csimple/joor/OriginalSimpleTest.java
 
b/components/camel-csimple-joor/src/test/java/org/apache/camel/language/csimple/joor/OriginalSimpleTest.java
index 68cb1ff109d..b81c45826d5 100644
--- 
a/components/camel-csimple-joor/src/test/java/org/apache/camel/language/csimple/joor/OriginalSimpleTest.java
+++ 
b/components/camel-csimple-joor/src/test/java/org/apache/camel/language/csimple/joor/OriginalSimpleTest.java
@@ -2002,6 +2002,23 @@ public class OriginalSimpleTest extends 
LanguageTestSupport {
         assertThrows(ExpressionEvaluationException.class, () -> 
evaluateExpression("${empty(unknownType)}", null));
     }
 
+    @Test
+    public void testList() {
+        exchange.getMessage().setBody("4");
+        assertExpression("${list(1,2,3)}", "[1, 2, 3]");
+        assertExpression("${list(1,2,3,${body})}", "[1, 2, 3, 4]");
+        assertExpression("${list('a','b','c')}", "[a, b, c]");
+        assertExpression("${list()}", "[]");
+    }
+
+    @Test
+    public void testMap() {
+        exchange.getMessage().setBody("d");
+        assertExpression("${map(1,'a',2,'b',3,'c')}", "{1=a, 2=b, 3=c}");
+        assertExpression("${map(1,'a',2,'b',3,'c',4,${body})}", "{1=a, 2=b, 
3=c, 4=d}");
+        assertExpression("${map()}", "{}");
+    }
+
     private void assertExpressionCreateNewEmpty(
             String type, Class<?> expectedClass, 
java.util.function.Predicate<Object> isEmptyAssertion) {
         Object value = evaluateExpression("${empty(%s)}".formatted(type), 
null);
diff --git 
a/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/simple/simple.json
 
b/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/simple/simple.json
index 86bacbf7bdd..dc32cebe45e 100644
--- 
a/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/simple/simple.json
+++ 
b/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/simple/simple.json
@@ -75,15 +75,17 @@
     "messageHistory(boolean)": { "index": 50, "kind": "function", 
"displayName": "Print Message History", "group": "function", "label": 
"function", "required": false, "javaType": "String", "prefix": "${", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The message history of the current exchange (how it has 
been routed). This is similar to the route stack-trace message history the 
error handler logs in case of an unhandled exception. The b [...]
     "uuid(type)": { "index": 51, "kind": "function", "displayName": "Generate 
UUID", "group": "function", "label": "function", "required": false, "javaType": 
"String", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns a UUID using the 
Camel `UuidGenerator`. You can choose between `default`, `classic`, `short` and 
`simple` as the type. If no type is given, the default is used. It is also 
possible to use a custom `UuidG [...]
     "hash(exp,algorithm)": { "index": 52, "kind": "function", "displayName": 
"Compute Hash Value", "group": "function", "label": "function", "required": 
false, "javaType": "String", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Returns a hashed value (string in hex decimal) of the message body\/expression 
using JDK MessageDigest. The algorithm can be SHA-256 (default) or SHA3-256.", 
"ognl": false, "suffix": "}" },
-    "empty(type)": { "index": 53, "kind": "function", "displayName": "Create 
Empty Object", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Creates a new empty 
object (decided by type). Use `string` to create an empty String. Use `list` to 
create an empty `java.util.ArrayList`. Use `map` to create an empty 
`java.util.HashMap`.", "ognl": false [...]
+    "empty(type)": { "index": 53, "kind": "function", "displayName": "Create 
Empty Object", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Creates a new empty 
object (decided by type). Use `string` to create an empty String. Use `list` to 
create an empty `java.util.ArrayList`. Use `map` to create an empty 
`java.util.LinkedHashMap`.", "ognl": [...]
     "iif(predicate,trueExp,falseExp)": { "index": 54, "kind": "function", 
"displayName": "If Then Else", "group": "function", "label": "function", 
"required": false, "javaType": "Object", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Evaluates the predicate and returns the value of trueExp or falseExp. This 
function is similar to the ternary operator in Java.", "ognl": false, "suffix": 
"}" },
-    "attachments": { "index": 55, "kind": "function", "displayName": 
"Attachments", "group": "function", "label": "function", "required": false, 
"javaType": "java.util.Map", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": "All 
the attachments as a Map<String,DataHandler.", "ognl": false, "suffix": "}" },
-    "attachments.size": { "index": 56, "kind": "function", "displayName": 
"Attachments", "group": "function", "label": "function", "required": false, 
"javaType": "int", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "The number of attachments. 
Is 0 if there are no attachments.", "ognl": false, "suffix": "}" },
-    "attachmentContentAsText": { "index": 57, "kind": "function", 
"displayName": "Attachment Content As Text", "group": "function", "label": 
"function", "required": false, "javaType": "String", "prefix": "${", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The content of the attachment as text (ie String).", 
"ognl": false, "suffix": "}" },
-    "attachmentContent": { "index": 58, "kind": "function", "displayName": 
"Attachment Content", "group": "function", "label": "function", "required": 
false, "javaType": "Object", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": "The 
content of the attachment", "ognl": false, "suffix": "}" },
-    "attachmentContentAs(type)": { "index": 59, "kind": "function", 
"displayName": "Attachment Content As", "group": "function", "label": 
"function", "required": false, "javaType": "Object", "prefix": "${", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The content of the attachment, converted to the given 
type.", "ognl": false, "suffix": "}" },
-    "attachmentHeader(key,name)": { "index": 60, "kind": "function", 
"displayName": "Attachment Header", "group": "function", "label": "function", 
"required": false, "javaType": "String", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": "The 
attachment header with the given name.", "ognl": false, "suffix": "}" },
-    "attachmentHeader(key,name,type)": { "index": 61, "kind": "function", 
"displayName": "Attachment Header", "group": "function", "label": "function", 
"required": false, "javaType": "Object", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": "The 
attachment header with the given name, converted to the given type.", "ognl": 
false, "suffix": "}" },
-    "attachment(key)": { "index": 62, "kind": "function", "displayName": 
"Attachment", "group": "function", "label": "function", "required": false, 
"javaType": "jakarta.activation.DataHandler", "prefix": "${", "deprecated": 
false, "deprecationNote": "", "autowired": false, "secret": false, 
"description": "The DataHandler for the given attachment.", "ognl": true, 
"suffix": "}" }
+    "list(val...)": { "index": 55, "kind": "function", "displayName": "Create 
List of values", "group": "function", "label": "function", "required": false, 
"javaType": "java.util.ArrayList", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": "The 
list function creates an ArrayList with the given set of values.", "ognl": 
false, "suffix": "}" },
+    "map(key1,value1,...)": { "index": 56, "kind": "function", "displayName": 
"Create Map of pairs", "group": "function", "label": "function", "required": 
false, "javaType": "java.util.LinkedHashMap", "prefix": "${", "deprecated": 
false, "deprecationNote": "", "autowired": false, "secret": false, 
"description": "The map function creates a LinkedHashMap with the given set of 
pairs.", "ognl": false, "suffix": "}" },
+    "attachments": { "index": 57, "kind": "function", "displayName": 
"Attachments", "group": "function", "label": "function", "required": false, 
"javaType": "java.util.Map", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": "All 
the attachments as a Map<String,DataHandler.", "ognl": false, "suffix": "}" },
+    "attachments.size": { "index": 58, "kind": "function", "displayName": 
"Attachments", "group": "function", "label": "function", "required": false, 
"javaType": "int", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "The number of attachments. 
Is 0 if there are no attachments.", "ognl": false, "suffix": "}" },
+    "attachmentContentAsText": { "index": 59, "kind": "function", 
"displayName": "Attachment Content As Text", "group": "function", "label": 
"function", "required": false, "javaType": "String", "prefix": "${", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The content of the attachment as text (ie String).", 
"ognl": false, "suffix": "}" },
+    "attachmentContent": { "index": 60, "kind": "function", "displayName": 
"Attachment Content", "group": "function", "label": "function", "required": 
false, "javaType": "Object", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": "The 
content of the attachment", "ognl": false, "suffix": "}" },
+    "attachmentContentAs(type)": { "index": 61, "kind": "function", 
"displayName": "Attachment Content As", "group": "function", "label": 
"function", "required": false, "javaType": "Object", "prefix": "${", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The content of the attachment, converted to the given 
type.", "ognl": false, "suffix": "}" },
+    "attachmentHeader(key,name)": { "index": 62, "kind": "function", 
"displayName": "Attachment Header", "group": "function", "label": "function", 
"required": false, "javaType": "String", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": "The 
attachment header with the given name.", "ognl": false, "suffix": "}" },
+    "attachmentHeader(key,name,type)": { "index": 63, "kind": "function", 
"displayName": "Attachment Header", "group": "function", "label": "function", 
"required": false, "javaType": "Object", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": "The 
attachment header with the given name, converted to the given type.", "ognl": 
false, "suffix": "}" },
+    "attachment(key)": { "index": 64, "kind": "function", "displayName": 
"Attachment", "group": "function", "label": "function", "required": false, 
"javaType": "jakarta.activation.DataHandler", "prefix": "${", "deprecated": 
false, "deprecationNote": "", "autowired": false, "secret": false, 
"description": "The DataHandler for the given attachment.", "ognl": true, 
"suffix": "}" }
   }
 }
diff --git 
a/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-language.adoc
 
b/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-language.adoc
index 3d9afef38d0..1ec24b1a428 100644
--- 
a/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-language.adoc
+++ 
b/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-language.adoc
@@ -234,7 +234,11 @@ constant field from Exchange as: 
`org.apache.camel.Exchange.FILE_NAME`
 
 `string` -> empty String +
 `list`   -> empty ArrayList +
-`map`    -> empty HashMap +
+`map`    -> empty LinkedHashMap +
+
+|list(val1,val2,...) | java.util.ArrayList | The list function creates an 
ArrayList with the given set of values.
+
+|map(key1,value1,...) | java.util.LinkedHashMap | The map function creates a 
LinkedHashMap with the given set of pairs.
 
 |null |null |represents a *null*
 
diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimpleHelper.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimpleHelper.java
index 428f79be280..1bf95915039 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimpleHelper.java
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimpleHelper.java
@@ -504,6 +504,25 @@ public final class CSimpleHelper {
         throw new IllegalArgumentException("function empty(%s) has unknown 
type".formatted(type));
     }
 
+    public static List<Object> list(Exchange exchange, Object... args) {
+        List<Object> answer = new ArrayList<>();
+        for (int i = 0; args != null && i < args.length; i++) {
+            answer.add(args[i]);
+        }
+        return answer;
+    }
+
+    public static Map<String, Object> map(Exchange exchange, Object... args) {
+        Map<String, Object> answer = new LinkedHashMap<>();
+        for (int i = 0, j = 0; args != null && i < args.length - 1; j++) {
+            String key = 
exchange.getContext().getTypeConverter().convertTo(String.class, exchange, 
args[i]);
+            Object value = args[i + 1];
+            answer.put(key, value);
+            i = i + 2;
+        }
+        return answer;
+    }
+
     public static String substring(Exchange exchange, Object num1, Object 
num2) {
         int head = 
exchange.getContext().getTypeConverter().tryConvertTo(int.class, exchange, 
num1);
         int tail = 
exchange.getContext().getTypeConverter().tryConvertTo(int.class, exchange, 
num2);
diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleConstants.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleConstants.java
index bbfb12048eb..cfed2884a95 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleConstants.java
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleConstants.java
@@ -206,12 +206,18 @@ public final class SimpleConstants {
     @Metadata(description = "Returns a hashed value (string in hex decimal) of 
the message body/expression using JDK MessageDigest. The algorithm can be 
SHA-256 (default) or SHA3-256.",
               label = "function", javaType = "String", displayName = "Compute 
Hash Value")
     public static final String HASH = "hash(exp,algorithm)";
-    @Metadata(description = "Creates a new empty object (decided by type). Use 
`string` to create an empty String. Use `list` to create an empty 
`java.util.ArrayList`. Use `map` to create an empty `java.util.HashMap`.",
+    @Metadata(description = "Creates a new empty object (decided by type). Use 
`string` to create an empty String. Use `list` to create an empty 
`java.util.ArrayList`. Use `map` to create an empty `java.util.LinkedHashMap`.",
               label = "function", javaType = "Object", displayName = "Create 
Empty Object")
     public static final String EMPTY = "empty(type)";
     @Metadata(description = "Evaluates the predicate and returns the value of 
trueExp or falseExp. This function is similar to the ternary operator in Java.",
               label = "function", javaType = "Object", displayName = "If Then 
Else")
     public static final String IIF = "iif(predicate,trueExp,falseExp)";
+    @Metadata(description = "The list function creates an ArrayList with the 
given set of values.",
+              label = "function", javaType = "java.util.ArrayList", 
displayName = "Create List of values")
+    public static final String LIST = "list(val...)";
+    @Metadata(description = "The map function creates a LinkedHashMap with the 
given set of pairs.",
+              label = "function", javaType = "java.util.LinkedHashMap", 
displayName = "Create Map of pairs")
+    public static final String MAP = "map(key1,value1,...)";
     @Metadata(description = "All the attachments as a 
Map<String,DataHandler.", javaType = "java.util.Map", label = "function")
     public static final String ATTACHMENTS = "attachments";
     @Metadata(description = "The number of attachments. Is 0 if there are no 
attachments.", javaType = "int",
diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionBuilder.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionBuilder.java
index e616167f8d1..0e583d41d80 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionBuilder.java
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionBuilder.java
@@ -18,10 +18,12 @@ package org.apache.camel.language.simple;
 
 import java.security.MessageDigest;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Date;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Random;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.regex.Pattern;
@@ -212,6 +214,82 @@ public final class SimpleExpressionBuilder {
         };
     }
 
+    /**
+     * 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
      */
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 55c6701ce6d..24127aab861 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,6 +21,7 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.StringJoiner;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.Expression;
@@ -891,6 +892,33 @@ 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, ',');
+            }
+            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, ',');
+            }
+            // 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);
+        }
+
         return null;
     }
 
@@ -1913,6 +1941,38 @@ public class SimpleFunctionExpression extends 
LiteralExpression {
             return "empty(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++) {
+                sj.add(tokens[i]);
+            }
+            String p = sj.length() > 0 ? sj.toString() : "null";
+            return "list(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++) {
+                sj.add(tokens[i]);
+            }
+            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/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 fbcc49b19a1..c6a2d27650a 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
@@ -2045,6 +2045,23 @@ public class SimpleTest extends LanguageTestSupport {
         assertExpression("${replace(&quot;,&apos;,${header.foo})}", "{'foo': 
'cheese'}");
     }
 
+    @Test
+    public void testList() {
+        exchange.getMessage().setBody("4");
+        assertExpression("${list(1,2,3)}", "[1, 2, 3]");
+        assertExpression("${list(1,2,3,${body})}", "[1, 2, 3, 4]");
+        assertExpression("${list('a','b','c')}", "[a, b, c]");
+        assertExpression("${list()}", "[]");
+    }
+
+    @Test
+    public void testMap() {
+        exchange.getMessage().setBody("d");
+        assertExpression("${map(1,a,2,b,3,c)}", "{1=a, 2=b, 3=c}");
+        assertExpression("${map(1,a,2,b,3,c,4,${body})}", "{1=a, 2=b, 3=c, 
4=d}");
+        assertExpression("${map()}", "{}");
+    }
+
     @Test
     public void testSubstringExpression() {
         exchange.getMessage().setBody("ABCDEFGHIJK");


Reply via email to