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 064d6da1d6b8 CAMEL-22894: extract `body`, `header` and `variable`
Simple functions into dedicated factories (#23382)
064d6da1d6b8 is described below
commit 064d6da1d6b85cba734c2d9c8fb64ba50b6e082f
Author: Adriano Machado <[email protected]>
AuthorDate: Wed May 20 13:35:27 2026 -0400
CAMEL-22894: extract `body`, `header` and `variable` Simple functions into
dedicated factories (#23382)
Extract three built-in Simple language functions (body, header, variable)
from
`SimpleFunctionExpression` into dedicated `SimpleLanguageFunctionFactory`
implementations
(`BodyFunctionFactory`, `HeaderFunctionFactory`,
`VariableFunctionFactory`), following
the same pattern already established for `random`, `skip`, `collate` and
`join`.
Shared utilities (`ifStartsWithReturnRemainder`, `splitOgnl`,
`ognlCodeMethods`, `appendClass`, `parseInHeader`, `parseVariable`) are
consolidated in `SimpleFunctionHelper` so the new factories can import them
without introducing a circular dependency on the `ast` package.
The two legacy dispatch methods (`tryCreate`, `tryCreateCode`) in
`SimpleFunctionDispatcher`
are preserved as `@Deprecated(forRemoval=true)` with a Javadoc note that
they were
migrated as-is and are candidates for removal once all functions are
extracted (CAMEL-22894).
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
rh-pre-commit.version: 2.3.2
rh-pre-commit.check-secrets: ENABLED
---
.../camel/spi/SimpleLanguageFunctionFactory.java | 2 +-
.../language/simple/SimpleFunctionDispatcher.java | 20 +-
.../language/simple/SimpleFunctionHelper.java | 137 ++++
.../simple/ast/SimpleFunctionExpression.java | 720 +--------------------
.../simple/functions/BodyFunctionFactory.java | 293 +++++++++
.../simple/functions/CollateFunctionFactory.java | 2 +-
.../simple/functions/HeaderFunctionFactory.java | 218 +++++++
.../simple/functions/JoinFunctionFactory.java | 2 +-
.../simple/functions/RandomFunctionFactory.java | 2 +-
.../simple/functions/SkipFunctionFactory.java | 2 +-
.../simple/functions/VariableFunctionFactory.java | 162 +++++
.../simple/functions/BodyFunctionFactoryTest.java | 125 ++++
.../functions/HeaderFunctionFactoryTest.java | 96 +++
.../functions/VariableFunctionFactoryTest.java | 86 +++
14 files changed, 1148 insertions(+), 719 deletions(-)
diff --git
a/core/camel-api/src/main/java/org/apache/camel/spi/SimpleLanguageFunctionFactory.java
b/core/camel-api/src/main/java/org/apache/camel/spi/SimpleLanguageFunctionFactory.java
index 9667ccb3409f..1a8b763ed160 100644
---
a/core/camel-api/src/main/java/org/apache/camel/spi/SimpleLanguageFunctionFactory.java
+++
b/core/camel-api/src/main/java/org/apache/camel/spi/SimpleLanguageFunctionFactory.java
@@ -53,7 +53,7 @@ public interface SimpleLanguageFunctionFactory {
* @param function the function
* @param index index of the function in the literal input
* @return the source code or <tt>null</tt> if not
supported by this factory.
- * @deprecated will be removed in 5.0 along with csimple
+ * @deprecated will be removed along with csimple
*/
@Nullable
@Deprecated(since = "4.21")
diff --git
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleFunctionDispatcher.java
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleFunctionDispatcher.java
index 65057ecb8124..0f41262c81dc 100644
---
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleFunctionDispatcher.java
+++
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleFunctionDispatcher.java
@@ -21,14 +21,17 @@ import java.util.function.Predicate;
import org.apache.camel.CamelContext;
import org.apache.camel.Expression;
+import org.apache.camel.language.simple.functions.BodyFunctionFactory;
import org.apache.camel.language.simple.functions.CollateFunctionFactory;
+import org.apache.camel.language.simple.functions.HeaderFunctionFactory;
import org.apache.camel.language.simple.functions.JoinFunctionFactory;
import org.apache.camel.language.simple.functions.RandomFunctionFactory;
import org.apache.camel.language.simple.functions.SkipFunctionFactory;
+import org.apache.camel.language.simple.functions.VariableFunctionFactory;
import org.apache.camel.spi.SimpleLanguageFunctionFactory;
import org.apache.camel.support.ResolverHelper;
-import static
org.apache.camel.language.simple.ast.SimpleFunctionExpression.ifStartsWithReturnRemainder;
+import static
org.apache.camel.language.simple.SimpleFunctionHelper.ifStartsWithReturnRemainder;
/**
* Dispatches Simple/CSimple function lookup to built-in function factories
and to {@link SimpleLanguageFunctionFactory}
@@ -45,9 +48,12 @@ public final class SimpleFunctionDispatcher {
/**
* Built-in factories shipped by camel-core-languages itself. Iterated
before {@link #EXPRESSION_ENTRIES}, matching
* the original priority of these functions inside {@code
SimpleFunctionExpression}. Each factory returns
- * {@code null} for inputs it does not recognise, so no gating predicate
is needed.
+ * {@code null} for inputs it does not recognize, so no gating predicate
is needed.
*/
private static final List<SimpleLanguageFunctionFactory> BUILT_INS =
List.of(
+ new BodyFunctionFactory(),
+ new HeaderFunctionFactory(),
+ new VariableFunctionFactory(),
new RandomFunctionFactory(),
new SkipFunctionFactory(),
new CollateFunctionFactory(),
@@ -69,6 +75,11 @@ public final class SimpleFunctionDispatcher {
private SimpleFunctionDispatcher() {
}
+ /**
+ * Migrated as-is from main; not currently called. Candidate for removal
once all Simple functions are extracted
+ * into dedicated {@link SimpleLanguageFunctionFactory} implementations
(CAMEL-22894).
+ */
+ @Deprecated(forRemoval = true)
public static Expression tryCreate(CamelContext camelContext, String
function, int index) {
Expression answer = tryCreateBuiltIn(camelContext, function, index);
if (answer != null) {
@@ -101,6 +112,11 @@ public final class SimpleFunctionDispatcher {
return null;
}
+ /**
+ * Migrated as-is from main; not currently called. Candidate for removal
once all Simple functions are extracted
+ * into dedicated {@link SimpleLanguageFunctionFactory} implementations
(CAMEL-22894).
+ */
+ @Deprecated(forRemoval = true)
public static String tryCreateCode(CamelContext camelContext, String
function, int index) {
String code = tryCreateCodeBuiltIn(camelContext, function, index);
if (code != null) {
diff --git
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleFunctionHelper.java
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleFunctionHelper.java
index d6ae1b132dce..6e11f57f7c7a 100644
---
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleFunctionHelper.java
+++
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleFunctionHelper.java
@@ -19,11 +19,148 @@ package org.apache.camel.language.simple;
import java.util.ArrayList;
import java.util.List;
+import org.apache.camel.util.OgnlHelper;
+import org.apache.camel.util.StringHelper;
+
public final class SimpleFunctionHelper {
private SimpleFunctionHelper() {
}
+ public static String ifStartsWithReturnRemainder(String prefix, String
text) {
+ if (text.startsWith(prefix)) {
+ String remainder = text.substring(prefix.length());
+ if (!remainder.isEmpty()) {
+ return remainder;
+ }
+ }
+ return null;
+ }
+
+ public static List<String> splitOgnl(String remainder) {
+ List<String> methods = OgnlHelper.splitOgnl(remainder);
+ List<String> answer = new ArrayList<>();
+ for (String m : methods) {
+ if (m.startsWith(".")) {
+ m = m.substring(1);
+ }
+ boolean index = m.startsWith("[") && m.endsWith("]");
+ if (index) {
+ String last = answer.isEmpty() ? null :
answer.get(answer.size() - 1);
+ boolean lastIndex = last != null && last.startsWith("[") &&
last.endsWith("]");
+ if (lastIndex) {
+ answer.set(answer.size() - 1, last + m);
+ } else {
+ answer.add(m);
+ }
+ } else {
+ answer.add(m);
+ }
+ }
+ return answer;
+ }
+
+ public static String ognlCodeMethods(String remainder, String type) {
+ StringBuilder sb = new StringBuilder(256);
+
+ if (remainder != null) {
+ List<String> methods = splitOgnl(remainder);
+ for (String m : methods) {
+ if (m.startsWith("(")) {
+ sb.append(m);
+ continue;
+ }
+
+ String index = StringHelper.betweenOuterPair(m, '[', ']');
+ if (index != null) {
+ m = StringHelper.before(m, "[");
+ }
+
+ if (m != null && m.equals("length")) {
+ if (type != null && type.contains("[]")) {
+ sb.append(".length");
+ continue;
+ }
+ }
+
+ if (m != null) {
+ m = OgnlHelper.methodAsDoubleQuotes(m);
+ }
+
+ if (m != null && !m.isEmpty()) {
+ sb.append(".");
+ char ch = m.charAt(m.length() - 1);
+ if (Character.isAlphabetic(ch)) {
+ if (!m.startsWith("get")) {
+ sb.append("get");
+ sb.append(Character.toUpperCase(m.charAt(0)));
+ sb.append(m.substring(1));
+ } else {
+ sb.append(m);
+ }
+ sb.append("()");
+ } else {
+ sb.append(m);
+ }
+ }
+
+ if (index != null) {
+ sb.append(".get(");
+ try {
+ long lon = Long.parseLong(index);
+ sb.append(lon);
+ if (lon > Integer.MAX_VALUE) {
+ sb.append("l");
+ }
+ } catch (Exception e) {
+ index =
StringHelper.removeLeadingAndEndingQuotes(index);
+ sb.append("\"");
+ sb.append(index);
+ sb.append("\"");
+ }
+ sb.append(")");
+ }
+ }
+ }
+
+ if (!sb.isEmpty()) {
+ return sb.toString();
+ } else {
+ return remainder;
+ }
+ }
+
+ public static String appendClass(String type) {
+ type = StringHelper.removeQuotes(type);
+ if (!type.endsWith(".class")) {
+ type = type + ".class";
+ }
+ return type;
+ }
+
+ public static String parseInHeader(String function) {
+ String remainder;
+ remainder = ifStartsWithReturnRemainder("in.headers", function);
+ if (remainder == null) {
+ remainder = ifStartsWithReturnRemainder("in.header", function);
+ }
+ if (remainder == null) {
+ remainder = ifStartsWithReturnRemainder("headers", function);
+ }
+ if (remainder == null) {
+ remainder = ifStartsWithReturnRemainder("header", function);
+ }
+ return remainder;
+ }
+
+ public static String parseVariable(String function) {
+ String remainder = ifStartsWithReturnRemainder("variables", function);
+ if (remainder == null) {
+ remainder = ifStartsWithReturnRemainder("variable", function);
+ }
+ return remainder;
+ }
+
public static String[] codeSplitSafe(String input, char separator, boolean
trim, boolean keepQuotes) {
if (input == null) {
return null;
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 843691c5a6b3..2e5d7ba037b3 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
@@ -30,6 +30,7 @@ import org.apache.camel.RuntimeCamelException;
import org.apache.camel.language.simple.BaseSimpleParser;
import org.apache.camel.language.simple.SimpleExpressionBuilder;
import org.apache.camel.language.simple.SimpleFunctionDispatcher;
+import org.apache.camel.language.simple.SimpleFunctionHelper;
import org.apache.camel.language.simple.SimplePredicateParser;
import org.apache.camel.language.simple.types.SimpleParserException;
import org.apache.camel.language.simple.types.SimpleToken;
@@ -119,16 +120,6 @@ public class SimpleFunctionExpression extends
LiteralExpression {
return answer;
}
- // body and headers first
- answer = createSimpleExpressionBodyOrHeader(function, strict);
- if (answer != null) {
- return answer;
- }
- // variables
- answer = createSimpleExpressionVariables(function, strict);
- if (answer != null) {
- return answer;
- }
// custom functions
answer = createSimpleCustomFunction(camelContext, function, strict);
if (answer != null) {
@@ -457,181 +448,6 @@ public class SimpleFunctionExpression extends
LiteralExpression {
return null;
}
- private Expression createSimpleExpressionBodyOrHeader(String function,
boolean strict) {
- // bodyAs
- String remainder = ifStartsWithReturnRemainder("bodyAs(", function);
- if (remainder != null) {
- String type = StringHelper.before(remainder, ")");
- if (type == null) {
- throw new SimpleParserException("Valid syntax: ${bodyAs(type)}
was: " + function, token.getIndex());
- }
- type = StringHelper.removeQuotes(type);
- remainder = StringHelper.after(remainder, ")");
- if (ObjectHelper.isNotEmpty(remainder)) {
- boolean invalid =
OgnlHelper.isInvalidValidOgnlExpression(remainder);
- if (invalid) {
- throw new SimpleParserException("Valid syntax:
${bodyAs(type).OGNL} was: " + function, token.getIndex());
- }
- return SimpleExpressionBuilder.bodyOgnlExpression(type,
remainder);
- } else {
- return ExpressionBuilder.bodyExpression(type);
- }
- }
- // mandatoryBodyAs
- remainder = ifStartsWithReturnRemainder("mandatoryBodyAs(", function);
- if (remainder != null) {
- String type = StringHelper.before(remainder, ")");
- if (type == null) {
- throw new SimpleParserException("Valid syntax:
${mandatoryBodyAs(type)} was: " + function, token.getIndex());
- }
- type = StringHelper.removeQuotes(type);
- remainder = StringHelper.after(remainder, ")");
- if (ObjectHelper.isNotEmpty(remainder)) {
- boolean invalid =
OgnlHelper.isInvalidValidOgnlExpression(remainder);
- if (invalid) {
- throw new SimpleParserException(
- "Valid syntax: ${mandatoryBodyAs(type).OGNL} was:
" + function, token.getIndex());
- }
- return
SimpleExpressionBuilder.mandatoryBodyOgnlExpression(type, remainder);
- } else {
- return SimpleExpressionBuilder.mandatoryBodyExpression(type);
- }
- }
-
- // body OGNL
- remainder = ifStartsWithReturnRemainder("body", function);
- if (remainder == null) {
- remainder = ifStartsWithReturnRemainder("in.body", function);
- }
- if (remainder != null) {
- // OGNL must start with a ".", "?" or "[".
- boolean ognlStart = remainder.startsWith(".") ||
remainder.startsWith("?") || remainder.startsWith("[");
- boolean invalid = !ognlStart ||
OgnlHelper.isInvalidValidOgnlExpression(remainder);
- if (invalid) {
- throw new SimpleParserException("Valid syntax: ${body.OGNL}
was: " + function, token.getIndex());
- }
- return SimpleExpressionBuilder.bodyOgnlExpression(remainder);
- }
-
- // headerAs
- remainder = ifStartsWithReturnRemainder("headerAs(", function);
- if (remainder != null) {
- String keyAndType = StringHelper.before(remainder, ")");
- if (keyAndType == null) {
- throw new SimpleParserException("Valid syntax: ${headerAs(key,
type)} was: " + function, token.getIndex());
- }
-
- String key = StringHelper.before(keyAndType, ",");
- String type = StringHelper.after(keyAndType, ",");
- remainder = StringHelper.after(remainder, ")");
- if (ObjectHelper.isEmpty(key) || ObjectHelper.isEmpty(type) ||
ObjectHelper.isNotEmpty(remainder)) {
- throw new SimpleParserException("Valid syntax: ${headerAs(key,
type)} was: " + function, token.getIndex());
- }
- key = StringHelper.removeQuotes(key);
- type = StringHelper.removeQuotes(type);
- return ExpressionBuilder.headerExpression(key, type);
- }
-
- // headers function
- if ("in.headers".equals(function) || "headers".equals(function)) {
- return ExpressionBuilder.headersExpression();
- } else if ("headers.size".equals(function) ||
"headers.size()".equals(function)
- || "headers.length".equals(function) ||
"headers.length()".equals(function)) {
- return ExpressionBuilder.headersSizeExpression();
- }
-
- // in header function
- remainder = parseInHeader(function);
- if (remainder != null) {
- // remove leading character (dot, colon or ?)
- if (remainder.startsWith(".") || remainder.startsWith(":") ||
remainder.startsWith("?")) {
- remainder = remainder.substring(1);
- }
- // remove starting and ending brackets
- if (remainder.startsWith("[") && remainder.endsWith("]")) {
- remainder = remainder.substring(1, remainder.length() - 1);
- }
- // remove quotes from key
- String key = StringHelper.removeLeadingAndEndingQuotes(remainder);
-
- // validate syntax
- boolean invalid = OgnlHelper.isInvalidValidOgnlExpression(key);
- if (invalid) {
- throw new SimpleParserException("Valid syntax:
${header.name[key]} was: " + function, token.getIndex());
- }
-
- if (OgnlHelper.isValidOgnlExpression(key)) {
- // ognl based header
- return SimpleExpressionBuilder.headersOgnlExpression(key);
- } else {
- // regular header
- return ExpressionBuilder.headerExpression(key);
- }
- }
-
- return null;
- }
-
- private Expression createSimpleExpressionVariables(String function,
boolean strict) {
- // variableAs
- String remainder = ifStartsWithReturnRemainder("variableAs(",
function);
- if (remainder != null) {
- String keyAndType = StringHelper.before(remainder, ")");
- if (keyAndType == null) {
- throw new SimpleParserException("Valid syntax:
${variableAs(key, type)} was: " + function, token.getIndex());
- }
-
- String key = StringHelper.before(keyAndType, ",");
- String type = StringHelper.after(keyAndType, ",");
- remainder = StringHelper.after(remainder, ")");
- if (ObjectHelper.isEmpty(key) || ObjectHelper.isEmpty(type) ||
ObjectHelper.isNotEmpty(remainder)) {
- throw new SimpleParserException("Valid syntax:
${variableAs(key, type)} was: " + function, token.getIndex());
- }
- key = StringHelper.removeQuotes(key);
- type = StringHelper.removeQuotes(type);
- return ExpressionBuilder.variableExpression(key, type);
- }
-
- // variables function
- if ("variables".equals(function)) {
- return ExpressionBuilder.variablesExpression();
- } else if ("variables.size".equals(function) ||
"variables.size()".equals(function)
- || "variables.length".equals(function) ||
"variables.length()".equals(function)) {
- return ExpressionBuilder.variablesSizeExpression();
- }
-
- // variable function
- remainder = parseVariable(function);
- if (remainder != null) {
- // remove leading character (dot, colon or ?)
- if (remainder.startsWith(".") || remainder.startsWith(":") ||
remainder.startsWith("?")) {
- remainder = remainder.substring(1);
- }
- // remove starting and ending brackets
- if (remainder.startsWith("[") && remainder.endsWith("]")) {
- remainder = remainder.substring(1, remainder.length() - 1);
- }
- // remove quotes from key
- String key = StringHelper.removeLeadingAndEndingQuotes(remainder);
-
- // validate syntax
- boolean invalid = OgnlHelper.isInvalidValidOgnlExpression(key);
- if (invalid) {
- throw new SimpleParserException("Valid syntax:
${variable.name[key]} was: " + function, token.getIndex());
- }
-
- if (OgnlHelper.isValidOgnlExpression(key)) {
- // ognl based variable
- return SimpleExpressionBuilder.variablesOgnlExpression(key);
- } else {
- // regular variable
- return ExpressionBuilder.variableExpression(key);
- }
- }
-
- return null;
- }
-
private Expression createSimpleCustomFunction(CamelContext camelContext,
String function, boolean strict) {
String remainder = ifStartsWithReturnRemainder("function(", function);
if (remainder != null) {
@@ -758,21 +574,7 @@ public class SimpleFunctionExpression extends
LiteralExpression {
}
private Expression createSimpleExpressionDirectly(CamelContext
camelContext, String expression) {
- if (ObjectHelper.isEqualToAny(expression, "body", "in.body")) {
- return ExpressionBuilder.bodyExpression();
- } else if (ObjectHelper.equal(expression, "bodyType")) {
- return ExpressionBuilder.bodyTypeExpression();
- } else if (ObjectHelper.equal(expression, "prettyBody")) {
- return ExpressionBuilder.prettyBodyExpression();
- } else if (ObjectHelper.equal(expression, "toJsonBody")) {
- return
ExpressionBuilder.toJsonExpression(ExpressionBuilder.bodyExpression(), false);
- } else if (ObjectHelper.equal(expression, "toPrettyJsonBody")) {
- return
ExpressionBuilder.toJsonExpression(ExpressionBuilder.bodyExpression(), true);
- } else if (ObjectHelper.equal(expression, "bodyOneLine")) {
- return ExpressionBuilder.bodyOneLine();
- } else if (ObjectHelper.equal(expression, "originalBody")) {
- return ExpressionBuilder.originalBodyExpression();
- } else if (ObjectHelper.equal(expression, "id")) {
+ if (ObjectHelper.equal(expression, "id")) {
return ExpressionBuilder.messageIdExpression();
} else if (ObjectHelper.equal(expression, "messageTimestamp")) {
return ExpressionBuilder.messageTimestampExpression();
@@ -1814,14 +1616,9 @@ public class SimpleFunctionExpression extends
LiteralExpression {
return null;
}
+ @Deprecated(since = "4.21")
public static String ifStartsWithReturnRemainder(String prefix, String
text) {
- if (text.startsWith(prefix)) {
- String remainder = text.substring(prefix.length());
- if (!remainder.isEmpty()) {
- return remainder;
- }
- }
- return null;
+ return SimpleFunctionHelper.ifStartsWithReturnRemainder(prefix, text);
}
@Override
@@ -1838,24 +1635,11 @@ public class SimpleFunctionExpression extends
LiteralExpression {
return answer;
}
- // body, headers and exchange property first
- answer = createCodeBody(function);
- if (answer != null) {
- return answer;
- }
- answer = createCodeHeader(function);
- if (answer != null) {
- return answer;
- }
+ // exchange property first
answer = createCodeExchangeProperty(function);
if (answer != null) {
return answer;
}
- answer = createCodeVariables(function);
- if (answer != null) {
- return answer;
- }
-
// camelContext OGNL
String remainder = ifStartsWithReturnRemainder("camelContext",
function);
if (remainder != null) {
@@ -2063,19 +1847,7 @@ public class SimpleFunctionExpression extends
LiteralExpression {
}
public String createCodeDirectly(String expression) throws
SimpleParserException {
- if (ObjectHelper.isEqualToAny(expression, "body", "in.body")) {
- return "body";
- } else if (ObjectHelper.equal(expression, "bodyType")) {
- return "bodyType(exchange)";
- } else if (ObjectHelper.equal(expression, "prettyBody")) {
- return "prettyBody(exchange)";
- } else if (ObjectHelper.equal(expression, "toJsonBody")) {
- return "toJsonBody(exchange, false)";
- } else if (ObjectHelper.equal(expression, "toPrettyJsonBody")) {
- return "toJsonBody(exchange, true)";
- } else if (ObjectHelper.equal(expression, "bodyOneLine")) {
- return "bodyOneLine(exchange)";
- } else if (ObjectHelper.equal(expression, "id")) {
+ if (ObjectHelper.equal(expression, "id")) {
return "message.getMessageId()";
} else if (ObjectHelper.equal(expression, "messageTimestamp")) {
return "message.getMessageTimestamp()";
@@ -2112,408 +1884,6 @@ public class SimpleFunctionExpression extends
LiteralExpression {
return null;
}
- private String createCodeBody(final String function) {
- // bodyAsIndex
- String remainder = ifStartsWithReturnRemainder("bodyAsIndex(",
function);
- if (remainder != null) {
- String typeAndIndex = StringHelper.before(remainder, ")");
- if (typeAndIndex == null) {
- throw new SimpleParserException(
- "Valid syntax: ${bodyAsIndex(type, index).OGNL} was: "
+ function, token.getIndex());
- }
-
- String type = StringHelper.before(typeAndIndex, ",");
- String index = StringHelper.after(typeAndIndex, ",");
- remainder = StringHelper.after(remainder, ")");
- if (ObjectHelper.isEmpty(type) || ObjectHelper.isEmpty(index)) {
- throw new SimpleParserException(
- "Valid syntax: ${bodyAsIndex(type, index).OGNL} was: "
+ function, token.getIndex());
- }
- type = type.trim();
- type = appendClass(type);
- type = type.replace('$', '.');
- index = StringHelper.removeQuotes(index);
- index = index.trim();
- if (ObjectHelper.isNotEmpty(remainder)) {
- boolean invalid =
OgnlHelper.isInvalidValidOgnlExpression(remainder);
- if (invalid) {
- throw new SimpleParserException(
- "Valid syntax: ${bodyAsIndex(type, index).OGNL}
was: " + function, token.getIndex());
- }
- return "bodyAsIndex(message, " + type + ", \"" + index + "\")"
+ ognlCodeMethods(remainder, type);
- } else {
- return "bodyAsIndex(message, " + type + ", \"" + index + "\")";
- }
- }
-
- // bodyAs
- remainder = ifStartsWithReturnRemainder("bodyAs(", function);
- if (remainder != null) {
- String type = StringHelper.before(remainder, ")");
- if (type == null) {
- throw new SimpleParserException("Valid syntax: ${bodyAs(type)}
was: " + function, token.getIndex());
- }
- type = appendClass(type);
- type = type.replace('$', '.');
- type = type.trim();
- remainder = StringHelper.after(remainder, ")");
- if (ObjectHelper.isNotEmpty(remainder)) {
- boolean invalid =
OgnlHelper.isInvalidValidOgnlExpression(remainder);
- if (invalid) {
- throw new SimpleParserException("Valid syntax:
${bodyAs(type).OGNL} was: " + function, token.getIndex());
- }
- if (remainder.startsWith("[")) {
- // is there any index, then we should use bodyAsIndex
function instead
- // (use splitOgnl which assembles multiple indexes into a
single part)
- List<String> parts = splitOgnl(remainder);
- if (!parts.isEmpty()) {
- String func = "bodyAsIndex(" + type + ", \"" +
parts.remove(0) + "\")";
- String last = String.join("", parts);
- if (!last.isEmpty()) {
- func += "." + last;
- }
- return createCodeBody(func);
- }
- }
- return "bodyAs(message, " + type + ")" +
ognlCodeMethods(remainder, type);
- } else {
- return "bodyAs(message, " + type + ")";
- }
- }
-
- // mandatoryBodyAsIndex
- remainder = ifStartsWithReturnRemainder("mandatoryBodyAsIndex(",
function);
- if (remainder != null) {
- String typeAndIndex = StringHelper.before(remainder, ")");
- if (typeAndIndex == null) {
- throw new SimpleParserException(
- "Valid syntax: ${mandatoryBodyAsIndex(type,
index).OGNL} was: " + function, token.getIndex());
- }
-
- String type = StringHelper.before(typeAndIndex, ",");
- String index = StringHelper.after(typeAndIndex, ",");
- remainder = StringHelper.after(remainder, ")");
- if (ObjectHelper.isEmpty(type) || ObjectHelper.isEmpty(index)) {
- throw new SimpleParserException(
- "Valid syntax: ${mandatoryBodyAsIndex(type,
index).OGNL} was: " + function, token.getIndex());
- }
- type = type.trim();
- type = appendClass(type);
- type = type.replace('$', '.');
- index = StringHelper.removeQuotes(index);
- index = index.trim();
- if (ObjectHelper.isNotEmpty(remainder)) {
- boolean invalid =
OgnlHelper.isInvalidValidOgnlExpression(remainder);
- if (invalid) {
- throw new SimpleParserException(
- "Valid syntax: ${mandatoryBodyAsIndex(type,
index).OGNL} was: " + function, token.getIndex());
- }
- return "mandatoryBodyAsIndex(message, " + type + ", \"" +
index + "\")" + ognlCodeMethods(remainder, type);
- } else {
- return "mandatoryBodyAsIndex(message, " + type + ", \"" +
index + "\")";
- }
- }
-
- // mandatoryBodyAs
- remainder = ifStartsWithReturnRemainder("mandatoryBodyAs(", function);
- if (remainder != null) {
- String type = StringHelper.before(remainder, ")");
- if (type == null) {
- throw new SimpleParserException("Valid syntax:
${mandatoryBodyAs(type)} was: " + function, token.getIndex());
- }
- type = appendClass(type);
- type = type.replace('$', '.');
- type = type.trim();
- remainder = StringHelper.after(remainder, ")");
- if (ObjectHelper.isNotEmpty(remainder)) {
- boolean invalid =
OgnlHelper.isInvalidValidOgnlExpression(remainder);
- if (invalid) {
- throw new SimpleParserException(
- "Valid syntax: ${mandatoryBodyAs(type).OGNL} was:
" + function, token.getIndex());
- }
- if (remainder.startsWith("[")) {
- // is there any index, then we should use
mandatoryBodyAsIndex function instead
- // (use splitOgnl which assembles multiple indexes into a
single part)
- List<String> parts = splitOgnl(remainder);
- if (!parts.isEmpty()) {
- String func = "mandatoryBodyAsIndex(" + type + ", \""
+ parts.remove(0) + "\")";
- String last = String.join("", parts);
- if (!last.isEmpty()) {
- func += "." + last;
- }
- return createCodeBody(func);
- }
- }
- return "mandatoryBodyAs(message, " + type + ")" +
ognlCodeMethods(remainder, type);
- } else {
- return "mandatoryBodyAs(message, " + type + ")";
- }
- }
-
- // body OGNL
- remainder = ifStartsWithReturnRemainder("body", function);
- if (remainder == null) {
- remainder = ifStartsWithReturnRemainder("in.body", function);
- }
- if (remainder != null) {
- // OGNL must start with a . ? or [
- boolean ognlStart = remainder.startsWith(".") ||
remainder.startsWith("?") || remainder.startsWith("[");
- boolean invalid = !ognlStart ||
OgnlHelper.isInvalidValidOgnlExpression(remainder);
- if (invalid) {
- throw new SimpleParserException("Valid syntax: ${body.OGNL}
was: " + function, token.getIndex());
- }
- if (remainder.startsWith("[")) {
- // is there any index, then we should use bodyAsIndex function
instead
- // (use splitOgnl which assembles multiple indexes into a
single part)
- List<String> parts = splitOgnl(remainder);
- if (!parts.isEmpty()) {
- String func = "bodyAsIndex(Object.class, \"" +
parts.remove(0) + "\")";
- String last = String.join("", parts);
- if (!last.isEmpty()) {
- func += "." + last;
- }
- return createCodeBody(func);
- }
- }
- return "body" + ognlCodeMethods(remainder, null);
- }
-
- return null;
- }
-
- private String createCodeHeader(final String function) {
- // headerAsIndex
- String remainder = ifStartsWithReturnRemainder("headerAsIndex(",
function);
- if (remainder != null) {
- String keyTypeAndIndex = StringHelper.before(remainder, ")");
- if (keyTypeAndIndex == null) {
- throw new SimpleParserException(
- "Valid syntax: ${headerAsIndex(key, type, index)} was:
" + function, token.getIndex());
- }
- String[] parts = keyTypeAndIndex.split(",");
- if (parts.length != 3) {
- throw new SimpleParserException(
- "Valid syntax: ${headerAsIndex(key, type, index)} was:
" + function, token.getIndex());
- }
- String key = parts[0];
- String type = parts[1];
- String index = parts[2];
- if (ObjectHelper.isEmpty(key) || ObjectHelper.isEmpty(type) ||
ObjectHelper.isEmpty(index)) {
- throw new SimpleParserException(
- "Valid syntax: ${headerAsIndex(key, type, index)} was:
" + function, token.getIndex());
- }
- key = StringHelper.removeQuotes(key);
- key = key.trim();
- type = appendClass(type);
- type = type.replace('$', '.');
- type = type.trim();
- index = StringHelper.removeQuotes(index);
- index = index.trim();
- remainder = StringHelper.after(remainder, ")");
- if (ObjectHelper.isNotEmpty(remainder)) {
- boolean invalid =
OgnlHelper.isInvalidValidOgnlExpression(remainder);
- if (invalid) {
- throw new SimpleParserException(
- "Valid syntax: ${headerAsIndex(key, type,
index).OGNL} was: " + function, token.getIndex());
- }
- return "headerAsIndex(message, " + type + ", \"" + key + "\",
\"" + index + "\")"
- + ognlCodeMethods(remainder, type);
- } else {
- return "headerAsIndex(message, " + type + ", \"" + key + "\",
\"" + index + "\")";
- }
- }
-
- // headerAs
- remainder = ifStartsWithReturnRemainder("headerAs(", function);
- if (remainder != null) {
- String keyAndType = StringHelper.before(remainder, ")");
- if (keyAndType == null) {
- throw new SimpleParserException("Valid syntax: ${headerAs(key,
type)} was: " + function, token.getIndex());
- }
-
- String key = StringHelper.before(keyAndType, ",");
- String type = StringHelper.after(keyAndType, ",");
- remainder = StringHelper.after(remainder, ")");
- if (ObjectHelper.isEmpty(key) || ObjectHelper.isEmpty(type)) {
- throw new SimpleParserException("Valid syntax: ${headerAs(key,
type)} was: " + function, token.getIndex());
- }
- key = StringHelper.removeQuotes(key);
- key = key.trim();
- type = appendClass(type);
- type = type.replace('$', '.');
- type = type.trim();
- return "headerAs(message, \"" + key + "\", " + type + ")" +
ognlCodeMethods(remainder, type);
- }
-
- // headers function
- if ("in.headers".equals(function) || "headers".equals(function)) {
- return "message.getHeaders()";
- } else if ("headers.size".equals(function) ||
"headers.size()".equals(function)
- || "headers.length".equals(function) ||
"headers.length()".equals(function)) {
- return "message.getHeaders().size()";
- }
-
- // in header function
- remainder = parseInHeader(function);
- if (remainder != null) {
- // remove leading character (dot, colon or ?)
- if (remainder.startsWith(".") || remainder.startsWith(":") ||
remainder.startsWith("?")) {
- remainder = remainder.substring(1);
- }
- // remove starting and ending brackets
- if (remainder.startsWith("[") && remainder.endsWith("]")) {
- remainder = remainder.substring(1, remainder.length() - 1);
- }
- // remove quotes from key
- String key = StringHelper.removeLeadingAndEndingQuotes(remainder);
- key = key.trim();
-
- // validate syntax
- boolean invalid = OgnlHelper.isInvalidValidOgnlExpression(key);
- if (invalid) {
- throw new SimpleParserException("Valid syntax:
${header.name[key]} was: " + function, token.getIndex());
- }
-
- // the key can contain index as it may be a map header.foo[0]
- // and the key can also be OGNL (e.g., if there is a dot)
- boolean index = false;
- List<String> parts = splitOgnl(key);
- if (!parts.isEmpty()) {
- String s = parts.get(0);
- int pos = s.indexOf('[');
- if (pos != -1) {
- index = true;
- // split key into name and index
- String before = s.substring(0, pos);
- String after = s.substring(pos);
- parts.set(0, before);
- parts.add(1, after);
- }
- }
- if (index) {
- // is there any index, then we should use headerAsIndex
function instead
- // (use splitOgnl which assembles multiple indexes into a
single part?)
- String func = "headerAsIndex(\"" + parts.get(0) + "\",
Object.class, \"" + parts.get(1) + "\")";
- if (parts.size() > 2) {
- String last = String.join("", parts.subList(2,
parts.size()));
- if (!last.isEmpty()) {
- func += "." + last;
- }
- }
- return createCodeHeader(func);
- } else if (OgnlHelper.isValidOgnlExpression(key)) {
- // ognl based header must be typed
- throw new SimpleParserException("Valid syntax: ${headerAs(key,
type).OGNL} was: " + function, token.getIndex());
- } else {
- // regular header
- return "header(message, \"" + key + "\")";
- }
- }
-
- return null;
- }
-
- private String parseInHeader(String function) {
- String remainder;
- remainder = ifStartsWithReturnRemainder("in.headers", function);
- if (remainder == null) {
- remainder = ifStartsWithReturnRemainder("in.header", function);
- }
- if (remainder == null) {
- remainder = ifStartsWithReturnRemainder("headers", function);
- }
- if (remainder == null) {
- remainder = ifStartsWithReturnRemainder("header", function);
- }
- return remainder;
- }
-
- private String parseVariable(String function) {
- String remainder;
- remainder = ifStartsWithReturnRemainder("variables", function);
- if (remainder == null) {
- remainder = ifStartsWithReturnRemainder("variable", function);
- }
- return remainder;
- }
-
- private String createCodeVariables(final String function) {
- // variableAs
- String remainder = ifStartsWithReturnRemainder("variableAs(",
function);
- if (remainder != null) {
- String keyAndType = StringHelper.before(remainder, ")");
- if (keyAndType == null) {
- throw new SimpleParserException("Valid syntax:
${variableAs(key, type)} was: " + function, token.getIndex());
- }
- String key = StringHelper.before(keyAndType, ",");
- String type = StringHelper.after(keyAndType, ",");
- remainder = StringHelper.after(remainder, ")");
- if (ObjectHelper.isEmpty(key) || ObjectHelper.isEmpty(type)) {
- throw new SimpleParserException(
- "Valid syntax: ${variableAs(key, type)} was: " +
function, token.getIndex());
- }
- key = StringHelper.removeQuotes(key);
- key = key.trim();
- type = appendClass(type);
- type = type.replace('$', '.');
- type = type.trim();
- return "variableAs(exchange, \"" + key + "\", " + type + ")" +
ognlCodeMethods(remainder, type);
- }
-
- // variables function
- if ("variables".equals(function)) {
- return "variables(exchange)";
- } else if ("variables.size".equals(function) ||
"variables.size()".equals(function)
- || "variables.length".equals(function) ||
"variables.length()".equals(function)) {
- return "variablesSize(exchange)";
- }
-
- // variable
- remainder = ifStartsWithReturnRemainder("variable", function);
- if (remainder != null) {
- // remove leading character (dot or ?)
- if (remainder.startsWith(".") || remainder.startsWith("?")) {
- remainder = remainder.substring(1);
- }
- // remove starting and ending brackets
- if (remainder.startsWith("[") && remainder.endsWith("]")) {
- remainder = remainder.substring(1, remainder.length() - 1);
- }
- // remove quotes from key
- String key = StringHelper.removeLeadingAndEndingQuotes(remainder);
- key = key.trim();
-
- // validate syntax
- boolean invalid = OgnlHelper.isInvalidValidOgnlExpression(key);
- if (invalid) {
- throw new SimpleParserException(
- "Valid syntax: ${variable.name[key]} was: " +
function, token.getIndex());
- }
-
- // it is an index?
- String index = null;
- if (key.endsWith("]")) {
- index = StringHelper.between(key, "[", "]");
- if (index != null) {
- key = StringHelper.before(key, "[");
- }
- }
- if (index != null) {
- index = StringHelper.removeLeadingAndEndingQuotes(index);
- return "variableAsIndex(exchange, Object.class, \"" + key +
"\", \"" + index + "\")";
- } else if (OgnlHelper.isValidOgnlExpression(remainder)) {
- // ognl based exchange property must be typed
- throw new SimpleParserException(
- "Valid syntax: ${variableAs(key, type)} was: " +
function, token.getIndex());
- } else {
- // regular property
- return "variable(exchange, \"" + key + "\")";
- }
- }
-
- return null;
- }
-
private String createCodeExchangeProperty(final String function) {
// exchangePropertyAsIndex
String remainder =
ifStartsWithReturnRemainder("exchangePropertyAsIndex(", function);
@@ -4078,83 +3448,9 @@ public class SimpleFunctionExpression extends
LiteralExpression {
return null;
}
+ @Deprecated(since = "4.21")
public static String ognlCodeMethods(String remainder, String type) {
- StringBuilder sb = new StringBuilder(256);
-
- if (remainder != null) {
- List<String> methods = splitOgnl(remainder);
- for (int i = 0; i < methods.size(); i++) {
- String m = methods.get(i);
- if (m.startsWith("(")) {
- // its parameters for the function so add as-is and
continue
- sb.append(m);
- continue;
- }
-
- // clip index
- String index = StringHelper.betweenOuterPair(m, '[', ']');
- if (index != null) {
- m = StringHelper.before(m, "[");
- }
-
- // special for length on arrays
- if (m != null && m.equals("length")) {
- if (type != null && type.contains("[]")) {
- sb.append(".length");
- continue;
- }
- }
-
- // single quotes for string literals should be replaced as
double quotes
- if (m != null) {
- m = OgnlHelper.methodAsDoubleQuotes(m);
- }
-
- // shorthand getter syntax: .name -> .getName()
- if (m != null && !m.isEmpty()) {
- // a method so append with a dot
- sb.append(".");
- char ch = m.charAt(m.length() - 1);
- if (Character.isAlphabetic(ch)) {
- if (!m.startsWith("get")) {
- sb.append("get");
- sb.append(Character.toUpperCase(m.charAt(0)));
- sb.append(m.substring(1));
- } else {
- sb.append(m);
- }
- sb.append("()");
- } else {
- sb.append(m);
- }
- }
-
- // append index via a get method - eg get for a list, or get
for a map (array not supported)
- if (index != null) {
- sb.append(".get(");
- try {
- long lon = Long.parseLong(index);
- sb.append(lon);
- if (lon > Integer.MAX_VALUE) {
- sb.append("l");
- }
- } catch (Exception e) {
- // its text based
- index =
StringHelper.removeLeadingAndEndingQuotes(index);
- sb.append("\"");
- sb.append(index);
- sb.append("\"");
- }
- sb.append(")");
- }
- }
- }
-
- if (!sb.isEmpty()) {
- return sb.toString();
- } else {
- return remainder;
- }
+ return SimpleFunctionHelper.ognlCodeMethods(remainder, type);
}
public static String[] codeSplitSafe(String input, char separator, boolean
trim, boolean keepQuotes) {
diff --git
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/BodyFunctionFactory.java
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/BodyFunctionFactory.java
new file mode 100644
index 000000000000..76b59ee26d20
--- /dev/null
+++
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/BodyFunctionFactory.java
@@ -0,0 +1,293 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.language.simple.functions;
+
+import java.util.List;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Expression;
+import org.apache.camel.language.simple.SimpleExpressionBuilder;
+import org.apache.camel.language.simple.types.SimpleParserException;
+import org.apache.camel.spi.SimpleLanguageFunctionFactory;
+import org.apache.camel.support.builder.ExpressionBuilder;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.OgnlHelper;
+import org.apache.camel.util.StringHelper;
+
+import static
org.apache.camel.language.simple.SimpleFunctionHelper.appendClass;
+import static
org.apache.camel.language.simple.SimpleFunctionHelper.ifStartsWithReturnRemainder;
+import static
org.apache.camel.language.simple.SimpleFunctionHelper.ognlCodeMethods;
+import static org.apache.camel.language.simple.SimpleFunctionHelper.splitOgnl;
+
+/**
+ * Built-in Simple functions for the message body: {@code ${body}}, {@code
${bodyAs(type)}},
+ * {@code ${bodyAs(type).OGNL}}, {@code ${mandatoryBodyAs(type)}}, {@code
${bodyType}}, etc.
+ */
+public final class BodyFunctionFactory implements
SimpleLanguageFunctionFactory {
+
+ @Override
+ public Expression createFunction(CamelContext camelContext, String
function, int index) {
+ if (ObjectHelper.isEqualToAny(function, "body", "in.body")) {
+ return ExpressionBuilder.bodyExpression();
+ } else if (ObjectHelper.equal(function, "bodyType")) {
+ return ExpressionBuilder.bodyTypeExpression();
+ } else if (ObjectHelper.equal(function, "prettyBody")) {
+ return ExpressionBuilder.prettyBodyExpression();
+ } else if (ObjectHelper.equal(function, "toJsonBody")) {
+ return
ExpressionBuilder.toJsonExpression(ExpressionBuilder.bodyExpression(), false);
+ } else if (ObjectHelper.equal(function, "toPrettyJsonBody")) {
+ return
ExpressionBuilder.toJsonExpression(ExpressionBuilder.bodyExpression(), true);
+ } else if (ObjectHelper.equal(function, "bodyOneLine")) {
+ return ExpressionBuilder.bodyOneLine();
+ } else if (ObjectHelper.equal(function, "originalBody")) {
+ return ExpressionBuilder.originalBodyExpression();
+ }
+
+ // bodyAs
+ String remainder = ifStartsWithReturnRemainder("bodyAs(", function);
+ if (remainder != null) {
+ String type = StringHelper.before(remainder, ")");
+ if (type == null) {
+ throw new SimpleParserException("Valid syntax: ${bodyAs(type)}
was: " + function, index);
+ }
+ type = StringHelper.removeQuotes(type);
+ remainder = StringHelper.after(remainder, ")");
+ if (ObjectHelper.isNotEmpty(remainder)) {
+ boolean invalid =
OgnlHelper.isInvalidValidOgnlExpression(remainder);
+ if (invalid) {
+ throw new SimpleParserException("Valid syntax:
${bodyAs(type).OGNL} was: " + function, index);
+ }
+ return SimpleExpressionBuilder.bodyOgnlExpression(type,
remainder);
+ } else {
+ return ExpressionBuilder.bodyExpression(type);
+ }
+ }
+
+ // mandatoryBodyAs
+ remainder = ifStartsWithReturnRemainder("mandatoryBodyAs(", function);
+ if (remainder != null) {
+ String type = StringHelper.before(remainder, ")");
+ if (type == null) {
+ throw new SimpleParserException("Valid syntax:
${mandatoryBodyAs(type)} was: " + function, index);
+ }
+ type = StringHelper.removeQuotes(type);
+ remainder = StringHelper.after(remainder, ")");
+ if (ObjectHelper.isNotEmpty(remainder)) {
+ boolean invalid =
OgnlHelper.isInvalidValidOgnlExpression(remainder);
+ if (invalid) {
+ throw new SimpleParserException(
+ "Valid syntax: ${mandatoryBodyAs(type).OGNL} was:
" + function, index);
+ }
+ return
SimpleExpressionBuilder.mandatoryBodyOgnlExpression(type, remainder);
+ } else {
+ return SimpleExpressionBuilder.mandatoryBodyExpression(type);
+ }
+ }
+
+ // body OGNL (must come after exact matches)
+ remainder = ifStartsWithReturnRemainder("body", function);
+ if (remainder == null) {
+ remainder = ifStartsWithReturnRemainder("in.body", function);
+ }
+ if (remainder != null) {
+ boolean ognlStart = remainder.startsWith(".") ||
remainder.startsWith("?") || remainder.startsWith("[");
+ boolean invalid = !ognlStart ||
OgnlHelper.isInvalidValidOgnlExpression(remainder);
+ if (invalid) {
+ throw new SimpleParserException("Valid syntax: ${body.OGNL}
was: " + function, index);
+ }
+ return SimpleExpressionBuilder.bodyOgnlExpression(remainder);
+ }
+
+ return null;
+ }
+
+ @Override
+ @SuppressWarnings("deprecation")
+ public String createCode(CamelContext camelContext, String function, int
index) {
+ if (ObjectHelper.isEqualToAny(function, "body", "in.body")) {
+ return "body";
+ } else if (ObjectHelper.equal(function, "bodyType")) {
+ return "bodyType(exchange)";
+ } else if (ObjectHelper.equal(function, "prettyBody")) {
+ return "prettyBody(exchange)";
+ } else if (ObjectHelper.equal(function, "toJsonBody")) {
+ return "toJsonBody(exchange, false)";
+ } else if (ObjectHelper.equal(function, "toPrettyJsonBody")) {
+ return "toJsonBody(exchange, true)";
+ } else if (ObjectHelper.equal(function, "bodyOneLine")) {
+ return "bodyOneLine(exchange)";
+ }
+
+ // bodyAsIndex
+ String remainder = ifStartsWithReturnRemainder("bodyAsIndex(",
function);
+ if (remainder != null) {
+ String typeAndIndex = StringHelper.before(remainder, ")");
+ if (typeAndIndex == null) {
+ throw new SimpleParserException(
+ "Valid syntax: ${bodyAsIndex(type, index).OGNL} was: "
+ function, index);
+ }
+ String type = StringHelper.before(typeAndIndex, ",");
+ String idx = StringHelper.after(typeAndIndex, ",");
+ remainder = StringHelper.after(remainder, ")");
+ if (ObjectHelper.isEmpty(type) || ObjectHelper.isEmpty(idx)) {
+ throw new SimpleParserException(
+ "Valid syntax: ${bodyAsIndex(type, index).OGNL} was: "
+ function, index);
+ }
+ type = type.trim();
+ type = appendClass(type);
+ type = type.replace('$', '.');
+ idx = StringHelper.removeQuotes(idx);
+ idx = idx.trim();
+ if (ObjectHelper.isNotEmpty(remainder)) {
+ boolean invalid =
OgnlHelper.isInvalidValidOgnlExpression(remainder);
+ if (invalid) {
+ throw new SimpleParserException(
+ "Valid syntax: ${bodyAsIndex(type, index).OGNL}
was: " + function, index);
+ }
+ return "bodyAsIndex(message, " + type + ", \"" + idx + "\")" +
ognlCodeMethods(remainder, type);
+ } else {
+ return "bodyAsIndex(message, " + type + ", \"" + idx + "\")";
+ }
+ }
+
+ // bodyAs
+ remainder = ifStartsWithReturnRemainder("bodyAs(", function);
+ if (remainder != null) {
+ String type = StringHelper.before(remainder, ")");
+ if (type == null) {
+ throw new SimpleParserException("Valid syntax: ${bodyAs(type)}
was: " + function, index);
+ }
+ type = appendClass(type);
+ type = type.replace('$', '.');
+ type = type.trim();
+ remainder = StringHelper.after(remainder, ")");
+ if (ObjectHelper.isNotEmpty(remainder)) {
+ boolean invalid =
OgnlHelper.isInvalidValidOgnlExpression(remainder);
+ if (invalid) {
+ throw new SimpleParserException("Valid syntax:
${bodyAs(type).OGNL} was: " + function, index);
+ }
+ if (remainder.startsWith("[")) {
+ List<String> parts = splitOgnl(remainder);
+ if (!parts.isEmpty()) {
+ String func = "bodyAsIndex(" + type + ", \"" +
parts.remove(0) + "\")";
+ String last = String.join("", parts);
+ if (!last.isEmpty()) {
+ func += "." + last;
+ }
+ return createCode(camelContext, func, index);
+ }
+ }
+ return "bodyAs(message, " + type + ")" +
ognlCodeMethods(remainder, type);
+ } else {
+ return "bodyAs(message, " + type + ")";
+ }
+ }
+
+ // mandatoryBodyAsIndex
+ remainder = ifStartsWithReturnRemainder("mandatoryBodyAsIndex(",
function);
+ if (remainder != null) {
+ String typeAndIndex = StringHelper.before(remainder, ")");
+ if (typeAndIndex == null) {
+ throw new SimpleParserException(
+ "Valid syntax: ${mandatoryBodyAsIndex(type,
index).OGNL} was: " + function, index);
+ }
+ String type = StringHelper.before(typeAndIndex, ",");
+ String idx = StringHelper.after(typeAndIndex, ",");
+ remainder = StringHelper.after(remainder, ")");
+ if (ObjectHelper.isEmpty(type) || ObjectHelper.isEmpty(idx)) {
+ throw new SimpleParserException(
+ "Valid syntax: ${mandatoryBodyAsIndex(type,
index).OGNL} was: " + function, index);
+ }
+ type = type.trim();
+ type = appendClass(type);
+ type = type.replace('$', '.');
+ idx = StringHelper.removeQuotes(idx);
+ idx = idx.trim();
+ if (ObjectHelper.isNotEmpty(remainder)) {
+ boolean invalid =
OgnlHelper.isInvalidValidOgnlExpression(remainder);
+ if (invalid) {
+ throw new SimpleParserException(
+ "Valid syntax: ${mandatoryBodyAsIndex(type,
index).OGNL} was: " + function, index);
+ }
+ return "mandatoryBodyAsIndex(message, " + type + ", \"" + idx
+ "\")" + ognlCodeMethods(remainder, type);
+ } else {
+ return "mandatoryBodyAsIndex(message, " + type + ", \"" + idx
+ "\")";
+ }
+ }
+
+ // mandatoryBodyAs
+ remainder = ifStartsWithReturnRemainder("mandatoryBodyAs(", function);
+ if (remainder != null) {
+ String type = StringHelper.before(remainder, ")");
+ if (type == null) {
+ throw new SimpleParserException("Valid syntax:
${mandatoryBodyAs(type)} was: " + function, index);
+ }
+ type = appendClass(type);
+ type = type.replace('$', '.');
+ type = type.trim();
+ remainder = StringHelper.after(remainder, ")");
+ if (ObjectHelper.isNotEmpty(remainder)) {
+ boolean invalid =
OgnlHelper.isInvalidValidOgnlExpression(remainder);
+ if (invalid) {
+ throw new SimpleParserException(
+ "Valid syntax: ${mandatoryBodyAs(type).OGNL} was:
" + function, index);
+ }
+ if (remainder.startsWith("[")) {
+ List<String> parts = splitOgnl(remainder);
+ if (!parts.isEmpty()) {
+ String func = "mandatoryBodyAsIndex(" + type + ", \""
+ parts.remove(0) + "\")";
+ String last = String.join("", parts);
+ if (!last.isEmpty()) {
+ func += "." + last;
+ }
+ return createCode(camelContext, func, index);
+ }
+ }
+ return "mandatoryBodyAs(message, " + type + ")" +
ognlCodeMethods(remainder, type);
+ } else {
+ return "mandatoryBodyAs(message, " + type + ")";
+ }
+ }
+
+ // body OGNL (must come after exact matches)
+ remainder = ifStartsWithReturnRemainder("body", function);
+ if (remainder == null) {
+ remainder = ifStartsWithReturnRemainder("in.body", function);
+ }
+ if (remainder != null) {
+ boolean ognlStart = remainder.startsWith(".") ||
remainder.startsWith("?") || remainder.startsWith("[");
+ boolean invalid = !ognlStart ||
OgnlHelper.isInvalidValidOgnlExpression(remainder);
+ if (invalid) {
+ throw new SimpleParserException("Valid syntax: ${body.OGNL}
was: " + function, index);
+ }
+ if (remainder.startsWith("[")) {
+ List<String> parts = splitOgnl(remainder);
+ if (!parts.isEmpty()) {
+ String func = "bodyAsIndex(Object.class, \"" +
parts.remove(0) + "\")";
+ String last = String.join("", parts);
+ if (!last.isEmpty()) {
+ func += "." + last;
+ }
+ return createCode(camelContext, func, index);
+ }
+ }
+ return "body" + ognlCodeMethods(remainder, null);
+ }
+
+ return null;
+ }
+}
diff --git
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/CollateFunctionFactory.java
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/CollateFunctionFactory.java
index c2a3a43c5894..e469426bbe61 100644
---
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/CollateFunctionFactory.java
+++
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/CollateFunctionFactory.java
@@ -24,7 +24,7 @@ import org.apache.camel.spi.SimpleLanguageFunctionFactory;
import org.apache.camel.util.ObjectHelper;
import org.apache.camel.util.StringHelper;
-import static
org.apache.camel.language.simple.ast.SimpleFunctionExpression.ifStartsWithReturnRemainder;
+import static
org.apache.camel.language.simple.SimpleFunctionHelper.ifStartsWithReturnRemainder;
/**
* Built-in Simple function: {@code ${collate(group)}}.
diff --git
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/HeaderFunctionFactory.java
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/HeaderFunctionFactory.java
new file mode 100644
index 000000000000..f1068ea009c1
--- /dev/null
+++
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/HeaderFunctionFactory.java
@@ -0,0 +1,218 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.language.simple.functions;
+
+import java.util.List;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Expression;
+import org.apache.camel.language.simple.SimpleExpressionBuilder;
+import org.apache.camel.language.simple.types.SimpleParserException;
+import org.apache.camel.spi.SimpleLanguageFunctionFactory;
+import org.apache.camel.support.builder.ExpressionBuilder;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.OgnlHelper;
+import org.apache.camel.util.StringHelper;
+
+import static
org.apache.camel.language.simple.SimpleFunctionHelper.appendClass;
+import static
org.apache.camel.language.simple.SimpleFunctionHelper.ifStartsWithReturnRemainder;
+import static
org.apache.camel.language.simple.SimpleFunctionHelper.ognlCodeMethods;
+import static
org.apache.camel.language.simple.SimpleFunctionHelper.parseInHeader;
+import static org.apache.camel.language.simple.SimpleFunctionHelper.splitOgnl;
+
+/**
+ * Built-in Simple functions for message headers: {@code ${header.name}},
{@code ${headerAs(key, type)}},
+ * {@code ${headers}}, {@code ${headers.size}}, etc.
+ */
+public final class HeaderFunctionFactory implements
SimpleLanguageFunctionFactory {
+
+ @Override
+ public Expression createFunction(CamelContext camelContext, String
function, int index) {
+ // headerAs
+ String remainder = ifStartsWithReturnRemainder("headerAs(", function);
+ if (remainder != null) {
+ String keyAndType = StringHelper.before(remainder, ")");
+ if (keyAndType == null) {
+ throw new SimpleParserException("Valid syntax: ${headerAs(key,
type)} was: " + function, index);
+ }
+ String key = StringHelper.before(keyAndType, ",");
+ String type = StringHelper.after(keyAndType, ",");
+ remainder = StringHelper.after(remainder, ")");
+ if (ObjectHelper.isEmpty(key) || ObjectHelper.isEmpty(type) ||
ObjectHelper.isNotEmpty(remainder)) {
+ throw new SimpleParserException("Valid syntax: ${headerAs(key,
type)} was: " + function, index);
+ }
+ key = StringHelper.removeQuotes(key);
+ type = StringHelper.removeQuotes(type);
+ return ExpressionBuilder.headerExpression(key, type);
+ }
+
+ // headers exact matches (must check before parseInHeader to avoid
mis-routing)
+ if ("in.headers".equals(function) || "headers".equals(function)) {
+ return ExpressionBuilder.headersExpression();
+ } else if ("headers.size".equals(function) ||
"headers.size()".equals(function)
+ || "headers.length".equals(function) ||
"headers.length()".equals(function)) {
+ return ExpressionBuilder.headersSizeExpression();
+ }
+
+ // in header function (header.name, in.header.name, headers.name, etc.)
+ remainder = parseInHeader(function);
+ if (remainder != null) {
+ if (remainder.startsWith(".") || remainder.startsWith(":") ||
remainder.startsWith("?")) {
+ remainder = remainder.substring(1);
+ }
+ if (remainder.startsWith("[") && remainder.endsWith("]")) {
+ remainder = remainder.substring(1, remainder.length() - 1);
+ }
+ String key = StringHelper.removeLeadingAndEndingQuotes(remainder);
+
+ boolean invalid = OgnlHelper.isInvalidValidOgnlExpression(key);
+ if (invalid) {
+ throw new SimpleParserException("Valid syntax:
${header.name[key]} was: " + function, index);
+ }
+
+ if (OgnlHelper.isValidOgnlExpression(key)) {
+ return SimpleExpressionBuilder.headersOgnlExpression(key);
+ } else {
+ return ExpressionBuilder.headerExpression(key);
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ @SuppressWarnings("deprecation")
+ public String createCode(CamelContext camelContext, String function, int
index) {
+ // headerAsIndex
+ String remainder = ifStartsWithReturnRemainder("headerAsIndex(",
function);
+ if (remainder != null) {
+ String keyTypeAndIndex = StringHelper.before(remainder, ")");
+ if (keyTypeAndIndex == null) {
+ throw new SimpleParserException(
+ "Valid syntax: ${headerAsIndex(key, type, index)} was:
" + function, index);
+ }
+ String[] parts = keyTypeAndIndex.split(",");
+ if (parts.length != 3) {
+ throw new SimpleParserException(
+ "Valid syntax: ${headerAsIndex(key, type, index)} was:
" + function, index);
+ }
+ String key = parts[0];
+ String type = parts[1];
+ String idx = parts[2];
+ if (ObjectHelper.isEmpty(key) || ObjectHelper.isEmpty(type) ||
ObjectHelper.isEmpty(idx)) {
+ throw new SimpleParserException(
+ "Valid syntax: ${headerAsIndex(key, type, index)} was:
" + function, index);
+ }
+ key = StringHelper.removeQuotes(key);
+ key = key.trim();
+ type = appendClass(type);
+ type = type.replace('$', '.');
+ type = type.trim();
+ idx = StringHelper.removeQuotes(idx);
+ idx = idx.trim();
+ remainder = StringHelper.after(remainder, ")");
+ if (ObjectHelper.isNotEmpty(remainder)) {
+ boolean invalid =
OgnlHelper.isInvalidValidOgnlExpression(remainder);
+ if (invalid) {
+ throw new SimpleParserException(
+ "Valid syntax: ${headerAsIndex(key, type,
index).OGNL} was: " + function, index);
+ }
+ return "headerAsIndex(message, " + type + ", \"" + key + "\",
\"" + idx + "\")"
+ + ognlCodeMethods(remainder, type);
+ } else {
+ return "headerAsIndex(message, " + type + ", \"" + key + "\",
\"" + idx + "\")";
+ }
+ }
+
+ // headerAs
+ remainder = ifStartsWithReturnRemainder("headerAs(", function);
+ if (remainder != null) {
+ String keyAndType = StringHelper.before(remainder, ")");
+ if (keyAndType == null) {
+ throw new SimpleParserException("Valid syntax: ${headerAs(key,
type)} was: " + function, index);
+ }
+ String key = StringHelper.before(keyAndType, ",");
+ String type = StringHelper.after(keyAndType, ",");
+ remainder = StringHelper.after(remainder, ")");
+ if (ObjectHelper.isEmpty(key) || ObjectHelper.isEmpty(type)) {
+ throw new SimpleParserException("Valid syntax: ${headerAs(key,
type)} was: " + function, index);
+ }
+ key = StringHelper.removeQuotes(key);
+ key = key.trim();
+ type = appendClass(type);
+ type = type.replace('$', '.');
+ type = type.trim();
+ return "headerAs(message, \"" + key + "\", " + type + ")" +
ognlCodeMethods(remainder, type);
+ }
+
+ // headers exact matches (must check before parseInHeader)
+ if ("in.headers".equals(function) || "headers".equals(function)) {
+ return "message.getHeaders()";
+ } else if ("headers.size".equals(function) ||
"headers.size()".equals(function)
+ || "headers.length".equals(function) ||
"headers.length()".equals(function)) {
+ return "message.getHeaders().size()";
+ }
+
+ // in header function
+ remainder = parseInHeader(function);
+ if (remainder != null) {
+ if (remainder.startsWith(".") || remainder.startsWith(":") ||
remainder.startsWith("?")) {
+ remainder = remainder.substring(1);
+ }
+ if (remainder.startsWith("[") && remainder.endsWith("]")) {
+ remainder = remainder.substring(1, remainder.length() - 1);
+ }
+ String key = StringHelper.removeLeadingAndEndingQuotes(remainder);
+ key = key.trim();
+
+ boolean invalid = OgnlHelper.isInvalidValidOgnlExpression(key);
+ if (invalid) {
+ throw new SimpleParserException("Valid syntax:
${header.name[key]} was: " + function, index);
+ }
+
+ boolean hasIndex = false;
+ List<String> parts = splitOgnl(key);
+ if (!parts.isEmpty()) {
+ String s = parts.get(0);
+ int pos = s.indexOf('[');
+ if (pos != -1) {
+ hasIndex = true;
+ String before = s.substring(0, pos);
+ String after = s.substring(pos);
+ parts.set(0, before);
+ parts.add(1, after);
+ }
+ }
+ if (hasIndex) {
+ String func = "headerAsIndex(\"" + parts.get(0) + "\",
Object.class, \"" + parts.get(1) + "\")";
+ if (parts.size() > 2) {
+ String last = String.join("", parts.subList(2,
parts.size()));
+ if (!last.isEmpty()) {
+ func += "." + last;
+ }
+ }
+ return createCode(camelContext, func, index);
+ } else if (OgnlHelper.isValidOgnlExpression(key)) {
+ throw new SimpleParserException("Valid syntax: ${headerAs(key,
type).OGNL} was: " + function, index);
+ } else {
+ return "header(message, \"" + key + "\")";
+ }
+ }
+
+ return null;
+ }
+}
diff --git
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/JoinFunctionFactory.java
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/JoinFunctionFactory.java
index ea124ccdd6d2..7032da44246d 100644
---
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/JoinFunctionFactory.java
+++
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/JoinFunctionFactory.java
@@ -26,7 +26,7 @@ import org.apache.camel.util.ObjectHelper;
import org.apache.camel.util.StringHelper;
import org.apache.camel.util.StringQuoteHelper;
-import static
org.apache.camel.language.simple.ast.SimpleFunctionExpression.ifStartsWithReturnRemainder;
+import static
org.apache.camel.language.simple.SimpleFunctionHelper.ifStartsWithReturnRemainder;
/**
* Built-in Simple function: {@code ${join()}} / {@code ${join(separator)}} /
{@code ${join(separator,prefix)}} /
diff --git
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/RandomFunctionFactory.java
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/RandomFunctionFactory.java
index 81e00c9aa521..136d1d48eb5a 100644
---
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/RandomFunctionFactory.java
+++
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/RandomFunctionFactory.java
@@ -24,7 +24,7 @@ import org.apache.camel.spi.SimpleLanguageFunctionFactory;
import org.apache.camel.util.ObjectHelper;
import org.apache.camel.util.StringHelper;
-import static
org.apache.camel.language.simple.ast.SimpleFunctionExpression.ifStartsWithReturnRemainder;
+import static
org.apache.camel.language.simple.SimpleFunctionHelper.ifStartsWithReturnRemainder;
/**
* Built-in Simple function: {@code ${random(max)}} / {@code
${random(min,max)}}.
diff --git
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/SkipFunctionFactory.java
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/SkipFunctionFactory.java
index e5fb4073fcd8..25d8aa2862d3 100644
---
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/SkipFunctionFactory.java
+++
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/SkipFunctionFactory.java
@@ -24,7 +24,7 @@ import org.apache.camel.spi.SimpleLanguageFunctionFactory;
import org.apache.camel.util.ObjectHelper;
import org.apache.camel.util.StringHelper;
-import static
org.apache.camel.language.simple.ast.SimpleFunctionExpression.ifStartsWithReturnRemainder;
+import static
org.apache.camel.language.simple.SimpleFunctionHelper.ifStartsWithReturnRemainder;
/**
* Built-in Simple function: {@code ${skip(number)}}.
diff --git
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/VariableFunctionFactory.java
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/VariableFunctionFactory.java
new file mode 100644
index 000000000000..368368a55885
--- /dev/null
+++
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/VariableFunctionFactory.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.language.simple.functions;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Expression;
+import org.apache.camel.language.simple.SimpleExpressionBuilder;
+import org.apache.camel.language.simple.types.SimpleParserException;
+import org.apache.camel.spi.SimpleLanguageFunctionFactory;
+import org.apache.camel.support.builder.ExpressionBuilder;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.OgnlHelper;
+import org.apache.camel.util.StringHelper;
+
+import static
org.apache.camel.language.simple.SimpleFunctionHelper.appendClass;
+import static
org.apache.camel.language.simple.SimpleFunctionHelper.ifStartsWithReturnRemainder;
+import static
org.apache.camel.language.simple.SimpleFunctionHelper.ognlCodeMethods;
+import static
org.apache.camel.language.simple.SimpleFunctionHelper.parseVariable;
+
+/**
+ * Built-in Simple functions for variables: {@code ${variable.name}}, {@code
${variableAs(key, type)}},
+ * {@code ${variables}}, {@code ${variables.size}}, etc.
+ */
+public final class VariableFunctionFactory implements
SimpleLanguageFunctionFactory {
+
+ @Override
+ public Expression createFunction(CamelContext camelContext, String
function, int index) {
+ // variableAs
+ String remainder = ifStartsWithReturnRemainder("variableAs(",
function);
+ if (remainder != null) {
+ String keyAndType = StringHelper.before(remainder, ")");
+ if (keyAndType == null) {
+ throw new SimpleParserException("Valid syntax:
${variableAs(key, type)} was: " + function, index);
+ }
+ String key = StringHelper.before(keyAndType, ",");
+ String type = StringHelper.after(keyAndType, ",");
+ remainder = StringHelper.after(remainder, ")");
+ if (ObjectHelper.isEmpty(key) || ObjectHelper.isEmpty(type) ||
ObjectHelper.isNotEmpty(remainder)) {
+ throw new SimpleParserException("Valid syntax:
${variableAs(key, type)} was: " + function, index);
+ }
+ key = StringHelper.removeQuotes(key);
+ type = StringHelper.removeQuotes(type);
+ return ExpressionBuilder.variableExpression(key, type);
+ }
+
+ // variables exact matches (must check before parseVariable to avoid
mis-routing)
+ if ("variables".equals(function)) {
+ return ExpressionBuilder.variablesExpression();
+ } else if ("variables.size".equals(function) ||
"variables.size()".equals(function)
+ || "variables.length".equals(function) ||
"variables.length()".equals(function)) {
+ return ExpressionBuilder.variablesSizeExpression();
+ }
+
+ // variable function (variable.name, variables.name, etc.)
+ remainder = parseVariable(function);
+ if (remainder != null) {
+ if (remainder.startsWith(".") || remainder.startsWith(":") ||
remainder.startsWith("?")) {
+ remainder = remainder.substring(1);
+ }
+ if (remainder.startsWith("[") && remainder.endsWith("]")) {
+ remainder = remainder.substring(1, remainder.length() - 1);
+ }
+ String key = StringHelper.removeLeadingAndEndingQuotes(remainder);
+
+ boolean invalid = OgnlHelper.isInvalidValidOgnlExpression(key);
+ if (invalid) {
+ throw new SimpleParserException("Valid syntax:
${variable.name[key]} was: " + function, index);
+ }
+
+ if (OgnlHelper.isValidOgnlExpression(key)) {
+ return SimpleExpressionBuilder.variablesOgnlExpression(key);
+ } else {
+ return ExpressionBuilder.variableExpression(key);
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ @SuppressWarnings("deprecation")
+ public String createCode(CamelContext camelContext, String function, int
index) {
+ // variableAs
+ String remainder = ifStartsWithReturnRemainder("variableAs(",
function);
+ if (remainder != null) {
+ String keyAndType = StringHelper.before(remainder, ")");
+ if (keyAndType == null) {
+ throw new SimpleParserException("Valid syntax:
${variableAs(key, type)} was: " + function, index);
+ }
+ String key = StringHelper.before(keyAndType, ",");
+ String type = StringHelper.after(keyAndType, ",");
+ remainder = StringHelper.after(remainder, ")");
+ if (ObjectHelper.isEmpty(key) || ObjectHelper.isEmpty(type)) {
+ throw new SimpleParserException("Valid syntax:
${variableAs(key, type)} was: " + function, index);
+ }
+ key = StringHelper.removeQuotes(key);
+ key = key.trim();
+ type = appendClass(type);
+ type = type.replace('$', '.');
+ type = type.trim();
+ return "variableAs(exchange, \"" + key + "\", " + type + ")" +
ognlCodeMethods(remainder, type);
+ }
+
+ // variables exact matches (must check before variable prefix)
+ if ("variables".equals(function)) {
+ return "variables(exchange)";
+ } else if ("variables.size".equals(function) ||
"variables.size()".equals(function)
+ || "variables.length".equals(function) ||
"variables.length()".equals(function)) {
+ return "variablesSize(exchange)";
+ }
+
+ // variable (note: only matches "variable" prefix, not "variables" —
preserving original asymmetry)
+ remainder = ifStartsWithReturnRemainder("variable", function);
+ if (remainder != null) {
+ if (remainder.startsWith(".") || remainder.startsWith("?")) {
+ remainder = remainder.substring(1);
+ }
+ if (remainder.startsWith("[") && remainder.endsWith("]")) {
+ remainder = remainder.substring(1, remainder.length() - 1);
+ }
+ String key = StringHelper.removeLeadingAndEndingQuotes(remainder);
+ key = key.trim();
+
+ boolean invalid = OgnlHelper.isInvalidValidOgnlExpression(key);
+ if (invalid) {
+ throw new SimpleParserException("Valid syntax:
${variable.name[key]} was: " + function, index);
+ }
+
+ String idx = null;
+ if (key.endsWith("]")) {
+ idx = StringHelper.between(key, "[", "]");
+ if (idx != null) {
+ key = StringHelper.before(key, "[");
+ }
+ }
+ if (idx != null) {
+ idx = StringHelper.removeLeadingAndEndingQuotes(idx);
+ return "variableAsIndex(exchange, Object.class, \"" + key +
"\", \"" + idx + "\")";
+ } else if (OgnlHelper.isValidOgnlExpression(remainder)) {
+ throw new SimpleParserException("Valid syntax:
${variableAs(key, type)} was: " + function, index);
+ } else {
+ return "variable(exchange, \"" + key + "\")";
+ }
+ }
+
+ return null;
+ }
+}
diff --git
a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/BodyFunctionFactoryTest.java
b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/BodyFunctionFactoryTest.java
new file mode 100644
index 000000000000..e5b3643ab60e
--- /dev/null
+++
b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/BodyFunctionFactoryTest.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.language.simple.functions;
+
+import org.apache.camel.spi.SimpleLanguageFunctionFactory;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+public class BodyFunctionFactoryTest extends
AbstractSimpleFunctionFactoryTestSupport {
+
+ @Override
+ protected SimpleLanguageFunctionFactory createFactory() {
+ return new BodyFunctionFactory();
+ }
+
+ @Test
+ public void testBody() {
+ exchange.getIn().setBody("Hello World");
+ assertEquals("Hello World", evaluate("body", String.class));
+ }
+
+ @Test
+ public void testInBody() {
+ exchange.getIn().setBody("Hello World");
+ assertEquals("Hello World", evaluate("in.body", String.class));
+ }
+
+ @Test
+ public void testBodyAs() {
+ exchange.getIn().setBody(42);
+ assertEquals("42", evaluate("bodyAs(String)", String.class));
+ }
+
+ @Test
+ public void testMandatoryBodyAs() {
+ exchange.getIn().setBody(42);
+ assertEquals(42, evaluate("mandatoryBodyAs(Integer)", Integer.class));
+ }
+
+ @Test
+ public void testBodyOgnl() {
+ exchange.getIn().setBody("Hello World");
+ assertEquals(11, evaluate("body.length()", Integer.class));
+ }
+
+ @Test
+ public void testBodyType() {
+ exchange.getIn().setBody("Hello World");
+ assertNotNull(evaluate("bodyType"));
+ }
+
+ @Test
+ public void testCreateCodeBody() {
+ assertEquals("body", createCode("body"));
+ }
+
+ @Test
+ public void testCreateCodeInBody() {
+ assertEquals("body", createCode("in.body"));
+ }
+
+ @Test
+ public void testCreateCodeBodyType() {
+ assertEquals("bodyType(exchange)", createCode("bodyType"));
+ }
+
+ @Test
+ public void testCreateCodePrettyBody() {
+ assertEquals("prettyBody(exchange)", createCode("prettyBody"));
+ }
+
+ @Test
+ public void testCreateCodeToJsonBody() {
+ assertEquals("toJsonBody(exchange, false)", createCode("toJsonBody"));
+ }
+
+ @Test
+ public void testCreateCodeToPrettyJsonBody() {
+ assertEquals("toJsonBody(exchange, true)",
createCode("toPrettyJsonBody"));
+ }
+
+ @Test
+ public void testCreateCodeBodyOneLine() {
+ assertEquals("bodyOneLine(exchange)", createCode("bodyOneLine"));
+ }
+
+ @Test
+ public void testCreateCodeBodyAs() {
+ assertEquals("bodyAs(message, String.class)",
createCode("bodyAs(String)"));
+ assertEquals("bodyAs(message, java.lang.Integer.class)",
createCode("bodyAs(java.lang.Integer)"));
+ }
+
+ @Test
+ public void testCreateCodeMandatoryBodyAs() {
+ assertEquals("mandatoryBodyAs(message, String.class)",
createCode("mandatoryBodyAs(String)"));
+ }
+
+ @Test
+ public void testCreateCodeBodyOgnl() {
+ assertEquals("body.getLength()", createCode("body.length"));
+ }
+
+ @Test
+ public void testUnknownFunctionReturnsNull() {
+ assertNull(createFactory().createFunction(context, "random(10)", 0));
+ assertNull(createFactory().createCode(context, "random(10)", 0));
+ }
+}
diff --git
a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/HeaderFunctionFactoryTest.java
b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/HeaderFunctionFactoryTest.java
new file mode 100644
index 000000000000..72b972a37ccb
--- /dev/null
+++
b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/HeaderFunctionFactoryTest.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.language.simple.functions;
+
+import java.util.Map;
+
+import org.apache.camel.spi.SimpleLanguageFunctionFactory;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+public class HeaderFunctionFactoryTest extends
AbstractSimpleFunctionFactoryTestSupport {
+
+ @Override
+ protected SimpleLanguageFunctionFactory createFactory() {
+ return new HeaderFunctionFactory();
+ }
+
+ @Test
+ public void testHeader() {
+ exchange.getIn().setHeader("foo", "bar");
+ assertEquals("bar", evaluate("header.foo", String.class));
+ }
+
+ @Test
+ public void testInHeader() {
+ exchange.getIn().setHeader("foo", "bar");
+ assertEquals("bar", evaluate("in.header.foo", String.class));
+ }
+
+ @Test
+ public void testHeaders() {
+ exchange.getIn().setHeader("foo", "bar");
+ Map<?, ?> headers = evaluate("headers", Map.class);
+ assertNotNull(headers);
+ assertEquals("bar", headers.get("foo"));
+ }
+
+ @Test
+ public void testHeaderAs() {
+ exchange.getIn().setHeader("num", "42");
+ assertEquals(42, evaluate("headerAs(num, Integer)", Integer.class));
+ }
+
+ @Test
+ public void testCreateCodeHeader() {
+ assertEquals("header(message, \"foo\")", createCode("header.foo"));
+ }
+
+ @Test
+ public void testCreateCodeInHeader() {
+ assertEquals("header(message, \"foo\")", createCode("in.header.foo"));
+ }
+
+ @Test
+ public void testCreateCodeHeaders() {
+ assertEquals("message.getHeaders()", createCode("headers"));
+ assertEquals("message.getHeaders()", createCode("in.headers"));
+ }
+
+ @Test
+ public void testCreateCodeHeadersSize() {
+ assertEquals("message.getHeaders().size()",
createCode("headers.size"));
+ assertEquals("message.getHeaders().size()",
createCode("headers.size()"));
+ assertEquals("message.getHeaders().size()",
createCode("headers.length"));
+ assertEquals("message.getHeaders().size()",
createCode("headers.length()"));
+ }
+
+ @Test
+ public void testCreateCodeHeaderAs() {
+ assertEquals("headerAs(message, \"num\", Integer.class)",
createCode("headerAs(num, Integer)"));
+ assertEquals("headerAs(message, \"num\", java.lang.Integer.class)",
createCode("headerAs(num, java.lang.Integer)"));
+ }
+
+ @Test
+ public void testUnknownFunctionReturnsNull() {
+ assertNull(createFactory().createFunction(context, "body", 0));
+ assertNull(createFactory().createCode(context, "body", 0));
+ }
+}
diff --git
a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/VariableFunctionFactoryTest.java
b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/VariableFunctionFactoryTest.java
new file mode 100644
index 000000000000..313ec8e4677a
--- /dev/null
+++
b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/VariableFunctionFactoryTest.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.language.simple.functions;
+
+import java.util.Map;
+
+import org.apache.camel.spi.SimpleLanguageFunctionFactory;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+public class VariableFunctionFactoryTest extends
AbstractSimpleFunctionFactoryTestSupport {
+
+ @Override
+ protected SimpleLanguageFunctionFactory createFactory() {
+ return new VariableFunctionFactory();
+ }
+
+ @Test
+ public void testVariable() {
+ context.getVariable("myVar", Object.class);
+ exchange.setVariable("myVar", "hello");
+ assertEquals("hello", evaluate("variable.myVar", String.class));
+ }
+
+ @Test
+ public void testVariableAs() {
+ exchange.setVariable("num", "99");
+ assertEquals(99, evaluate("variableAs(num, Integer)", Integer.class));
+ }
+
+ @Test
+ public void testVariables() {
+ exchange.setVariable("foo", "bar");
+ Map<?, ?> vars = evaluate("variables", Map.class);
+ assertNotNull(vars);
+ assertEquals("bar", vars.get("foo"));
+ }
+
+ @Test
+ public void testCreateCodeVariable() {
+ assertEquals("variable(exchange, \"myVar\")",
createCode("variable.myVar"));
+ }
+
+ @Test
+ public void testCreateCodeVariables() {
+ assertEquals("variables(exchange)", createCode("variables"));
+ }
+
+ @Test
+ public void testCreateCodeVariablesSize() {
+ assertEquals("variablesSize(exchange)", createCode("variables.size"));
+ assertEquals("variablesSize(exchange)",
createCode("variables.size()"));
+ assertEquals("variablesSize(exchange)",
createCode("variables.length"));
+ assertEquals("variablesSize(exchange)",
createCode("variables.length()"));
+ }
+
+ @Test
+ public void testCreateCodeVariableAs() {
+ assertEquals("variableAs(exchange, \"num\", Integer.class)",
createCode("variableAs(num, Integer)"));
+ assertEquals("variableAs(exchange, \"num\", java.lang.Integer.class)",
+ createCode("variableAs(num, java.lang.Integer)"));
+ }
+
+ @Test
+ public void testUnknownFunctionReturnsNull() {
+ assertNull(createFactory().createFunction(context, "body", 0));
+ assertNull(createFactory().createCode(context, "body", 0));
+ }
+}