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 3a3d834f5313 substring between function (#20841)
3a3d834f5313 is described below
commit 3a3d834f5313d231caf3c030772cdeddad2c1983
Author: Claus Ibsen <[email protected]>
AuthorDate: Fri Jan 16 07:57:06 2026 +0100
substring between function (#20841)
* CAMEL-22762: Add substringBetween function to simple language. Make more
functions able to supported inlined functions.
---
.../org/apache/camel/catalog/languages/simple.json | 53 +++++-----
.../language/csimple/joor/OriginalSimpleTest.java | 80 +++++++++++++++
.../org/apache/camel/language/simple/simple.json | 53 +++++-----
.../modules/languages/pages/simple-language.adoc | 1 +
.../camel/language/csimple/CSimpleHelper.java | 10 ++
.../camel/language/simple/SimpleConstants.java | 3 +
.../language/simple/SimpleExpressionBuilder.java | 97 ++++++++++++++++++
.../simple/ast/SimpleFunctionExpression.java | 104 +++++++++++++++----
.../apache/camel/language/simple/SimpleTest.java | 110 +++++++++++++++++++++
9 files changed, 441 insertions(+), 70 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 dc9b059cb0ae..747358c55f1b 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
@@ -72,31 +72,32 @@
"substring(head,tail)": { "index": 46, "kind": "function", "displayName":
"Substring", "group": "function", "label": "function", "required": false,
"javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "description": "Returns a substring of
the message body\/expression. If only one positive number, then the returned
string is clipped from the beginning. If only one negative number, then the
returned string is clipped fr [...]
"substringBefore(exp,before)": { "index": 47, "kind": "function",
"displayName": "Substring Before", "group": "function", "label": "function",
"required": false, "javaType": "String", "prefix": "${", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false, "description":
"Returns a substring of the message body\/expression that comes before. Returns
null if nothing comes before.", "ognl": false, "suffix": "}" },
"substringAfter(exp,before)": { "index": 48, "kind": "function",
"displayName": "Substring After", "group": "function", "label": "function",
"required": false, "javaType": "String", "prefix": "${", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false, "description":
"Returns a substring of the message body\/expression that comes after. Returns
null if nothing comes after.", "ognl": false, "suffix": "}" },
- "random(min,max)": { "index": 49, "kind": "function", "displayName":
"Generate Random Number", "group": "function", "label": "function", "required":
false, "javaType": "int", "prefix": "${", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false, "description":
"Returns a random number between min (included) and max (excluded).", "ognl":
false, "suffix": "}" },
- "skip(num)": { "index": 50, "kind": "function", "displayName": "Skip First
Items from the Message Body", "group": "function", "label": "function",
"required": false, "javaType": "java.util.Iterator", "prefix": "${",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The skip function iterates the message body and skips
the first number of items. This can be used with the Splitter EIP to split a
message body and skip the first N number of [...]
- "convertTo(exp,type)": { "index": 51, "kind": "function", "displayName":
"Convert To", "group": "function", "label": "function", "required": false,
"javaType": "", "prefix": "${", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "Converts the message body
(or expression) to the specified type.", "ognl": true, "suffix": "}" },
- "trim(exp)": { "index": 52, "kind": "function", "displayName": "Trim",
"group": "function", "label": "function", "required": false, "javaType":
"String", "prefix": "${", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "The trim function trims
the message body (or expression) by removing all leading and trailing white
spaces.", "ognl": false, "suffix": "}" },
- "length(exp)": { "index": 53, "kind": "function", "displayName": "Length",
"group": "function", "label": "function", "required": false, "javaType": "int",
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "The payload length (number of bytes) of the
message body (or expression).", "ognl": false, "suffix": "}" },
- "size(exp)": { "index": 54, "kind": "function", "displayName": "Size",
"group": "function", "label": "function", "required": false, "javaType": "int",
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "The size of the message body (or expression).
If the payload is java.util.Collection or java.util.Map based then the size is
the number of elements; otherwise the payload size in bytes.", "ognl": false,
"suffix": "}" },
- "uppercase(exp)": { "index": 55, "kind": "function", "displayName":
"Uppercase", "group": "function", "label": "function", "required": false,
"javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "description": "Uppercases the message
body (or expression)", "ognl": false, "suffix": "}" },
- "lowercase(exp)": { "index": 56, "kind": "function", "displayName":
"Lowercase", "group": "function", "label": "function", "required": false,
"javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "description": "Lowercases the message
body (or expression)", "ognl": false, "suffix": "}" },
- "concat(exp,exp,separator)": { "index": 57, "kind": "function",
"displayName": "Concat", "group": "function", "label": "function", "required":
false, "javaType": "String", "prefix": "${", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false, "description":
"Performs a string concat using two expressions (message body as default) with
optional separator", "ognl": false, "suffix": "}" },
- "collate(num)": { "index": 58, "kind": "function", "displayName": "Group
Message Body into Sub Lists", "group": "function", "label": "function",
"required": false, "javaType": "java.util.Iterator", "prefix": "${",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The collate function iterates the message body and
groups the data into sub lists of specified size. This can be used with the
Splitter EIP to split a message body and group\/ba [...]
- "join(separator,prefix,exp)": { "index": 59, "kind": "function",
"displayName": "Join", "group": "function", "label": "function", "required":
false, "javaType": "String", "prefix": "${", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false, "description": "The
join function iterates the message body\/expression and joins the data into a
string. The separator is by default a comma. The prefix is optional. The join
uses the message body as source by default. [...]
- "messageHistory(boolean)": { "index": 60, "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": 61, "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": 62, "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": 63, "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": 64, "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":
"}" },
- "list(val...)": { "index": 65, "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": 66, "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": 67, "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": 68, "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": 69, "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": 70, "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": 71, "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": 72, "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": 73, "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": 74, "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": "}" }
+ "substringBetween(exp,after,before)": { "index": 49, "kind": "function",
"displayName": "Generate Random Number", "group": "function", "label":
"function", "required": false, "javaType": "int", "prefix": "${", "deprecated":
false, "deprecationNote": "", "autowired": false, "secret": false,
"description": "Returns a random number between min (included) and max
(excluded).", "ognl": false, "suffix": "}" },
+ "random(min,max)": { "index": 50, "kind": "function", "displayName":
"Random", "group": "function", "label": "function", "required": false,
"javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "description": "Returns a substring of
the message body\/expression that are between after and before. Returns null if
nothing comes between.", "ognl": false, "suffix": "}" },
+ "skip(num)": { "index": 51, "kind": "function", "displayName": "Skip First
Items from the Message Body", "group": "function", "label": "function",
"required": false, "javaType": "java.util.Iterator", "prefix": "${",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The skip function iterates the message body and skips
the first number of items. This can be used with the Splitter EIP to split a
message body and skip the first N number of [...]
+ "convertTo(exp,type)": { "index": 52, "kind": "function", "displayName":
"Convert To", "group": "function", "label": "function", "required": false,
"javaType": "", "prefix": "${", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "Converts the message body
(or expression) to the specified type.", "ognl": true, "suffix": "}" },
+ "trim(exp)": { "index": 53, "kind": "function", "displayName": "Trim",
"group": "function", "label": "function", "required": false, "javaType":
"String", "prefix": "${", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "The trim function trims
the message body (or expression) by removing all leading and trailing white
spaces.", "ognl": false, "suffix": "}" },
+ "length(exp)": { "index": 54, "kind": "function", "displayName": "Length",
"group": "function", "label": "function", "required": false, "javaType": "int",
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "The payload length (number of bytes) of the
message body (or expression).", "ognl": false, "suffix": "}" },
+ "size(exp)": { "index": 55, "kind": "function", "displayName": "Size",
"group": "function", "label": "function", "required": false, "javaType": "int",
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "The size of the message body (or expression).
If the payload is java.util.Collection or java.util.Map based then the size is
the number of elements; otherwise the payload size in bytes.", "ognl": false,
"suffix": "}" },
+ "uppercase(exp)": { "index": 56, "kind": "function", "displayName":
"Uppercase", "group": "function", "label": "function", "required": false,
"javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "description": "Uppercases the message
body (or expression)", "ognl": false, "suffix": "}" },
+ "lowercase(exp)": { "index": 57, "kind": "function", "displayName":
"Lowercase", "group": "function", "label": "function", "required": false,
"javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "description": "Lowercases the message
body (or expression)", "ognl": false, "suffix": "}" },
+ "concat(exp,exp,separator)": { "index": 58, "kind": "function",
"displayName": "Concat", "group": "function", "label": "function", "required":
false, "javaType": "String", "prefix": "${", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false, "description":
"Performs a string concat using two expressions (message body as default) with
optional separator", "ognl": false, "suffix": "}" },
+ "collate(num)": { "index": 59, "kind": "function", "displayName": "Group
Message Body into Sub Lists", "group": "function", "label": "function",
"required": false, "javaType": "java.util.Iterator", "prefix": "${",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The collate function iterates the message body and
groups the data into sub lists of specified size. This can be used with the
Splitter EIP to split a message body and group\/ba [...]
+ "join(separator,prefix,exp)": { "index": 60, "kind": "function",
"displayName": "Join", "group": "function", "label": "function", "required":
false, "javaType": "String", "prefix": "${", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false, "description": "The
join function iterates the message body\/expression and joins the data into a
string. The separator is by default a comma. The prefix is optional. The join
uses the message body as source by default. [...]
+ "messageHistory(boolean)": { "index": 61, "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": 62, "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": 63, "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": 64, "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": 65, "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":
"}" },
+ "list(val...)": { "index": 66, "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": 67, "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": 68, "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": 69, "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": 70, "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": 71, "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": 72, "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": 73, "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": 74, "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": 75, "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 a46a23f10f35..cb77d0af7756 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
@@ -1879,6 +1879,58 @@ public class OriginalSimpleTest extends
LanguageTestSupport {
assertEquals("G", chunk3.get(0));
}
+ @Test
+ public void testCollateDynamic() {
+ List<Object> data = new ArrayList<>();
+ data.add("A");
+ data.add("B");
+ data.add("C");
+ data.add("D");
+ data.add("E");
+ data.add("F");
+ data.add("G");
+ exchange.getIn().setBody(data);
+
+ exchange.getIn().setHeader("num", 3);
+
+ Iterator it = (Iterator)
evaluateExpression("${collate(${header.num})}", null);
+ List chunk = (List) it.next();
+ List chunk2 = (List) it.next();
+ List chunk3 = (List) it.next();
+ assertFalse(it.hasNext());
+
+ assertEquals(3, chunk.size());
+ assertEquals(3, chunk2.size());
+ assertEquals(1, chunk3.size());
+
+ assertEquals("A", chunk.get(0));
+ assertEquals("B", chunk.get(1));
+ assertEquals("C", chunk.get(2));
+ assertEquals("D", chunk2.get(0));
+ assertEquals("E", chunk2.get(1));
+ assertEquals("F", chunk2.get(2));
+ assertEquals("G", chunk3.get(0));
+ }
+
+ @Test
+ public void testSkip() {
+ List<Object> data = new ArrayList<>();
+ data.add("A");
+ data.add("B");
+ data.add("C");
+ data.add("D");
+ data.add("E");
+ data.add("F");
+ exchange.getIn().setBody(data);
+
+ Iterator it = (Iterator) evaluateExpression("${skip(2)}", null);
+ assertEquals("C", it.next());
+ assertEquals("D", it.next());
+ assertEquals("E", it.next());
+ assertEquals("F", it.next());
+ assertFalse(it.hasNext());
+ }
+
@Test
public void testRandomExpression() {
int min = 1;
@@ -2396,6 +2448,34 @@ public class OriginalSimpleTest extends
LanguageTestSupport {
assertEquals(" World", s);
}
+ @Test
+ public void testSubstringBetween() {
+ exchange.getMessage().setBody("Hello big great World");
+
+ Expression expression =
context.resolveLanguage("csimple").createExpression("${substringBetween('Hello','World')}");
+ String s = expression.evaluate(exchange, String.class);
+ assertEquals(" big great ", s);
+
+ expression =
context.resolveLanguage("csimple").createExpression("${substringBetween('Hello
',' World')}");
+ s = expression.evaluate(exchange, String.class);
+ assertEquals("big great", s);
+
+ expression =
context.resolveLanguage("csimple").createExpression("${substringBetween(${body},'big
',' World')}");
+ s = expression.evaluate(exchange, String.class);
+ assertEquals("great", s);
+
+ expression =
context.resolveLanguage("csimple").createExpression("${substringBetween('Hello','Unknown')}");
+ s = expression.evaluate(exchange, String.class);
+ assertNull(s);
+
+ exchange.getMessage().setHeader("place", "Hello");
+ exchange.getMessage().setHeader("place2", "great");
+ expression = context.resolveLanguage("csimple")
+
.createExpression("${substringBetween(${body},${header.place},${header.place2})}");
+ s = expression.evaluate(exchange, String.class);
+ assertEquals(" big ", s);
+ }
+
@Test
public void testTrim() {
exchange.getMessage().setBody(" Hello World ");
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 dc9b059cb0ae..747358c55f1b 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
@@ -72,31 +72,32 @@
"substring(head,tail)": { "index": 46, "kind": "function", "displayName":
"Substring", "group": "function", "label": "function", "required": false,
"javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "description": "Returns a substring of
the message body\/expression. If only one positive number, then the returned
string is clipped from the beginning. If only one negative number, then the
returned string is clipped fr [...]
"substringBefore(exp,before)": { "index": 47, "kind": "function",
"displayName": "Substring Before", "group": "function", "label": "function",
"required": false, "javaType": "String", "prefix": "${", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false, "description":
"Returns a substring of the message body\/expression that comes before. Returns
null if nothing comes before.", "ognl": false, "suffix": "}" },
"substringAfter(exp,before)": { "index": 48, "kind": "function",
"displayName": "Substring After", "group": "function", "label": "function",
"required": false, "javaType": "String", "prefix": "${", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false, "description":
"Returns a substring of the message body\/expression that comes after. Returns
null if nothing comes after.", "ognl": false, "suffix": "}" },
- "random(min,max)": { "index": 49, "kind": "function", "displayName":
"Generate Random Number", "group": "function", "label": "function", "required":
false, "javaType": "int", "prefix": "${", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false, "description":
"Returns a random number between min (included) and max (excluded).", "ognl":
false, "suffix": "}" },
- "skip(num)": { "index": 50, "kind": "function", "displayName": "Skip First
Items from the Message Body", "group": "function", "label": "function",
"required": false, "javaType": "java.util.Iterator", "prefix": "${",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The skip function iterates the message body and skips
the first number of items. This can be used with the Splitter EIP to split a
message body and skip the first N number of [...]
- "convertTo(exp,type)": { "index": 51, "kind": "function", "displayName":
"Convert To", "group": "function", "label": "function", "required": false,
"javaType": "", "prefix": "${", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "Converts the message body
(or expression) to the specified type.", "ognl": true, "suffix": "}" },
- "trim(exp)": { "index": 52, "kind": "function", "displayName": "Trim",
"group": "function", "label": "function", "required": false, "javaType":
"String", "prefix": "${", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "The trim function trims
the message body (or expression) by removing all leading and trailing white
spaces.", "ognl": false, "suffix": "}" },
- "length(exp)": { "index": 53, "kind": "function", "displayName": "Length",
"group": "function", "label": "function", "required": false, "javaType": "int",
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "The payload length (number of bytes) of the
message body (or expression).", "ognl": false, "suffix": "}" },
- "size(exp)": { "index": 54, "kind": "function", "displayName": "Size",
"group": "function", "label": "function", "required": false, "javaType": "int",
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "The size of the message body (or expression).
If the payload is java.util.Collection or java.util.Map based then the size is
the number of elements; otherwise the payload size in bytes.", "ognl": false,
"suffix": "}" },
- "uppercase(exp)": { "index": 55, "kind": "function", "displayName":
"Uppercase", "group": "function", "label": "function", "required": false,
"javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "description": "Uppercases the message
body (or expression)", "ognl": false, "suffix": "}" },
- "lowercase(exp)": { "index": 56, "kind": "function", "displayName":
"Lowercase", "group": "function", "label": "function", "required": false,
"javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "description": "Lowercases the message
body (or expression)", "ognl": false, "suffix": "}" },
- "concat(exp,exp,separator)": { "index": 57, "kind": "function",
"displayName": "Concat", "group": "function", "label": "function", "required":
false, "javaType": "String", "prefix": "${", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false, "description":
"Performs a string concat using two expressions (message body as default) with
optional separator", "ognl": false, "suffix": "}" },
- "collate(num)": { "index": 58, "kind": "function", "displayName": "Group
Message Body into Sub Lists", "group": "function", "label": "function",
"required": false, "javaType": "java.util.Iterator", "prefix": "${",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The collate function iterates the message body and
groups the data into sub lists of specified size. This can be used with the
Splitter EIP to split a message body and group\/ba [...]
- "join(separator,prefix,exp)": { "index": 59, "kind": "function",
"displayName": "Join", "group": "function", "label": "function", "required":
false, "javaType": "String", "prefix": "${", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false, "description": "The
join function iterates the message body\/expression and joins the data into a
string. The separator is by default a comma. The prefix is optional. The join
uses the message body as source by default. [...]
- "messageHistory(boolean)": { "index": 60, "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": 61, "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": 62, "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": 63, "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": 64, "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":
"}" },
- "list(val...)": { "index": 65, "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": 66, "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": 67, "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": 68, "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": 69, "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": 70, "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": 71, "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": 72, "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": 73, "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": 74, "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": "}" }
+ "substringBetween(exp,after,before)": { "index": 49, "kind": "function",
"displayName": "Generate Random Number", "group": "function", "label":
"function", "required": false, "javaType": "int", "prefix": "${", "deprecated":
false, "deprecationNote": "", "autowired": false, "secret": false,
"description": "Returns a random number between min (included) and max
(excluded).", "ognl": false, "suffix": "}" },
+ "random(min,max)": { "index": 50, "kind": "function", "displayName":
"Random", "group": "function", "label": "function", "required": false,
"javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "description": "Returns a substring of
the message body\/expression that are between after and before. Returns null if
nothing comes between.", "ognl": false, "suffix": "}" },
+ "skip(num)": { "index": 51, "kind": "function", "displayName": "Skip First
Items from the Message Body", "group": "function", "label": "function",
"required": false, "javaType": "java.util.Iterator", "prefix": "${",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The skip function iterates the message body and skips
the first number of items. This can be used with the Splitter EIP to split a
message body and skip the first N number of [...]
+ "convertTo(exp,type)": { "index": 52, "kind": "function", "displayName":
"Convert To", "group": "function", "label": "function", "required": false,
"javaType": "", "prefix": "${", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "Converts the message body
(or expression) to the specified type.", "ognl": true, "suffix": "}" },
+ "trim(exp)": { "index": 53, "kind": "function", "displayName": "Trim",
"group": "function", "label": "function", "required": false, "javaType":
"String", "prefix": "${", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "The trim function trims
the message body (or expression) by removing all leading and trailing white
spaces.", "ognl": false, "suffix": "}" },
+ "length(exp)": { "index": 54, "kind": "function", "displayName": "Length",
"group": "function", "label": "function", "required": false, "javaType": "int",
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "The payload length (number of bytes) of the
message body (or expression).", "ognl": false, "suffix": "}" },
+ "size(exp)": { "index": 55, "kind": "function", "displayName": "Size",
"group": "function", "label": "function", "required": false, "javaType": "int",
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "The size of the message body (or expression).
If the payload is java.util.Collection or java.util.Map based then the size is
the number of elements; otherwise the payload size in bytes.", "ognl": false,
"suffix": "}" },
+ "uppercase(exp)": { "index": 56, "kind": "function", "displayName":
"Uppercase", "group": "function", "label": "function", "required": false,
"javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "description": "Uppercases the message
body (or expression)", "ognl": false, "suffix": "}" },
+ "lowercase(exp)": { "index": 57, "kind": "function", "displayName":
"Lowercase", "group": "function", "label": "function", "required": false,
"javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "description": "Lowercases the message
body (or expression)", "ognl": false, "suffix": "}" },
+ "concat(exp,exp,separator)": { "index": 58, "kind": "function",
"displayName": "Concat", "group": "function", "label": "function", "required":
false, "javaType": "String", "prefix": "${", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false, "description":
"Performs a string concat using two expressions (message body as default) with
optional separator", "ognl": false, "suffix": "}" },
+ "collate(num)": { "index": 59, "kind": "function", "displayName": "Group
Message Body into Sub Lists", "group": "function", "label": "function",
"required": false, "javaType": "java.util.Iterator", "prefix": "${",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The collate function iterates the message body and
groups the data into sub lists of specified size. This can be used with the
Splitter EIP to split a message body and group\/ba [...]
+ "join(separator,prefix,exp)": { "index": 60, "kind": "function",
"displayName": "Join", "group": "function", "label": "function", "required":
false, "javaType": "String", "prefix": "${", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false, "description": "The
join function iterates the message body\/expression and joins the data into a
string. The separator is by default a comma. The prefix is optional. The join
uses the message body as source by default. [...]
+ "messageHistory(boolean)": { "index": 61, "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": 62, "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": 63, "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": 64, "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": 65, "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":
"}" },
+ "list(val...)": { "index": 66, "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": 67, "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": 68, "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": 69, "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": 70, "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": 71, "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": 72, "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": 73, "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": 74, "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": 75, "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 c883e34d2fb6..7fb4a3caaa20 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
@@ -270,6 +270,7 @@ If the number is negative, then the returned string is
clipped from the ending.
|substringBefore(exp,before) |String |Returns a substring of the message
body/expression that comes before. Returns null if nothing comes before.
|substringAfter(exp,before) |String |Returns a substring of the message
body/expression that comes after. Returns null if nothing comes after.
+|substringBetween(exp,after,before) |String |Returns a substring of the
message body/expression that are between before and after. Returns null if
nothing comes between.
|collate(group) |List |The collate function iterates the message body and
groups
the data into sub lists of specified size. This can be used with the
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 c393035eea3c..202236b2ca0e 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
@@ -572,6 +572,16 @@ public final class CSimpleHelper {
return StringHelper.after(body, after);
}
+ public static String substringBetween(Exchange exchange, Object value,
Object after, Object before) {
+ String body =
exchange.getContext().getTypeConverter().tryConvertTo(String.class, exchange,
value);
+ if (body == null) {
+ return null;
+ }
+ String strAfter =
exchange.getContext().getTypeConverter().convertTo(String.class, exchange,
after);
+ String strBefore =
exchange.getContext().getTypeConverter().convertTo(String.class, exchange,
before);
+ return StringHelper.between(body, strAfter, strBefore);
+ }
+
public static int random(Exchange exchange, Object min, Object max) {
int num1 =
exchange.getContext().getTypeConverter().tryConvertTo(int.class, exchange, min);
int num2 =
exchange.getContext().getTypeConverter().tryConvertTo(int.class, exchange, max);
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 57dd03120f8d..505189bba43f 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
@@ -183,6 +183,9 @@ public final class SimpleConstants {
public static final String SUBSTRING_AFTER = "substringAfter(exp,before)";
@Metadata(description = "Returns a random number between min (included)
and max (excluded).", label = "function",
javaType = "int", displayName = "Generate Random Number")
+ public static final String SUBSTRING_BETWEEN =
"substringBetween(exp,after,before)";
+ @Metadata(description = "Returns a substring of the message
body/expression that are between after and before. Returns null if nothing
comes between.",
+ label = "function", javaType = "String")
public static final String RANDOM = "random(min,max)";
@Metadata(description = "The skip function iterates the message body and
skips the first number of items."
+ " This can be used with the Splitter EIP to
split a message body and skip the first N number of items.",
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 c3ba27e19e42..b594c7b2e41d 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
@@ -163,6 +163,37 @@ public final class SimpleExpressionBuilder {
};
}
+ /**
+ * Returns an iterator to collate (iterate) the given expression
+ */
+ public static Expression collateExpression(final String expression, final
String group) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+ private Expression num;
+
+ @Override
+ public void init(CamelContext context) {
+ exp =
context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ num =
context.resolveLanguage("simple").createExpression(group);
+ num.init(context);
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ Integer n = num.evaluate(exchange, Integer.class);
+ exp = ExpressionBuilder.groupIteratorExpression(exp, null,
Integer.toString(n), false);
+ exp.init(exchange.getContext());
+ return exp.evaluate(exchange, Object.class);
+ }
+
+ @Override
+ public String toString() {
+ return "collate(" + expression + "," + group + ")";
+ }
+ };
+ }
+
/**
* Returns an iterator to skip (iterate) the given expression
*/
@@ -188,6 +219,35 @@ public final class SimpleExpressionBuilder {
};
}
+ /**
+ * Returns an iterator to skip (iterate) the given expression
+ */
+ public static Expression skipExpression(final String expression, final
String number) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+ private Expression num;
+
+ @Override
+ public void init(CamelContext context) {
+ exp =
context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ num =
context.resolveLanguage("simple").createExpression(number);
+ num.init(context);
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ int n = num.evaluate(exchange, Integer.class);
+ return skipIteratorExpression(exp, n).evaluate(exchange,
Object.class);
+ }
+
+ @Override
+ public String toString() {
+ return "skip(" + expression + "," + number + ")";
+ }
+ };
+ }
+
/**
* Trims the given expressions (uses message body if expression is null)
*/
@@ -828,6 +888,43 @@ public final class SimpleExpressionBuilder {
};
}
+ /**
+ * Returns the substring from the given expression that are between after
and before
+ */
+ public static Expression substringBetweenExpression(final String
expression, final String after, final String before) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+ private Expression expAfter;
+ private Expression expBefore;
+
+ @Override
+ public void init(CamelContext context) {
+ exp =
context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ expAfter = ExpressionBuilder.simpleExpression(after);
+ expAfter.init(context);
+ expBefore = ExpressionBuilder.simpleExpression(before);
+ expBefore.init(context);
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ String body = exp.evaluate(exchange, String.class);
+ if (body == null) {
+ return null;
+ }
+ String aft = expAfter.evaluate(exchange, String.class);
+ String bef = expBefore.evaluate(exchange, String.class);
+ return StringHelper.between(body, aft, bef);
+ }
+
+ @Override
+ public String toString() {
+ return "substringBetween(" + expression + "," + after + "," +
before + ")";
+ }
+ };
+ }
+
/**
* Hashes the value using the given algorithm
*/
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 db2c29fa6309..2e35068662ac 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
@@ -728,7 +728,7 @@ public class SimpleFunctionExpression extends
LiteralExpression {
// replace function
remainder = ifStartsWithReturnRemainder("replace(", function);
if (remainder != null) {
- String values = StringHelper.before(remainder, ")");
+ String values = StringHelper.beforeLast(remainder, ")");
if (values == null || ObjectHelper.isEmpty(values)) {
throw new SimpleParserException(
"Valid syntax: ${replace(from,to)} or
${replace(from,to,expression)} was: " + function,
@@ -755,7 +755,7 @@ public class SimpleFunctionExpression extends
LiteralExpression {
// substring function
remainder = ifStartsWithReturnRemainder("substring(", function);
if (remainder != null) {
- String values = StringHelper.before(remainder, ")");
+ String values = StringHelper.beforeLast(remainder, ")");
if (values == null || ObjectHelper.isEmpty(values)) {
throw new SimpleParserException(
"Valid syntax: ${substring(num)},
${substring(num,num)}, or ${substring(num,num,expression)} was: "
@@ -780,7 +780,7 @@ public class SimpleFunctionExpression extends
LiteralExpression {
}
remainder = ifStartsWithReturnRemainder("substringBefore(", function);
if (remainder != null) {
- String values = StringHelper.before(remainder, ")");
+ String values = StringHelper.beforeLast(remainder, ")");
if (values == null || ObjectHelper.isEmpty(values)) {
throw new SimpleParserException(
"Valid syntax: ${substringBefore(exp)} or
${substringBefore(exp,exp)} was: "
@@ -806,7 +806,7 @@ public class SimpleFunctionExpression extends
LiteralExpression {
}
remainder = ifStartsWithReturnRemainder("substringAfter(", function);
if (remainder != null) {
- String values = StringHelper.before(remainder, ")");
+ String values = StringHelper.beforeLast(remainder, ")");
if (values == null || ObjectHelper.isEmpty(values)) {
throw new SimpleParserException(
"Valid syntax: ${substringAfter(exp)} or
${substringAfter(exp,exp)} was: "
@@ -830,11 +830,40 @@ public class SimpleFunctionExpression extends
LiteralExpression {
}
return SimpleExpressionBuilder.substringAfterExpression(exp1,
after);
}
+ remainder = ifStartsWithReturnRemainder("substringBetween(", function);
+ if (remainder != null) {
+ String values = StringHelper.beforeLast(remainder, ")");
+ if (values == null || ObjectHelper.isEmpty(values)) {
+ throw new SimpleParserException(
+ "Valid syntax: ${substringBetween(after,before)} or
${substringAfter(exp,after,before)} was: "
+ + function,
+ token.getIndex());
+ }
+ String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',',
false);
+ if (tokens.length < 2 || tokens.length > 3) {
+ throw new SimpleParserException(
+ "Valid syntax: ${substringBetween(after,before)} or
${substringAfter(exp,after,before)} was: "
+ + function,
+ token.getIndex());
+ }
+ String exp1 = "${body}";
+ String after;
+ String before;
+ if (tokens.length == 3) {
+ exp1 = tokens[0];
+ after = tokens[1];
+ before = tokens[2];
+ } else {
+ after = tokens[0];
+ before = tokens[1];
+ }
+ return SimpleExpressionBuilder.substringBetweenExpression(exp1,
after, before);
+ }
// random function
remainder = ifStartsWithReturnRemainder("random(", function);
if (remainder != null) {
- String values = StringHelper.before(remainder, ")");
+ String values = StringHelper.beforeLast(remainder, ")");
if (values == null || ObjectHelper.isEmpty(values)) {
throw new SimpleParserException(
"Valid syntax: ${random(min,max)} or ${random(max)}
was: " + function, token.getIndex());
@@ -859,26 +888,24 @@ public class SimpleFunctionExpression extends
LiteralExpression {
throw new SimpleParserException("Valid syntax: ${skip(number)}
was: " + function, token.getIndex());
}
String exp = "${body}";
- int num = Integer.parseInt(values.trim());
- return SimpleExpressionBuilder.skipExpression(exp, num);
+ return SimpleExpressionBuilder.skipExpression(exp, values.trim());
}
// collate function
remainder = ifStartsWithReturnRemainder("collate(", function);
if (remainder != null) {
- String values = StringHelper.before(remainder, ")");
+ String values = StringHelper.beforeLast(remainder, ")");
if (values == null || ObjectHelper.isEmpty(values)) {
throw new SimpleParserException("Valid syntax:
${collate(group)} was: " + function, token.getIndex());
}
String exp = "${body}";
- int num = Integer.parseInt(values.trim());
- return SimpleExpressionBuilder.collateExpression(exp, num);
+ return SimpleExpressionBuilder.collateExpression(exp,
values.trim());
}
// join function
remainder = ifStartsWithReturnRemainder("join(", function);
if (remainder != null) {
- String values = StringHelper.before(remainder, ")");
+ String values = StringHelper.beforeLast(remainder, ")");
String separator = ",";
String prefix = null;
String exp = "${body}";
@@ -906,7 +933,7 @@ public class SimpleFunctionExpression extends
LiteralExpression {
remainder = ifStartsWithReturnRemainder("trim(", function);
if (remainder != null) {
String exp = null;
- String value = StringHelper.before(remainder, ")");
+ String value = StringHelper.beforeLast(remainder, ")");
if (ObjectHelper.isNotEmpty(value)) {
exp = StringHelper.removeQuotes(value);
}
@@ -919,7 +946,7 @@ public class SimpleFunctionExpression extends
LiteralExpression {
String separator = null;
String exp1 = "${body}";
String exp2;
- String values = StringHelper.before(remainder, ")");
+ String values = StringHelper.beforeLast(remainder, ")");
if (values == null || ObjectHelper.isEmpty(values)) {
throw new SimpleParserException(
"Valid syntax: ${concat(exp)} or ${concat(exp,exp)} or
${concat(exp,exp,separator)} was: " + function,
@@ -949,6 +976,7 @@ public class SimpleFunctionExpression extends
LiteralExpression {
if (remainder != null) {
String exp = "${body}";
String type;
+ // do not use beforeLast as this supports OGNL
String values = StringHelper.before(remainder, ")");
if (values == null || ObjectHelper.isEmpty(values)) {
throw new SimpleParserException(
@@ -985,7 +1013,7 @@ public class SimpleFunctionExpression extends
LiteralExpression {
remainder = ifStartsWithReturnRemainder("uppercase(", function);
if (remainder != null) {
String exp = null;
- String value = StringHelper.before(remainder, ")");
+ String value = StringHelper.beforeLast(remainder, ")");
if (ObjectHelper.isNotEmpty(value)) {
exp = StringHelper.removeQuotes(value);
}
@@ -995,7 +1023,7 @@ public class SimpleFunctionExpression extends
LiteralExpression {
remainder = ifStartsWithReturnRemainder("lowercase(", function);
if (remainder != null) {
String exp = null;
- String value = StringHelper.before(remainder, ")");
+ String value = StringHelper.beforeLast(remainder, ")");
if (ObjectHelper.isNotEmpty(value)) {
exp = StringHelper.removeQuotes(value);
}
@@ -1006,7 +1034,7 @@ public class SimpleFunctionExpression extends
LiteralExpression {
remainder = ifStartsWithReturnRemainder("length(", function);
if (remainder != null) {
String exp = null;
- String value = StringHelper.before(remainder, ")");
+ String value = StringHelper.beforeLast(remainder, ")");
if (ObjectHelper.isNotEmpty(value)) {
exp = StringHelper.removeQuotes(value);
}
@@ -1016,7 +1044,7 @@ public class SimpleFunctionExpression extends
LiteralExpression {
remainder = ifStartsWithReturnRemainder("size(", function);
if (remainder != null) {
String exp = null;
- String value = StringHelper.before(remainder, ")");
+ String value = StringHelper.beforeLast(remainder, ")");
if (ObjectHelper.isNotEmpty(value)) {
exp = StringHelper.removeQuotes(value);
}
@@ -1050,7 +1078,7 @@ public class SimpleFunctionExpression extends
LiteralExpression {
// hash function
remainder = ifStartsWithReturnRemainder("hash(", function);
if (remainder != null) {
- String values = StringHelper.before(remainder, ")");
+ String values = StringHelper.beforeLast(remainder, ")");
if (values == null || ObjectHelper.isEmpty(values)) {
throw new SimpleParserException(
"Valid syntax: ${hash(value,algorithm)} or
${hash(value)} was: " + function, token.getIndex());
@@ -2076,6 +2104,46 @@ public class SimpleFunctionExpression extends
LiteralExpression {
return "Object value = " + body + ";\n Object after = " +
before
+ ";\n return substringAfter(exchange, value,
after);";
}
+ remainder = ifStartsWithReturnRemainder("substringBetween(", function);
+ if (remainder != null) {
+ String values = StringHelper.beforeLast(remainder, ")");
+ if (values == null || ObjectHelper.isEmpty(values)) {
+ throw new SimpleParserException(
+ "Valid syntax: ${substringBetween(after,before)},
${substringBetween(exp,after,before)} was: "
+ + function,
+ token.getIndex());
+ }
+ String[] tokens = codeSplitSafe(values, ',', true, true);
+ if (tokens.length < 2 || tokens.length > 3) {
+ throw new SimpleParserException(
+ "Valid syntax: ${substringBetween(after,before)},
${substringBetween(exp,after,before)} was: "
+ + function,
+ token.getIndex());
+ }
+ // single quotes should be double quotes
+ for (int i = 0; i < tokens.length; i++) {
+ String s = tokens[i];
+ if (StringHelper.isSingleQuoted(s)) {
+ s = StringHelper.removeLeadingAndEndingQuotes(s);
+ s = StringQuoteHelper.doubleQuote(s);
+ tokens[i] = s;
+ }
+ }
+ String body = "body";
+ String after;
+ String before;
+ if (tokens.length == 3) {
+ body = tokens[0];
+ after = tokens[1];
+ before = tokens[2];
+ } else {
+ after = tokens[0];
+ before = tokens[1];
+ }
+ return "Object value = " + body + ";\n Object after = " +
after
+ + ";\n Object before = " + before
+ + ";\n return substringBetween(exchange, value,
after, before);";
+ }
// random function
remainder = ifStartsWithReturnRemainder("random(", function);
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 2a1a488899d7..81298eaeb1e9 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
@@ -1967,6 +1967,76 @@ public class SimpleTest extends LanguageTestSupport {
assertEquals("G", chunk3.get(0));
}
+ @Test
+ public void testCollateDynamic() {
+ List<Object> data = new ArrayList<>();
+ data.add("A");
+ data.add("B");
+ data.add("C");
+ data.add("D");
+ data.add("E");
+ data.add("F");
+ data.add("G");
+ exchange.getIn().setBody(data);
+
+ exchange.getIn().setHeader("num", 3);
+
+ Iterator it = (Iterator)
evaluateExpression("${collate(${header.num})}", null);
+ List chunk = (List) it.next();
+ List chunk2 = (List) it.next();
+ List chunk3 = (List) it.next();
+ assertFalse(it.hasNext());
+
+ assertEquals(3, chunk.size());
+ assertEquals(3, chunk2.size());
+ assertEquals(1, chunk3.size());
+
+ assertEquals("A", chunk.get(0));
+ assertEquals("B", chunk.get(1));
+ assertEquals("C", chunk.get(2));
+ assertEquals("D", chunk2.get(0));
+ assertEquals("E", chunk2.get(1));
+ assertEquals("F", chunk2.get(2));
+ assertEquals("G", chunk3.get(0));
+ }
+
+ @Test
+ public void testSkip() {
+ List<Object> data = new ArrayList<>();
+ data.add("A");
+ data.add("B");
+ data.add("C");
+ data.add("D");
+ data.add("E");
+ data.add("F");
+ exchange.getIn().setBody(data);
+
+ Iterator it = (Iterator) evaluateExpression("${skip(2)}", null);
+ assertEquals("C", it.next());
+ assertEquals("D", it.next());
+ assertEquals("E", it.next());
+ assertEquals("F", it.next());
+ assertFalse(it.hasNext());
+ }
+
+ @Test
+ public void testSkipDynamic() {
+ List<Object> data = new ArrayList<>();
+ data.add("A");
+ data.add("B");
+ data.add("C");
+ data.add("D");
+ data.add("E");
+ data.add("F");
+ exchange.getIn().setBody(data);
+ exchange.getIn().setHeader("num", 4);
+
+ Iterator it = (Iterator) evaluateExpression("${skip(${header.num})}",
null);
+ assertEquals("E", it.next());
+ assertEquals("F", it.next());
+ assertFalse(it.hasNext());
+ }
+
@Test
public void testJoinBody() {
List<Object> data = new ArrayList<>();
@@ -2564,6 +2634,10 @@ public class SimpleTest extends LanguageTestSupport {
s = expression.evaluate(exchange, String.class);
assertEquals("Hello", s);
+ expression =
context.resolveLanguage("simple").createExpression("${trim(${substringBefore('World')})}");
+ s = expression.evaluate(exchange, String.class);
+ assertEquals("Hello", s);
+
expression =
context.resolveLanguage("simple").createExpression("${substringBefore(${body},'World')}");
s = expression.evaluate(exchange, String.class);
assertEquals("Hello ", s);
@@ -2594,6 +2668,10 @@ public class SimpleTest extends LanguageTestSupport {
s = expression.evaluate(exchange, String.class);
assertEquals(" World", s);
+ expression =
context.resolveLanguage("simple").createExpression("${trim(${substringAfter(${body},'Hello')})}");
+ s = expression.evaluate(exchange, String.class);
+ assertEquals("World", s);
+
expression =
context.resolveLanguage("simple").createExpression("${substringAfter('Unknown')}");
s = expression.evaluate(exchange, String.class);
assertNull(s);
@@ -2604,6 +2682,38 @@ public class SimpleTest extends LanguageTestSupport {
assertEquals(" World", s);
}
+ @Test
+ public void testSubstringBetween() {
+ exchange.getMessage().setBody("Hello big great World");
+
+ Expression expression =
context.resolveLanguage("simple").createExpression("${substringBetween('Hello','World')}");
+ String s = expression.evaluate(exchange, String.class);
+ assertEquals(" big great ", s);
+
+ expression =
context.resolveLanguage("simple").createExpression("${substringBetween('Hello
',' World')}");
+ s = expression.evaluate(exchange, String.class);
+ assertEquals("big great", s);
+
+ expression =
context.resolveLanguage("simple").createExpression("${substringBetween(${body},'big
',' World')}");
+ s = expression.evaluate(exchange, String.class);
+ assertEquals("great", s);
+
+ expression =
context.resolveLanguage("simple").createExpression("${trim(${substringBetween(${body},'big','World')})}");
+ s = expression.evaluate(exchange, String.class);
+ assertEquals("great", s);
+
+ expression =
context.resolveLanguage("simple").createExpression("${substringBetween('Hello','Unknown')}");
+ s = expression.evaluate(exchange, String.class);
+ assertNull(s);
+
+ exchange.getMessage().setHeader("place", "Hello");
+ exchange.getMessage().setHeader("place2", "great");
+ expression = context.resolveLanguage("simple")
+
.createExpression("${substringBetween(${body},${header.place},${header.place2})}");
+ s = expression.evaluate(exchange, String.class);
+ assertEquals(" big ", s);
+ }
+
@Test
public void testConcat() {
exchange.getMessage().setBody("Hello");