This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch ic
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 802e2d1e0ecb174e9fd5a0ed7110d5e544d5a400
Author: Claus Ibsen <[email protected]>
AuthorDate: Fri Jan 30 09:45:14 2026 +0100

    CAMEL-22899: camel-core - Simple language - Add custom function in init 
block via chains
---
 .../language/simple/SimpleInitBlockParser.java     | 16 +++++++---
 .../language/simple/SimpleInitBlockTokenizer.java  | 28 +++++++++++++---
 .../camel/language/simple/SimpleTokenizer.java     |  6 ++++
 .../language/simple/ast/InitBlockExpression.java   |  4 ++-
 .../language/simple/types/InitOperatorType.java    |  7 +++-
 .../language/simple/types/SimpleTokenType.java     |  7 ++++
 .../language/simple/SimpleInitBlockChainTest.java} | 37 ++++++++++------------
 7 files changed, 75 insertions(+), 30 deletions(-)

diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleInitBlockParser.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleInitBlockParser.java
index 6e34eeb0b181..ec686f0e5dd9 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleInitBlockParser.java
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleInitBlockParser.java
@@ -92,6 +92,7 @@ class SimpleInitBlockParser extends SimpleExpressionParser {
             functionText();
             unaryOperator();
             otherOperator();
+            chainOperator();
             nextToken();
         }
 
@@ -107,12 +108,14 @@ class SimpleInitBlockParser extends 
SimpleExpressionParser {
         parseAndCreateAstModel();
         // compact and stack blocks (eg function start/end)
         prepareBlocks();
-        // compact and stack init blocks
-        prepareInitBlocks();
+        // compact and stack chain expressions
+        prepareChainExpression();
         // compact and stack unary operators
         prepareUnaryExpressions();
         // compact and stack other expressions
         prepareOtherExpressions();
+        // compact and stack init blocks
+        prepareInitBlocks();
 
         return nodes;
     }
@@ -121,8 +124,13 @@ class SimpleInitBlockParser extends SimpleExpressionParser 
{
     // $$name := <function>
     // $$name2 := <function>
     protected boolean initText() {
+        // has there been a new line since last (which reset and allow to look 
for new init variable)
+        if (!getTokenizer().hasNewLine()) {
+            return false;
+        }
+
         // turn on init mode so the parser can find the beginning of the init 
variable
-        getTokenizer().setAcceptInitTokens(true);
+        getTokenizer().setAcceptInitTokens(true, index);
         while (!token.getType().isInitVariable() && !token.getType().isEol()) {
             // skip until we find init variable/function (this skips code 
comments)
             nextToken(TokenType.functionStart, TokenType.unaryOperator, 
TokenType.chainOperator, TokenType.otherOperator,
@@ -140,7 +148,7 @@ class SimpleInitBlockParser extends SimpleExpressionParser {
             expectAndAcceptMore(TokenType.whiteSpace);
             // turn off init mode so the parser does not detect init variables 
inside functions or literal text
             // because they may also use := or $$ symbols
-            getTokenizer().setAcceptInitTokens(false);
+            getTokenizer().setAcceptInitTokens(false, index);
             return true;
         }
 
diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleInitBlockTokenizer.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleInitBlockTokenizer.java
index 65339cd2b562..285c10688924 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleInitBlockTokenizer.java
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleInitBlockTokenizer.java
@@ -28,7 +28,7 @@ import org.apache.camel.language.simple.types.TokenType;
 public class SimpleInitBlockTokenizer extends SimpleTokenizer {
 
     // keep this number in sync with tokens list
-    private static final int NUMBER_OF_TOKENS = 2;
+    private static final int NUMBER_OF_TOKENS = 3;
 
     private static final SimpleTokenType[] INIT_TOKENS = new 
SimpleTokenType[NUMBER_OF_TOKENS];
 
@@ -39,11 +39,13 @@ public class SimpleInitBlockTokenizer extends 
SimpleTokenizer {
 
     static {
         // init
-        INIT_TOKENS[0] = new SimpleTokenType(TokenType.initOperator, ":=");
-        INIT_TOKENS[1] = new SimpleTokenType(TokenType.initVariable, "$");
+        INIT_TOKENS[0] = new SimpleTokenType(TokenType.initVariable, "$");
+        INIT_TOKENS[1] = new SimpleTokenType(TokenType.initOperator, ":=");
+        INIT_TOKENS[2] = new SimpleTokenType(TokenType.initOperator, "~:=");
     }
 
     private boolean acceptInitTokens = true; // flag to turn on|off
+    private boolean newLine = true;
 
     /**
      * Does the expression include a simple init block.
@@ -58,15 +60,33 @@ public class SimpleInitBlockTokenizer extends 
SimpleTokenizer {
         return false;
     }
 
-    protected void setAcceptInitTokens(boolean accept) {
+    protected void setAcceptInitTokens(boolean accept, int index) {
         this.acceptInitTokens = accept;
+        if (!accept) {
+            this.newLine = false;
+        }
+    }
+
+    protected boolean hasNewLine() {
+        return newLine;
+    }
+
+    @Override
+    protected void onToken(SimpleTokenType token, int index) {
+        if (token.isNewLine()) {
+            this.newLine = true;
+        }
     }
 
     @Override
     protected SimpleToken customToken(String expression, int index, boolean 
allowEscape, TokenType... filters) {
+        // when discovering init tokens we must have seen a new-line prior
         if (!acceptInitTokens) {
             return null;
         }
+        if (!newLine) {
+            return null;
+        }
 
         // it could be any of the known tokens
         String text = expression.substring(index);
diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleTokenizer.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleTokenizer.java
index 0335a87a5464..650bb72b90ec 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleTokenizer.java
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleTokenizer.java
@@ -188,12 +188,14 @@ public class SimpleTokenizer {
             SimpleTokenType token = KNOWN_TOKENS[i];
             if (acceptType(token.getType(), filters)
                     && acceptToken(token, text, expression, index)) {
+                onToken(token, index);
                 return new SimpleToken(token, index);
             }
         }
 
         SimpleToken custom = customToken(expression, index, allowEscape, 
filters);
         if (custom != null) {
+            onToken(custom.getType(), index);
             return custom;
         }
 
@@ -206,6 +208,10 @@ public class SimpleTokenizer {
         return null;
     }
 
+    protected void onToken(SimpleTokenType token, int index) {
+        // noop
+    }
+
     private static int repositionIndex(String expression, int index, 
StringBuilder sb) {
         boolean digit = true;
         while (digit && index < expression.length()) {
diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/InitBlockExpression.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/InitBlockExpression.java
index 07812ccd961a..684e78f50c85 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/InitBlockExpression.java
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/InitBlockExpression.java
@@ -86,9 +86,11 @@ public class InitBlockExpression extends BaseSimpleNode {
 
         if (operator == InitOperatorType.ASSIGNMENT) {
             return createAssignmentExpression(camelContext, leftExp, rightExp);
+        } else if (operator == InitOperatorType.CHAIN_ASSIGNMENT) {
+            throw new UnsupportedOperationException("TODO: Implement ~:=");
         }
 
-        throw new SimpleParserException("Unknown other operator " + operator, 
token.getIndex());
+        throw new SimpleParserException("Unknown init operator " + operator, 
token.getIndex());
     }
 
     private Expression createAssignmentExpression(
diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/types/InitOperatorType.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/types/InitOperatorType.java
index 1ebccdd6a799..ba0edb4d8226 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/types/InitOperatorType.java
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/types/InitOperatorType.java
@@ -21,11 +21,14 @@ package org.apache.camel.language.simple.types;
  */
 public enum InitOperatorType {
 
-    ASSIGNMENT;
+    ASSIGNMENT,
+    CHAIN_ASSIGNMENT;
 
     public static InitOperatorType asOperator(String text) {
         if (":=".equals(text)) {
             return ASSIGNMENT;
+        } else if ("~:=".equals(text)) {
+            return CHAIN_ASSIGNMENT;
         }
         throw new IllegalArgumentException("Operator not supported: " + text);
     }
@@ -33,6 +36,8 @@ public enum InitOperatorType {
     public static String getOperatorText(InitOperatorType operator) {
         if (operator == ASSIGNMENT) {
             return ":=";
+        } else if (operator == CHAIN_ASSIGNMENT) {
+            return "~:=";
         }
         return "";
     }
diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/types/SimpleTokenType.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/types/SimpleTokenType.java
index f4f6ff780954..9a2d1dfa4772 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/types/SimpleTokenType.java
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/types/SimpleTokenType.java
@@ -61,6 +61,13 @@ public final class SimpleTokenType {
         return type == TokenType.whiteSpace;
     }
 
+    /**
+     * Whether the type is a new-line character
+     */
+    public boolean isNewLine() {
+        return isWhitespace() && "\n".equals(value);
+    }
+
     /**
      * Whether the type is eol
      */
diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/types/InitOperatorType.java
 
b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleInitBlockChainTest.java
similarity index 55%
copy from 
core/camel-core-languages/src/main/java/org/apache/camel/language/simple/types/InitOperatorType.java
copy to 
core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleInitBlockChainTest.java
index 1ebccdd6a799..a6a1c974ec25 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/types/InitOperatorType.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleInitBlockChainTest.java
@@ -14,32 +14,29 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.language.simple.types;
+package org.apache.camel.language.simple;
 
-/**
- * Types of init operators supported
- */
-public enum InitOperatorType {
+import org.apache.camel.LanguageTestSupport;
+import org.junit.jupiter.api.Test;
 
-    ASSIGNMENT;
+public class SimpleInitBlockChainTest extends LanguageTestSupport {
 
-    public static InitOperatorType asOperator(String text) {
-        if (":=".equals(text)) {
-            return ASSIGNMENT;
-        }
-        throw new IllegalArgumentException("Operator not supported: " + text);
-    }
+    private static final String INIT = """
+            $init{
+              $clean ~:= ${trim()} ~> ${normalizeWhitespace()} ~> 
${uppercase()}
+            }init$
+            You said: $clean()
+            """;
 
-    public static String getOperatorText(InitOperatorType operator) {
-        if (operator == ASSIGNMENT) {
-            return ":=";
-        }
-        return "";
+    @Test
+    public void testInitBlockChain() throws Exception {
+        exchange.getMessage().setBody("   Hello  big   World      ");
+
+        assertExpression(exchange, INIT, "You said: HELLO BIG WORLD");
     }
 
     @Override
-    public String toString() {
-        return getOperatorText(this);
+    protected String getLanguageName() {
+        return "simple";
     }
-
 }

Reply via email to