This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch ss in repository https://gitbox.apache.org/repos/asf/camel.git
commit 5837fbe066b24c77337cfa0395ba3542c430a481 Author: Claus Ibsen <[email protected]> AuthorDate: Mon Jul 1 07:11:51 2024 +0200 CAMEL-20931: camel-core - Add substring function to simple language --- .../language/csimple/joor/OriginalSimpleTest.java | 26 +++++++++++ .../modules/languages/pages/simple-language.adoc | 40 +++++++++++++++++ .../camel/language/csimple/CSimpleHelper.java | 33 ++++++++++++++ .../language/simple/SimpleExpressionBuilder.java | 40 +++++++++++++++++ .../simple/ast/SimpleFunctionExpression.java | 52 ++++++++++++++++++++++ .../apache/camel/language/simple/SimpleTest.java | 51 +++++++++++++++++++++ .../camel/support/builder/ExpressionBuilder.java | 45 +++++++++++++++++++ 7 files changed, 287 insertions(+) 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 3477bb46cf2..4c55bfb9b0b 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 @@ -249,6 +249,32 @@ public class OriginalSimpleTest extends LanguageTestSupport { assertExpression("${replace(",∅)}", "{foo: cheese}"); } + @Test + public void testSubstringExpression() { + exchange.getMessage().setBody("ABCDEFGHIJK"); + // head + assertExpression("${substring(0)}", "ABCDEFGHIJK"); + assertExpression("${substring(1)}", "BCDEFGHIJK"); + assertExpression("${substring(3)}", "DEFGHIJK"); + assertExpression("${substring(99)}", ""); + // tail + assertExpression("${substring(0)}", "ABCDEFGHIJK"); + assertExpression("${substring(-1)}", "ABCDEFGHIJ"); + assertExpression("${substring(-3)}", "ABCDEFGH"); + assertExpression("${substring(-99)}", ""); + // head and tail + assertExpression("${substring(1,-1)}", "BCDEFGHIJ"); + assertExpression("${substring(3,-3)}", "DEFGH"); + assertExpression("${substring(1,-3)}", "BCDEFGH"); + assertExpression("${substring(3,-1)}", "DEFGHIJ"); + assertExpression("${substring(0,-1)}", "ABCDEFGHIJ"); + assertExpression("${substring(1,0)}", "BCDEFGHIJK"); + assertExpression("${substring(99,-99)}", ""); + assertExpression("${substring(0,-99)}", ""); + assertExpression("${substring(99,0)}", ""); + assertExpression("${substring(0,0)}", "ABCDEFGHIJK"); + } + @Test public void testTrimSimpleExpressions() { assertExpression(" \t${exchangeId}\n".trim(), exchange.getExchangeId()); 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 55782cdaf81..b4ffc73e284 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 @@ -246,6 +246,18 @@ To make it easier to replace single and double quotes, then you can use XML esca |replace(from,to,exp) |String |replace all the string values in the given expression. To make it easier to replace single and double quotes, then you can use XML escaped values `\"` as double quote, `\'` as single quote, and `\∅` as empty value. +|substring(num1) |String |returns a substring of the message body. +If the number is positive then the returned string is clipped from the beginning. +If the number is negative then the returned string is clipped from the ending. + +|substring(num1,num2) |String |returns a substring of the message body. +If the number is positive then the returned string is clipped from the beginning. +If the number is negative then the returned string is clipped from the ending. + +|substring(num1,num2,exp) |String |replace all the string values in the given expression. +If the number is positive then the returned string is clipped from the beginning. +If the number is negative then the returned string is clipped from the ending. + |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 Splitter EIP to split a message body and group/batch @@ -854,6 +866,34 @@ You can nest functions, such as shown below: </setHeader> ---- +=== Substring + +You can use the `substring` function to more easily clip the message body. +For example if the message body contains the following 10 letters `ABCDEFGHIJ` then: + +[source,xml] +---- +<setBody> + <simple>${substring(3)}</simple> +</setBody> +---- + +Then the message body after the substring will be `DEFGHIJ`. +If you want to clip from the end instead, then use negative values such as `substring(-3)`. + +You can also clip from both ends at the same time such as `substring(1,-1)` that will clip the first and last character in the String. + +If the number is higher than the length of the message body, then an empty string is returned, for example `substring(99)`. + +Instead of the message body then a simple expression can be nested as input, for example using a variable, as shown below: + +[source,xml] +---- +<setBody> + <simple>${substring(1,-1,${variable.foo})}</simple> +</setBody> +---- + === Replacing double and single quotes You can use the `replace` function to more easily replace all single or double quotes in the message body, 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 13c09c92749..a540d1eabd6 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 @@ -483,6 +483,39 @@ public final class CSimpleHelper { throw new IllegalArgumentException("function empty(%s) has unknown type".formatted(type)); } + 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); + if (head < 0 && tail == 0) { + // if there is only one value and its negative then we want to clip from tail + tail = head; + head = 0; + } + head = Math.abs(head); + tail = Math.abs(tail); + String text = exchange.getMessage().getBody(String.class); + if (text == null) { + return null; + } + int len = text.length(); + if (head > 0) { + if (head <= len) { + text = text.substring(head); + } else { + text = ""; + } + len = text.length(); + } + if (tail > 0) { + if (tail <= len) { + text = text.substring(0, len - tail); + } else { + text = ""; + } + } + return text; + } + 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/SimpleExpressionBuilder.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionBuilder.java index 82d0a18b7b0..54e59bba477 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 @@ -234,6 +234,46 @@ public final class SimpleExpressionBuilder { }; } + /** + * Substring string values from the expression + */ + public static Expression substringExpression(final String expression, final String head, final String tail) { + return new ExpressionAdapter() { + private Expression exp; + private Expression exp1; + private Expression exp2; + + @Override + public void init(CamelContext context) { + exp = context.resolveLanguage("simple").createExpression(expression); + exp.init(context); + exp1 = ExpressionBuilder.simpleExpression(head); + exp1.init(context); + exp2 = ExpressionBuilder.simpleExpression(tail); + exp2.init(context); + } + + @Override + public Object evaluate(Exchange exchange) { + int num1 = exp1.evaluate(exchange, Integer.class); + int num2 = exp2.evaluate(exchange, Integer.class); + if (num1 < 0 && num2 == 0) { + // if there is only one value and its negative then we want to clip from tail + num2 = num1; + num1 = 0; + } + num1 = Math.abs(num1); + num2 = Math.abs(num2); + return ExpressionBuilder.substring(exp, num1, num2).evaluate(exchange, Object.class); + } + + @Override + public String toString() { + return "substring(" + expression + "," + head + "," + tail + ")"; + } + }; + } + /** * 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 200f9282372..27ca0bfab16 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 @@ -689,6 +689,33 @@ public class SimpleFunctionExpression extends LiteralExpression { return SimpleExpressionBuilder.replaceExpression(exp, from, to); } + // substring function + remainder = ifStartsWithReturnRemainder("substring(", function); + if (remainder != null) { + String values = StringHelper.before(remainder, ")"); + if (values == null || ObjectHelper.isEmpty(values)) { + throw new SimpleParserException( + "Valid syntax: ${substring(num)}, ${substring(num,num)}, or ${substring(num,num,expression)} was: " + + function, + token.getIndex()); + } + String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', false); + if (tokens.length > 3) { + throw new SimpleParserException( + "Valid syntax: ${replace(num,num,expression)} was: " + function, token.getIndex()); + } + String num1 = tokens[0]; + String num2 = "0"; + if (tokens.length > 1) { + num2 = tokens[1]; + } + String exp = "${body}"; + if (tokens.length == 3) { + exp = tokens[2]; + } + return SimpleExpressionBuilder.substringExpression(exp, num1, num2); + } + // random function remainder = ifStartsWithReturnRemainder("random(", function); if (remainder != null) { @@ -1638,6 +1665,31 @@ public class SimpleFunctionExpression extends LiteralExpression { private String createCodeExpressionMisc(String function) { String remainder; + // substring function + remainder = ifStartsWithReturnRemainder("substring(", function); + if (remainder != null) { + String values = StringHelper.before(remainder, ")"); + if (values == null || ObjectHelper.isEmpty(values)) { + throw new SimpleParserException( + "Valid syntax: ${substring(num)}, ${substring(num,num)} was: " + + function, + token.getIndex()); + } + String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', false); + if (tokens.length > 2) { + throw new SimpleParserException( + "Valid syntax: ${replace(num,num)} was: " + function, token.getIndex()); + } + String num1 = tokens[0]; + String num2 = "0"; + if (tokens.length > 1) { + num2 = tokens[1]; + } + num1 = num1.trim(); + num2 = num2.trim(); + return "substring(exchange, " + num1 + ", " + num2 + ")"; + } + // random function remainder = ifStartsWithReturnRemainder("random(", 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 938a4110adb..46bec4cc7b7 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 @@ -2063,6 +2063,57 @@ public class SimpleTest extends LanguageTestSupport { assertExpression("${replace(",',${header.foo})}", "{'foo': 'cheese'}"); } + @Test + public void testSubstringExpression() { + exchange.getMessage().setBody("ABCDEFGHIJK"); + // head + assertExpression("${substring(0)}", "ABCDEFGHIJK"); + assertExpression("${substring(1)}", "BCDEFGHIJK"); + assertExpression("${substring(3)}", "DEFGHIJK"); + assertExpression("${substring(99)}", ""); + // tail + assertExpression("${substring(0)}", "ABCDEFGHIJK"); + assertExpression("${substring(-1)}", "ABCDEFGHIJ"); + assertExpression("${substring(-3)}", "ABCDEFGH"); + assertExpression("${substring(-99)}", ""); + // head and tail + assertExpression("${substring(1,-1)}", "BCDEFGHIJ"); + assertExpression("${substring(3,-3)}", "DEFGH"); + assertExpression("${substring(1,-3)}", "BCDEFGH"); + assertExpression("${substring(3,-1)}", "DEFGHIJ"); + assertExpression("${substring(0,-1)}", "ABCDEFGHIJ"); + assertExpression("${substring(1,0)}", "BCDEFGHIJK"); + assertExpression("${substring(99,-99)}", ""); + assertExpression("${substring(0,-99)}", ""); + assertExpression("${substring(99,0)}", ""); + assertExpression("${substring(0,0)}", "ABCDEFGHIJK"); + + exchange.getMessage().setBody("Hello World"); + exchange.getMessage().setHeader("foo", "1234567890"); + + // head + assertExpression("${substring(0,0,${header.foo})}", "1234567890"); + assertExpression("${substring(1,0,${header.foo})}", "234567890"); + assertExpression("${substring(3,0,${header.foo})}", "4567890"); + assertExpression("${substring(99,0,${header.foo})}", ""); + // tail + assertExpression("${substring(0,0,${header.foo})}", "1234567890"); + assertExpression("${substring(0,-1,${header.foo})}", "123456789"); + assertExpression("${substring(0,-3,${header.foo})}", "1234567"); + assertExpression("${substring(0,-99,${header.foo})}", ""); + // head and tail + assertExpression("${substring(1,-1,${header.foo})}", "23456789"); + assertExpression("${substring(3,-3,${header.foo})}", "4567"); + assertExpression("${substring(1,-3,${header.foo})}", "234567"); + assertExpression("${substring(3,-1,${header.foo})}", "456789"); + assertExpression("${substring(0,-1,${header.foo})}", "123456789"); + assertExpression("${substring(1,0,${header.foo})}", "234567890"); + assertExpression("${substring(99,-99,${header.foo})}", ""); + assertExpression("${substring(0,-99,${header.foo})}", ""); + assertExpression("${substring(99,0,${header.foo})}", ""); + assertExpression("${substring(0,0,${header.foo})}", "1234567890"); + } + @Test public void testListRemoveByInstance() { List<Object> data = new ArrayList<>(); diff --git a/core/camel-support/src/main/java/org/apache/camel/support/builder/ExpressionBuilder.java b/core/camel-support/src/main/java/org/apache/camel/support/builder/ExpressionBuilder.java index 4b54b53f7db..19a0fdfe4d5 100644 --- a/core/camel-support/src/main/java/org/apache/camel/support/builder/ExpressionBuilder.java +++ b/core/camel-support/src/main/java/org/apache/camel/support/builder/ExpressionBuilder.java @@ -1841,6 +1841,51 @@ public class ExpressionBuilder { }; } + /** + * Substring string values in the given expression. + */ + public static Expression substring( + final Expression expression, + final int head, final int tail) { + return new ExpressionAdapter() { + @Override + public Object evaluate(Exchange exchange) { + String text = expression.evaluate(exchange, String.class); + if (text == null) { + return null; + } + int len = text.length(); + if (head > 0) { + if (head <= len) { + text = text.substring(head); + } else { + text = ""; + } + len = text.length(); + } + if (tail > 0) { + if (tail <= len) { + text = text.substring(0, len - tail); + } else { + text = ""; + } + } + return text; + } + + @Override + public void init(CamelContext context) { + super.init(context); + expression.init(context); + } + + @Override + public String toString() { + return "substring(" + expression + ", " + head + ", " + tail + ")"; + } + }; + } + /** * Replaces string values in the given expression. */
