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 696a7eca5b50 CAMEL-23048: camel-core - Simple init block to require
using semi colon to end each variable (#21550)
696a7eca5b50 is described below
commit 696a7eca5b50ac8df615f1cfda1bce25940ac96d
Author: Claus Ibsen <[email protected]>
AuthorDate: Sun Feb 22 20:35:08 2026 +0100
CAMEL-23048: camel-core - Simple init block to require using semi colon to
end each variable (#21550)
---
.../JsonPathSimpleInitBlockFunctionTest.java | 10 +-
.../jsonpath/JsonPathSimpleInitBlockTest.java | 16 +--
.../modules/languages/pages/simple-language.adoc | 48 ++++----
.../camel/language/simple/BaseSimpleParser.java | 44 +++++++-
.../language/simple/SimpleExpressionParser.java | 18 ++-
.../language/simple/SimpleInitBlockParser.java | 122 ++++++++++++++++-----
.../language/simple/SimpleInitBlockTokenizer.java | 3 +-
.../language/simple/SimplePredicateParser.java | 18 +--
.../language/simple/ast/LiteralExpression.java | 2 +-
.../language/simple/types/SimpleTokenType.java | 7 ++
.../camel/language/simple/types/TokenType.java | 1 +
.../simple/SimpleInitBlockFunctionTest.java | 22 ++--
.../camel/language/simple/SimpleInitBlockTest.java | 120 +++++++++++++++++---
.../ROOT/pages/camel-4x-upgrade-guide-4_18.adoc | 37 +++++++
.../ROOT/pages/camel-4x-upgrade-guide-4_19.adoc | 37 +++++++
.../camel/dsl/yaml/SimpleInitBlockTest.groovy | 4 +-
16 files changed, 398 insertions(+), 111 deletions(-)
diff --git
a/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathSimpleInitBlockFunctionTest.java
b/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathSimpleInitBlockFunctionTest.java
index 2ac9ef3dbb9f..1903676e1706 100644
---
a/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathSimpleInitBlockFunctionTest.java
+++
b/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathSimpleInitBlockFunctionTest.java
@@ -25,11 +25,11 @@ public class JsonPathSimpleInitBlockFunctionTest extends
CamelTestSupport {
private final String MAPPING = """
$init{
- $id := ${jsonpath($.id)}
- $type := ${header.type}
- $price := ${jsonpath($.amount)}
- $level ~:= ${body > 100 ? 'HIGH' : 'LOW'}
- $newStatus ~:= ${sum(${body},50)}
+ $id := ${jsonpath($.id)};
+ $type := ${header.type};
+ $price := ${jsonpath($.amount)};
+ $level ~:= ${body > 100 ? 'HIGH' : 'LOW'};
+ $newStatus ~:= ${sum(${body},50)};
}init$
{
"id": "$id",
diff --git
a/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathSimpleInitBlockTest.java
b/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathSimpleInitBlockTest.java
index 03aca4f59e21..b80ad8828d4b 100644
---
a/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathSimpleInitBlockTest.java
+++
b/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathSimpleInitBlockTest.java
@@ -25,10 +25,10 @@ public class JsonPathSimpleInitBlockTest extends
CamelTestSupport {
private final String MAPPING = """
$init{
- $id := ${jsonpath($.id)}
- $type := ${header.type}
- $price := ${jsonpath($.amount)}
- $level := ${iif(${jsonpath($.amount)} > 100,HIGH,LOW)}
+ $id := ${jsonpath($.id)};
+ $type := ${header.type};
+ $price := ${jsonpath($.amount)};
+ $level := ${iif(${jsonpath($.amount)} > 100,HIGH,LOW)};
}init$
{
"id": "$id",
@@ -40,10 +40,10 @@ public class JsonPathSimpleInitBlockTest extends
CamelTestSupport {
private final String MAPPING2 = """
$init{
- $id := ${jsonpath($.id)}
- $type := ${header.type}
- $price := ${jsonpath($.amount)}
- $level := ${iif(${jsonpath($.amount)} > 100,HIGH,LOW)}
+ $id := ${jsonpath($.id)};
+ $type := ${header.type};
+ $price := ${jsonpath($.amount)};
+ $level := ${iif(${jsonpath($.amount)} > 100,HIGH,LOW)};
}init$
{
"id": "$id",
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 2fb5b93ba8d0..5ea6e5cc412b 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
@@ -1179,29 +1179,37 @@ $init{
Notice how the block uses the `$init{` ... `}init$` markers to indicate the
start and end of the block.
-Inside the init block, then you can assign local variables in the syntax `$key
:= function` where you can then use simple language function(s) to compute
+Inside the init block, then you can assign local variables in the syntax `$key
:= <statement>;` where you can then use simple language to compute
the value of the variable.
+IMPORTANT: Each statement must end with semicolon and new-line (`;\n`). This
makes the init block more similar to Java programming language,
+and it was also necessary to make this work for the internal simple parser
used by Camel.
+
+Here are a couple of examples:
+
[source,text]
----
$init{
- $foo := ${upper('Hello $body}'}
- $bar := ${header.code > 999 ? 'Gold' : 'Silver'}
+ $cheese := 'Hello ${body}';
+ $minAge := 18;
+ $foo := ${upper('Hello $body}'};
+ $bar := ${header.code > 999 ? 'Gold' : 'Silver'};
}init$
----
-IMPORTANT: The left hand side must be a function - you cannot assign a
hardcoded literal value. Use the `val` function for this, such as `${val(123)}`
to use `123` as the value.
-
You can have Java style code comments in the init block using `// comment
here` as follows:
[source,text]
----
$init{
+ // minimum age to drive
+ $minAge := 18;
+
// say hello to my friend
- $foo := ${upper('Hello $body}'}
+ $foo := ${upper('Hello $body}'};
// either gold or silver
- $bar := ${header.code > 999 ? 'Gold' : 'Silver'}
+ $bar := ${header.code > 999 ? 'Gold' : 'Silver'};
}init$
----
@@ -1237,8 +1245,8 @@ from("direct:welcome")
.transform().simple(
"""
$init{
- $greeting := ${upper('Hello $body}'}
- $level := ${header.code > 999 ? 'Gold' : 'Silver'}
+ $greeting := ${upper('Hello $body}'};
+ $level := ${header.code > 999 ? 'Gold' : 'Silver'};
}init$
{
"message": "$greeting",
@@ -1258,8 +1266,8 @@ XML::
<transform>
<simple>
$init{
- $greeting := ${upper('Hello $body}'}
- $level := ${header.code > 999 ? 'Gold' : 'Silver'}
+ $greeting := ${upper('Hello $body}'};
+ $level := ${header.code > 999 ? 'Gold' : 'Silver'};
}init$
{
"message": "$greeting",
@@ -1283,8 +1291,8 @@ YAML::
simple:
expression: |-
$init{
- $greeting := ${upper('Hello $body}'}
- $level := ${header.code > 999 ? 'Gold' : 'Silver'}
+ $greeting := ${upper('Hello $body}'};
+ $level := ${header.code > 999 ? 'Gold' : 'Silver'};
}init$
{
"message": "$greeting",
@@ -1306,7 +1314,7 @@ and then refer to the file such as
`resource:classpath:mymapping.txt` where the
You can also declare custom functions using
-Inside the init block, it is a lso possible to define custom functions in the
syntax `$nane ~:= ...` where you can then use simple language to declare
+Inside the init block, it is a lso possible to define custom functions in the
syntax `$nane ~:= <statement>;` where you can then use simple language to
declare
the structure of the function. Then you can later use these custom functions
in your simple language expressions.
For example to create a function that can cleanup a `String` value:
@@ -1314,7 +1322,7 @@ For example to create a function that can cleanup a
`String` value:
[source,text]
----
$init{
- $cleanUp ~:= ${trim()} ~> ${normalizeWhitespace()} ~> ${uppercase()}
+ $cleanUp ~:= ${trim()} ~> ${normalizeWhitespace()} ~> ${uppercase()};
}init$
----
@@ -1343,20 +1351,20 @@ It is also possible to reuse custom functions from
other functions using `${func
[source,text]
----
$init{
- $cleanUp ~:= ${trim()} ~> ${normalizeWhitespace()} ~> ${uppercase()}
- $count ~:= ${function(cleanUp)} ~> ${split(' ')} ~> ${size()}
+ $cleanUp ~:= ${trim()} ~> ${normalizeWhitespace()} ~> ${uppercase()};
+ $count ~:= ${function(cleanUp)} ~> ${split(' ')} ~> ${size()};
}init$
----
Here the `$count` is declared as a function which calls the cleanUp function
and then counts the words via split and size functions.
-Instead of using `${function(name)}` you can also same syntax as the built-in
functions, as follows:
+Instead of using `${function(name)}` you can also same syntax as the built-in
Simple functions, as follows:
[source,text]
----
$init{
- $cleanUp ~:= ${trim()} ~> ${normalizeWhitespace()} ~> ${uppercase()}
- $count ~:= ${cleanUp()} ~> ${split(' ')} ~> ${size()}
+ $cleanUp ~:= ${trim()} ~> ${normalizeWhitespace()} ~> ${uppercase()};
+ $count ~:= ${cleanUp()} ~> ${split(' ')} ~> ${size()};
}init$
----
diff --git
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/BaseSimpleParser.java
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/BaseSimpleParser.java
index e821b04ec370..e181c12d5dd7 100644
---
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/BaseSimpleParser.java
+++
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/BaseSimpleParser.java
@@ -18,6 +18,7 @@ package org.apache.camel.language.simple;
import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Deque;
import java.util.List;
@@ -96,6 +97,19 @@ public abstract class BaseSimpleParser {
}
}
+ protected void skipToken() {
+ if (index < expression.length()) {
+ SimpleToken next = tokenizer.nextToken(expression, index,
allowEscape);
+ token = next;
+ // position index after the token
+ previousIndex = index;
+ index += next.getLength();
+ } else {
+ // end of tokens
+ token = new SimpleToken(new SimpleTokenType(TokenType.eol, null),
index);
+ }
+ }
+
/**
* Advances the parser position to the next known {@link SimpleToken} in
the input.
*
@@ -138,7 +152,7 @@ public abstract class BaseSimpleParser {
* {@link org.apache.camel.Expression}s to be used by Camel then the AST
graph has a linked and prepared graph of
* nodes which represent the input expression.
*/
- protected void prepareBlocks() {
+ protected void prepareBlocks(List<SimpleNode> nodes) {
List<SimpleNode> answer = new ArrayList<>();
Deque<Block> stack = new ArrayDeque<>();
@@ -191,7 +205,7 @@ public abstract class BaseSimpleParser {
* {@link org.apache.camel.Expression}s to be used by Camel then the AST
graph has a linked and prepared graph of
* nodes which represent the input expression.
*/
- protected void prepareUnaryExpressions() {
+ protected void prepareUnaryExpressions(List<SimpleNode> nodes) {
Deque<SimpleNode> stack = new ArrayDeque<>();
for (SimpleNode node : nodes) {
@@ -226,7 +240,7 @@ public abstract class BaseSimpleParser {
* So when the AST node is later used to create the {@link Predicate}s to
be used by Camel then the AST graph has a
* linked and prepared graph of nodes which represent the input expression.
*/
- protected void prepareChainExpression() {
+ protected void prepareChainExpression(List<SimpleNode> nodes) {
Deque<SimpleNode> stack = new ArrayDeque<>();
SimpleNode left = null;
@@ -287,7 +301,7 @@ public abstract class BaseSimpleParser {
Collections.reverse(nodes);
}
- protected void prepareOtherExpressions() {
+ protected void prepareOtherExpressions(List<SimpleNode> nodes) {
Deque<SimpleNode> stack = new ArrayDeque<>();
SimpleNode left = null;
@@ -350,7 +364,7 @@ public abstract class BaseSimpleParser {
* The ternary operator consists of two tokens: ? and : We need to find
the pattern: condition ? trueValue :
* falseValue
*/
- protected void prepareTernaryExpressions() {
+ protected void prepareTernaryExpressions(List<SimpleNode> nodes) {
List<SimpleNode> answer = new ArrayList<>();
for (int i = 0; i < nodes.size(); i++) {
@@ -434,6 +448,26 @@ public abstract class BaseSimpleParser {
return token == null || token.getType().getType() == accept;
}
+ /**
+ * Expect any of the given token(s)
+ *
+ * @param expect the token(s) to expect
+ * @throws SimpleParserException is thrown if the token is not as expected
+ */
+ protected void expect(TokenType... expect) throws SimpleParserException {
+ if (token == null) {
+ throw new SimpleParserException("expected any symbol " +
Arrays.asList(expect) + " but reached eol", previousIndex);
+ }
+ for (TokenType target : expect) {
+ if (token.getType().getType() == target) {
+ return;
+ }
+ }
+ // use the previous index as that is where the problem is
+ throw new SimpleParserException(
+ "expected any symbol " + Arrays.asList(expect) + " but was " +
token.getType().getType(), previousIndex);
+ }
+
/**
* Expect a given token
*
diff --git
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionParser.java
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionParser.java
index 5ff3b4c4ec0c..018f2a1df742 100644
---
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionParser.java
+++
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionParser.java
@@ -160,13 +160,13 @@ public class SimpleExpressionParser extends
BaseSimpleParser {
// turn the tokens into the ast model
parseAndCreateAstModel();
// compact and stack blocks (eg function start/end)
- prepareBlocks();
+ prepareBlocks(nodes);
// compact and stack unary operators
- prepareUnaryExpressions();
+ prepareUnaryExpressions(nodes);
// compact and stack chain expressions
- prepareChainExpression();
+ prepareChainExpression(nodes);
// compact and stack other expressions
- prepareOtherExpressions();
+ prepareOtherExpressions(nodes);
return nodes;
}
@@ -228,7 +228,7 @@ public class SimpleExpressionParser extends
BaseSimpleParser {
}
}
if (cur.getType().isInitVariable()) {
- if (prev.getType().isWhitespace()) {
+ if (prev.getType().isWhitespace() || "
".equals(prev.getText())) {
toRemove.add(prev);
}
}
@@ -266,6 +266,14 @@ public class SimpleExpressionParser extends
BaseSimpleParser {
continue;
}
+ if (token.getType().isInitVariable()) {
+ // we start a new init variable so the current image token
need to be added first
+ if (imageToken != null) {
+ nodes.add(imageToken);
+ imageToken = null;
+ }
+ }
+
// if no token was created, then it's a
character/whitespace/escaped symbol
// which we need to add together in the same image
if (imageToken == null) {
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 a627321d29d0..b56ab155219a 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
@@ -24,10 +24,13 @@ import java.util.Set;
import org.apache.camel.CamelContext;
import org.apache.camel.Expression;
+import org.apache.camel.language.simple.ast.CompositeNodes;
import org.apache.camel.language.simple.ast.InitBlockExpression;
+import org.apache.camel.language.simple.ast.LiteralExpression;
import org.apache.camel.language.simple.ast.LiteralNode;
import org.apache.camel.language.simple.ast.SimpleNode;
import org.apache.camel.language.simple.types.InitOperatorType;
+import org.apache.camel.language.simple.types.SimpleToken;
import org.apache.camel.language.simple.types.TokenType;
import org.apache.camel.util.StringHelper;
@@ -96,11 +99,6 @@ class SimpleInitBlockParser extends SimpleExpressionParser {
nextToken();
while (!token.getType().isEol()) {
initText();
- functionText();
- unaryOperator();
- otherOperator();
- chainOperator();
- nextToken();
}
// now after parsing, we need a bit of work to do, to make it easier
to turn the tokens
@@ -109,20 +107,13 @@ class SimpleInitBlockParser extends
SimpleExpressionParser {
// remove any ignore and ignorable white space tokens
removeIgnorableWhiteSpaceTokens();
- // prepare for any local variables to use $$ syntax in simple
expression
// turn the tokens into the ast model
parseAndCreateAstModel();
// compact and stack blocks (eg function start/end)
- prepareBlocks();
- // compact and stack chain expressions
- prepareChainExpression();
- // compact and stack unary operators
- prepareUnaryExpressions();
- // compact and stack other expressions
- prepareOtherExpressions();
+ prepareBlocks(nodes);
// compact and stack init blocks
- prepareInitBlocks();
+ prePrepareInitBlocks(nodes);
return nodes;
}
@@ -131,20 +122,12 @@ 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, index);
+ // skip until we find a new init variable
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,
- TokenType.initVariable,
- TokenType.eol);
+ skipToken();
}
if (accept(TokenType.initVariable)) {
+ tokens.add(token);
while (!token.getType().isWhitespace() &&
!token.getType().isEol()) {
nextToken();
}
@@ -153,18 +136,99 @@ class SimpleInitBlockParser extends
SimpleExpressionParser {
expect(TokenType.initOperator);
nextToken();
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, index);
+ // must either be a function, string literal, a number, or boolean
+ expect(TokenType.functionStart, TokenType.singleQuote,
TokenType.doubleQuote, TokenType.numericValue,
+ TokenType.booleanValue);
+
+ // accept until we find init function end
+ SimpleToken prev = token;
+ while (!token.getType().isEol() &&
!prev.getType().isInitFunctionEnd()) {
+ // an init variable supports functions, chain, unary and also
whitespace as chain uses that between functions
+ nextToken(TokenType.functionStart, TokenType.functionEnd,
TokenType.unaryOperator, TokenType.chainOperator,
+ TokenType.otherOperator, TokenType.whiteSpace,
+ TokenType.numericValue, TokenType.booleanValue,
+ TokenType.singleQuote, TokenType.doubleQuote,
+ TokenType.initFunctionEnd, TokenType.eol);
+ prev = token;
+ }
+ if (prev.getType().isInitFunctionEnd()) {
+ tokens.remove(prev);
+ }
return true;
}
return false;
}
+ protected void prePrepareInitBlocks(List<SimpleNode> nodes) {
+ List<SimpleNode> answer = new ArrayList<>();
+
+ for (int i = 1; i < nodes.size() - 1; i++) {
+ SimpleNode token = nodes.get(i);
+ if (token instanceof InitBlockExpression ie) {
+ answer.add(ie);
+
+ SimpleNode prev = nodes.get(i - 1);
+ ie.acceptLeftNode(prev);
+ // remember which init variables we have created
+ if (prev instanceof LiteralNode ln) {
+ String key = StringHelper.after(ln.getText(), "$");
+ if (key != null) {
+ key = key.trim();
+ if
(ie.getOperator().equals(InitOperatorType.CHAIN_ASSIGNMENT)) {
+ initFunctions.add(key);
+ } else {
+ initKeys.add(key);
+ }
+ }
+ }
+
+ CompositeNodes cn = new CompositeNodes(ie.getToken());
+ ie.acceptRightNode(cn);
+ int j = i + 1;
+ while (j < nodes.size()) {
+ SimpleNode next = nodes.get(j);
+ if (next instanceof InitBlockExpression ||
next.getToken().getType().isInitVariable()) {
+ break;
+ } else if (!next.getToken().getType().isInitFunctionEnd())
{
+ if (next instanceof LiteralExpression le) {
+ String text = unquote(le.getText());
+ le.replaceText(text);
+ }
+ cn.addChild(next);
+ }
+ j++;
+ }
+
+ // prepare the children
+ prepareChainExpression(cn.getChildren());
+ prepareUnaryExpressions(cn.getChildren());
+ prepareOtherExpressions(cn.getChildren());
+ // if there are only 1 child then flatten and add directly to
right hand side
+ if (cn.getChildren().size() == 1) {
+ ie.acceptRightNode(cn.getChildren().get(0));
+ }
+ i = j;
+ }
+ }
+ nodes.clear();
+ nodes.addAll(answer);
+ }
+
+ private static String unquote(String text) {
+ if (text.startsWith("\"") || text.startsWith("'")) {
+ text = text.substring(1);
+ }
+ if (text.endsWith("\"") || text.endsWith("'")) {
+ text = text.substring(0, text.length() - 1);
+ }
+ return text;
+ }
+
+ @Deprecated
protected void prepareInitBlocks() {
List<SimpleNode> answer = new ArrayList<>();
- for (int i = 1; i < nodes.size() - 2; i++) {
+ for (int i = 1; i < nodes.size() - 1; i++) {
SimpleNode token = nodes.get(i);
if (token instanceof InitBlockExpression ie) {
SimpleNode prev = nodes.get(i - 1);
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 285c10688924..628eb7868b49 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 = 3;
+ private static final int NUMBER_OF_TOKENS = 4;
private static final SimpleTokenType[] INIT_TOKENS = new
SimpleTokenType[NUMBER_OF_TOKENS];
@@ -42,6 +42,7 @@ public class SimpleInitBlockTokenizer extends SimpleTokenizer
{
INIT_TOKENS[0] = new SimpleTokenType(TokenType.initVariable, "$");
INIT_TOKENS[1] = new SimpleTokenType(TokenType.initOperator, ":=");
INIT_TOKENS[2] = new SimpleTokenType(TokenType.initOperator, "~:=");
+ INIT_TOKENS[3] = new SimpleTokenType(TokenType.initFunctionEnd, ";\n");
}
private boolean acceptInitTokens = true; // flag to turn on|off
diff --git
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimplePredicateParser.java
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimplePredicateParser.java
index 0766704ed241..016e819faa3d 100644
---
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimplePredicateParser.java
+++
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimplePredicateParser.java
@@ -175,19 +175,19 @@ public class SimplePredicateParser extends
BaseSimpleParser {
// turn the tokens into the ast model
parseTokensAndCreateNodes();
// compact and stack blocks (eg function start/end, quotes start/end,
etc.)
- prepareBlocks();
+ prepareBlocks(nodes);
// compact and stack unary expressions
- prepareUnaryExpressions();
+ prepareUnaryExpressions(nodes);
// compact and stack chain expressions
- prepareChainExpression();
+ prepareChainExpression(nodes);
// compact and stack binary expressions
- prepareBinaryExpressions();
+ prepareBinaryExpressions(nodes);
// compact and stack ternary expressions
- prepareTernaryExpressions();
+ prepareTernaryExpressions(nodes);
// compact and stack other expressions
- prepareOtherExpressions();
+ prepareOtherExpressions(nodes);
// compact and stack logical expressions
- prepareLogicalExpressions();
+ prepareLogicalExpressions(nodes);
return nodes;
}
@@ -459,7 +459,7 @@ public class SimplePredicateParser extends BaseSimpleParser
{
* So when the AST node is later used to create the {@link Predicate}s to
be used by Camel then the AST graph has a
* linked and prepared graph of nodes which represent the input expression.
*/
- private void prepareBinaryExpressions() {
+ private void prepareBinaryExpressions(List<SimpleNode> nodes) {
Deque<SimpleNode> stack = new ArrayDeque<>();
SimpleNode left = null;
@@ -522,7 +522,7 @@ public class SimplePredicateParser extends BaseSimpleParser
{
* So when the AST node is later used to create the {@link Predicate}s to
be used by Camel then the AST graph has a
* linked and prepared graph of nodes which represent the input expression.
*/
- private void prepareLogicalExpressions() {
+ private void prepareLogicalExpressions(List<SimpleNode> nodes) {
Deque<SimpleNode> stack = new ArrayDeque<>();
SimpleNode left = null;
diff --git
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/LiteralExpression.java
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/LiteralExpression.java
index 626ab79c5626..2207ed343afe 100644
---
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/LiteralExpression.java
+++
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/LiteralExpression.java
@@ -43,7 +43,7 @@ public class LiteralExpression extends BaseSimpleNode
implements LiteralNode {
this.text.append(text);
}
- void replaceText(String text) {
+ public void replaceText(String text) {
this.text.setLength(0);
addText(text);
}
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 9a2d1dfa4772..63e14b5171de 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
@@ -152,6 +152,13 @@ public final class SimpleTokenType {
return type == TokenType.initVariable;
}
+ /**
+ * Whether the type is init function end
+ */
+ public boolean isInitFunctionEnd() {
+ return type == TokenType.initFunctionEnd;
+ }
+
/**
* Whether the type is a null value
*/
diff --git
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/types/TokenType.java
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/types/TokenType.java
index 99bc5dbba43b..9b742f488f1b 100644
---
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/types/TokenType.java
+++
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/types/TokenType.java
@@ -39,6 +39,7 @@ public enum TokenType {
logicalOperator,
initOperator,
initVariable,
+ initFunctionEnd,
ternaryOperator,
chainOperator,
eol
diff --git
a/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleInitBlockFunctionTest.java
b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleInitBlockFunctionTest.java
index 2194182e2354..993395b1e653 100644
---
a/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleInitBlockFunctionTest.java
+++
b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleInitBlockFunctionTest.java
@@ -23,53 +23,53 @@ public class SimpleInitBlockFunctionTest extends
LanguageTestSupport {
private static final String INIT1 = """
$init{
- $clean ~:= ${trim()} ~> ${normalizeWhitespace()} ~>
${uppercase()}
+ $clean ~:= ${trim()} ~> ${normalizeWhitespace()} ~>
${uppercase()};
}init$
You said: $clean()
""";
private static final String INIT2 = """
$init{
- $clean ~:= ${trim()} ~> ${normalizeWhitespace()} ~>
${uppercase()}
- $count ~:= ${function(clean)} ~> ${split(' ')} ~> ${size()}
+ $clean ~:= ${trim()} ~> ${normalizeWhitespace()} ~>
${uppercase()};
+ $count ~:= ${function(clean)} ~> ${split(' ')} ~> ${size()};
}init$
You said: $clean() in $count() words
""";
private static final String INIT3 = """
$init{
- $clean ~:= ${trim()} ~> ${normalizeWhitespace()} ~>
${uppercase()}
+ $clean ~:= ${trim()} ~> ${normalizeWhitespace()} ~>
${uppercase()};
}init$
You said: ${clean(' Clean this text please ... ')} and then
do something else
""";
private static final String INIT4 = """
$init{
- $clean ~:= ${trim()} ~> ${normalizeWhitespace()} ~>
${uppercase()}
- $count ~:= ${quote()} ~> ${function(clean)} ~> ${unquote()} ~>
${trim()} ~> ${split(' ')} ~> ${size()}
+ $clean ~:= ${trim()} ~> ${normalizeWhitespace()} ~>
${uppercase()};
+ $count ~:= ${quote()} ~> ${function(clean)} ~> ${unquote()} ~>
${trim()} ~> ${split(' ')} ~> ${size()};
}init$
You said: $clean() in $count() words
""";
private static final String INIT5 = """
$init{
- $clean ~:= ${trim()} ~> ${normalizeWhitespace()} ~>
${uppercase()}
- $count ~:= $clean() ~> ${split(' ')} ~> ${size()}
+ $clean ~:= ${trim()} ~> ${normalizeWhitespace()} ~>
${uppercase()};
+ $count ~:= ${clean()} ~> ${split(' ')} ~> ${size()};
}init$
You said: $clean() in $count() words
""";
private static final String INIT6 = """
$init{
- $clean ~:= ${trim()} ~> ${normalizeWhitespace()} ~>
${uppercase()}
- $count ~:= ${quote()} ~> ${clean()} ~> ${unquote()} ~> ${trim()}
~> ${split(' ')} ~> ${size()}
+ $clean ~:= ${trim()} ~> ${normalizeWhitespace()} ~>
${uppercase()};
+ $count ~:= ${quote()} ~> ${clean()} ~> ${unquote()} ~> ${trim()}
~> ${split(' ')} ~> ${size()};
}init$
You said: $clean() in $count() words
""";
private static final String INIT7 = """
$init{
- $clean ~:= ${trim()} ~> ${normalizeWhitespace()} ~>
${uppercase()}
+ $clean ~:= ${trim()} ~> ${normalizeWhitespace()} ~>
${uppercase()};
}init$
You said: ${clean()}
""";
diff --git
a/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleInitBlockTest.java
b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleInitBlockTest.java
index 65b0d2d06cb3..bc3e1d7301c8 100644
---
a/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleInitBlockTest.java
+++
b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleInitBlockTest.java
@@ -28,9 +28,9 @@ public class SimpleInitBlockTest extends LanguageTestSupport {
private static final String INIT = """
$init{
// this is a java like comment
- $sum := ${sum(${header.lines},100)}
+ $sum := ${sum(${header.lines},100)};
- $sku := ${body contains 'Camel' ? '123' : '999'}
+ $sku := ${body contains 'Camel' ? '123' : '999'};
}init$
orderId=$sku,total=$sum
""";
@@ -38,9 +38,9 @@ public class SimpleInitBlockTest extends LanguageTestSupport {
private static final String INIT2 = """
$init{
// this is a java like comment
- $sum := ${sum(${header.lines},100)}
+ $sum := ${sum(${header.lines},100)};
- $sku := ${body contains 'Camel' ? '123' : '999'}
+ $sku := ${body contains 'Camel' ? '123' : '999'};
}init$
$sum > 200 && $sku != 999
""";
@@ -48,18 +48,33 @@ public class SimpleInitBlockTest extends
LanguageTestSupport {
private static final String INIT3 = """
$init{
// this is a java like comment
- $sum := ${sum(${header.lines},100)}
+ $sum := ${sum(${header.lines},100)};
- $sku := ${body contains 'Camel' ? '123' : '999'}
+ $sku := ${body contains 'Camel' ? '123' : '999'};
}init$
""";
private static final String INIT4 = """
$init{
// this is a java like comment
- $sum := ${sum(${header.lines},100)}
+ $sum := ${sum(${header.lines},100)};
- $sku := ${body contains 'Hi := Me $sku' ? '123' : '999'}
+ $sku := ${body contains 'Hi := Me $sku' ? '123' : '999'};
+ }init$
+ orderId=$sku,total=$sum
+ """;
+
+ private static final String INIT5 = """
+ $init{
+ // this is a java like comment
+ $sum := ${sum(${header.lines},100)};
+
+ $sku := ${body contains 'Hi := Me $sku'
+ ?
+ '123'
+ :
+ '999'
+ };
}init$
orderId=$sku,total=$sum
""";
@@ -109,13 +124,21 @@ public class SimpleInitBlockTest extends
LanguageTestSupport {
assertExpression(exchange, INIT4, "orderId=123,total=208\n");
}
+ @Test
+ public void testInitBlockSpanLines() throws Exception {
+ exchange.getMessage().setBody("Hello Hi := Me $sku");
+ exchange.getMessage().setHeader("lines", "76,34");
+
+ assertExpression(exchange, INIT5, "orderId=123,total=210\n");
+ }
+
@Test
public void testInitBlockAverageFunction() {
String exp = """
$init{
- $a := ${body}
- $b := ${header.foo}
- $c := ${header.bar}
+ $a := ${body};
+ $b := ${header.foo};
+ $c := ${header.bar};
}init$
average: ${average($a,$b,$c)}
""";
@@ -133,9 +156,9 @@ public class SimpleInitBlockTest extends
LanguageTestSupport {
public void testInitBlockAverageVal() {
String exp = """
$init{
- $a := ${val(4)}
- $b := ${val(5)}
- $c := ${val(6)}
+ $a := ${val(4)};
+ $b := ${val(5)};
+ $c := ${val(6)};
}init$
average: ${average($a,$b,$c)}
""";
@@ -145,11 +168,78 @@ public class SimpleInitBlockTest extends
LanguageTestSupport {
assertEquals("average: 5\n", s);
}
+ @Test
+ public void testInitBlockAverageLiteral() {
+ String exp = """
+ $init{
+ $a := '5';
+ $b := '6';
+ $c := '7';
+ }init$
+ average: ${average($a,$b,$c)}
+ """;
+
+ Expression expression =
context.resolveLanguage("simple").createExpression(exp);
+ String s = expression.evaluate(exchange, String.class);
+ assertEquals("average: 6\n", s);
+ }
+
+ @Test
+ public void testInitBlockAverageNumeric() {
+ String exp = """
+ $init{
+ $a := 6;
+ $b := 7;
+ $c := 8;
+ }init$
+ ${average($a,$b,$c)}
+ """;
+
+ Expression expression =
context.resolveLanguage("simple").createExpression(exp);
+ String s = expression.evaluate(exchange, String.class);
+ assertEquals("7\n", s);
+ }
+
+ @Test
+ public void testInitBlockBoolean() {
+ String exp = """
+ $init{
+ $a := true;
+ $b := false;
+ }init$
+ ${body != null ? $a : $b}
+ """;
+
+ Expression expression =
context.resolveLanguage("simple").createExpression(exp);
+ String s = expression.evaluate(exchange, String.class);
+ assertEquals("true\n", s);
+
+ exchange.getMessage().setBody(null);
+ s = expression.evaluate(exchange, String.class);
+ assertEquals("false\n", s);
+ }
+
+ @Test
+ public void testInitBlockVal() {
+ String exp = """
+ $init{
+ $bar := ${val(Hi from ${body})};
+ }init$
+ $bar
+ """;
+
+ exchange.getMessage().setBody("Camel");
+
+ Expression expression =
context.resolveLanguage("simple").createExpression(exp);
+ String s = expression.evaluate(exchange, String.class);
+ assertEquals("Hi from Camel\n", s);
+ }
+
@Test
public void testInitBlockConstant() {
String exp = """
$init{
- $bar := ${val(Hi from ${body})}
+ $bar := 'Hi from ${body}';
}init$
$bar
""";
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc
index faf9f5c1cf37..68509821c942 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc
@@ -13,9 +13,46 @@ See the xref:camel-upgrade-recipes-tool.adoc[documentation]
page for details.
== Upgrading from 4.18.0 to 4.18.1
+=== camel-yaml-io / camel-xml-io
+
In the YAML DSL we have renamed `routePolicy` to `routePolicyRef` on the
`route` node,
as that is the correct name.
+=== camel-simple
+
+In the simple language then init blocks syntax has changed to require that
each variable ends with a semicolon and new line (no trailing comments etc is
allowed)
+
+For example
+
+[source,yaml]
+----
+ - setBody:
+ simple:
+ expression: |-
+ $init{
+ // this is a java like comment
+ $sum := ${sum(${header.lines},100)}
+
+ $sku := ${iif(${body} contains 'Camel',123,999)}
+ }init$
+ orderId=$sku,total=$sum
+----
+
+Should be changed to have semicolons as shown below:
+
+[source,yaml]
+----
+ - setBody:
+ simple:
+ expression: |-
+ $init{
+ // this is a java like comment
+ $sum := ${sum(${header.lines},100)};
+
+ $sku := ${iif(${body} contains 'Camel',123,999)};
+ }init$
+ orderId=$sku,total=$sum
+----
== Upgrading Camel 4.17 to 4.18
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_19.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_19.adoc
index 05948c3420a5..1db893eae666 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_19.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_19.adoc
@@ -20,6 +20,43 @@ uses `InOnly` pattern.
Removed 2 deprecated methods in Java DSL for `throttler` EIP.
+=== camel-simple
+
+In the simple language then init blocks syntax has changed to require that
each variable ends with a semicolon and new line (no trailing comments etc is
allowed)
+
+For example
+
+[source,yaml]
+----
+ - setBody:
+ simple:
+ expression: |-
+ $init{
+ // this is a java like comment
+ $sum := ${sum(${header.lines},100)}
+
+ $sku := ${iif(${body} contains 'Camel',123,999)}
+ }init$
+ orderId=$sku,total=$sum
+----
+
+Should be changed to have semicolons as shown below:
+
+[source,yaml]
+----
+ - setBody:
+ simple:
+ expression: |-
+ $init{
+ // this is a java like comment
+ $sum := ${sum(${header.lines},100)};
+
+ $sku := ${iif(${body} contains 'Camel',123,999)};
+ }init$
+ orderId=$sku,total=$sum
+----
+
+
=== camel-yaml-io / camel-xml-io
In the YAML DSL we have renamed `routePolicy` to `routePolicyRef` on the
`route` node,
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/SimpleInitBlockTest.groovy
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/SimpleInitBlockTest.groovy
index cbc63a01d037..b24a58b38049 100644
---
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/SimpleInitBlockTest.groovy
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/SimpleInitBlockTest.groovy
@@ -33,9 +33,9 @@ class SimpleInitBlockTest extends YamlTestSupport {
expression: |-
$init{
// this is a java like comment
- $sum := ${sum(${header.lines},100)}
+ $sum := ${sum(${header.lines},100)};
- $sku := ${iif(${body} contains
'Camel',123,999)}
+ $sku := ${iif(${body} contains
'Camel',123,999)};
}init$
orderId=$sku,total=$sum
- to: