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(&quot;,&empty;)}", "{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 `\&quot;` as double quote, `\&apos;` as single quote, and 
`\&empty;` 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(&quot;,&apos;,${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.
      */

Reply via email to