You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2026/06/04 05:27:06 UTC
(camel) branch main updated: CAMEL-22894: Split SimpleExpressionBuilder into domain-aligned builder classes (#23719)
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 2123fcd28946 CAMEL-22894: Split SimpleExpressionBuilder into domain-aligned builder classes (#23719)
2123fcd28946 is described below
commit 2123fcd289460b3e876bb96d5b9db773575b0a28
Author: Adriano Machado <60...@users.noreply.github.com>
AuthorDate: Thu Jun 4 01:26:59 2026 -0400
CAMEL-22894: Split SimpleExpressionBuilder into domain-aligned builder classes (#23719)
* CAMEL-22894: Split SimpleExpressionBuilder into domain-aligned builder classes
Replace the monolithic SimpleExpressionBuilder (3,523 lines, 100+ static
methods) with seven focused builder classes:
- CollectionExpressionBuilder: list, map, filter, sort, set-header, etc.
- DateExpressionBuilder: date/time expressions with offset support
- FileExpressionBuilder: file name, path, size, extension, etc.
- MathExpressionBuilder: abs, floor, ceil, sum, min, max, average
- MiscExpressionBuilder: uuid, random, type, hash, convert, etc.
- OgnlExpressionBuilder: OGNL body/header/variable/exchange navigation
- StringExpressionBuilder: pad, trim, substring, replace, contains, etc.
KeyedOgnlExpressionAdapter (inner class) moves to OgnlExpressionBuilder.
OFFSET_PATTERN static field moves to DateExpressionBuilder.
All 22 function factories and ast/SimpleFunctionExpression updated to import
their respective domain builder. AttachmentExpressionBuilder (camel-attachments)
updated to use OgnlExpressionBuilder.KeyedOgnlExpressionAdapter.
Pure mechanical refactor: no behavioral change.
Co-Authored-By: Claude Sonnet 4.6 <no...@anthropic.com>
rh-pre-commit.version: 2.4.0
rh-pre-commit.check-secrets: ENABLED
* CAMEL-22894: Fix pre-existing bugs surfaced by SimpleExpressionBuilder split
- shuffleExpression.toString() returned "reverse(...)" due to a copy-paste
error; now returns "shuffle(...)"
- mapExpression.toString() printed compiled Expression[] object refs instead
of the source String[] pairs; now prints the source pairs
- collateExpression(String,String) overwrote the 'exp' field inside
evaluate(), wrapping an already-grouped iterator on every call and causing
a data race under concurrent route execution; fixed by using a local
variable for the grouped expression
- throwExceptionExpression's catch(Exception) block caught the RuntimeException
it had just thrown and double-wrapped it, breaking typed onException handlers;
restructured so RuntimeExceptions propagate unwrapped
- skipExpression(String,String) unboxed a potentially-null Integer without a
null check; now throws IllegalArgumentException with a clear message
- padExpression returned NPE when the string or width expression was null;
now returns null for a null string and throws IllegalArgumentException for
a null width
- floor() and ceil() cast Math.floor/ceil to int, silently overflowing for
values outside Integer range; now returns long
- range(max) in the csimple code path used min=0 while the simple interpreter
used min=1; unified to min=1 to match documented behaviour
Tests added to document each pre-existing bug and verify the fix.
Co-Authored-By: Claude Sonnet 4.6 <no...@anthropic.com>
rh-pre-commit.version: 2.4.0
rh-pre-commit.check-secrets: ENABLED
* CAMEL-22894: Add upgrade guide entries for SimpleExpressionBuilder split and Simple language behavioral changes
Co-Authored-By: Claude Sonnet 4.6 <no...@anthropic.com>
rh-pre-commit.version: 2.4.0
rh-pre-commit.check-secrets: ENABLED
---
.../attachment/AttachmentExpressionBuilder.java | 4 +-
.../simple/CollectionExpressionBuilder.java | 696 ++++
.../language/simple/DateExpressionBuilder.java | 104 +
.../language/simple/FileExpressionBuilder.java | 238 ++
.../language/simple/MathExpressionBuilder.java | 337 ++
.../language/simple/MiscExpressionBuilder.java | 1095 ++++++
.../language/simple/OgnlExpressionBuilder.java | 476 +++
.../language/simple/SimpleExpressionBuilder.java | 3523 --------------------
.../language/simple/StringExpressionBuilder.java | 804 +++++
.../simple/ast/SimpleFunctionExpression.java | 30 +-
.../simple/functions/BodyFunctionFactory.java | 10 +-
.../simple/functions/CollateFunctionFactory.java | 4 +-
.../functions/CollectionFunctionFactory.java | 38 +-
.../simple/functions/CustomFunctionFactory.java | 4 +-
.../simple/functions/DateFunctionFactory.java | 8 +-
.../simple/functions/DirectFunctionFactory.java | 4 +-
.../simple/functions/ExchangeFunctionFactory.java | 10 +-
.../simple/functions/HeaderFunctionFactory.java | 4 +-
.../simple/functions/JoinFunctionFactory.java | 4 +-
.../simple/functions/MathFunctionFactory.java | 18 +-
.../simple/functions/MessageFunctionFactory.java | 4 +-
.../simple/functions/MiscFunctionFactory.java | 42 +-
.../functions/QueryLanguageFunctionFactory.java | 4 +-
.../simple/functions/RandomFunctionFactory.java | 6 +-
.../simple/functions/SkipFunctionFactory.java | 4 +-
.../simple/functions/StringFunctionFactory.java | 38 +-
.../simple/functions/TypeFunctionFactory.java | 6 +-
.../simple/functions/VariableFunctionFactory.java | 4 +-
.../apache/camel/language/simple/SimpleTest.java | 13 +-
.../functions/CollateFunctionFactoryTest.java | 26 +
.../functions/CollectionFunctionFactoryTest.java | 36 +-
.../simple/functions/MathFunctionFactoryTest.java | 14 +
.../simple/functions/MiscFunctionFactoryTest.java | 45 +
.../simple/functions/SkipFunctionFactoryTest.java | 14 +
.../ROOT/pages/camel-4x-upgrade-guide-4_21.adoc | 34 +
35 files changed, 4050 insertions(+), 3651 deletions(-)
diff --git a/components/camel-attachments/src/main/java/org/apache/camel/attachment/AttachmentExpressionBuilder.java b/components/camel-attachments/src/main/java/org/apache/camel/attachment/AttachmentExpressionBuilder.java
index c8fe2220ce82..7f5967613a46 100644
--- a/components/camel-attachments/src/main/java/org/apache/camel/attachment/AttachmentExpressionBuilder.java
+++ b/components/camel-attachments/src/main/java/org/apache/camel/attachment/AttachmentExpressionBuilder.java
@@ -29,7 +29,7 @@ import org.apache.camel.Exchange;
import org.apache.camel.Expression;
import org.apache.camel.NoTypeConversionAvailableException;
import org.apache.camel.RuntimeCamelException;
-import org.apache.camel.language.simple.SimpleExpressionBuilder;
+import org.apache.camel.language.simple.OgnlExpressionBuilder;
import org.apache.camel.support.ExpressionAdapter;
import org.apache.camel.support.ObjectHelper;
import org.apache.camel.support.ResourceHelper;
@@ -219,7 +219,7 @@ public class AttachmentExpressionBuilder {
* @param ognl methods to invoke on the attachment in a simple OGNL syntax
*/
public static Expression attachmentOgnlExpression(final String ognl) {
- return new SimpleExpressionBuilder.KeyedOgnlExpressionAdapter(
+ return new OgnlExpressionBuilder.KeyedOgnlExpressionAdapter(
ognl, "attachmentOgnl(" + ognl + ")",
(exchange, exp) -> {
String key = exp.evaluate(exchange, String.class);
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/CollectionExpressionBuilder.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/CollectionExpressionBuilder.java
new file mode 100644
index 000000000000..54b38e923e31
--- /dev/null
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/CollectionExpressionBuilder.java
@@ -0,0 +1,696 @@
+/*
+ * 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;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.Exchange;
+import org.apache.camel.Expression;
+import org.apache.camel.Predicate;
+import org.apache.camel.spi.ClassResolver;
+import org.apache.camel.support.ExchangeHelper;
+import org.apache.camel.support.ExpressionAdapter;
+import org.apache.camel.support.builder.ExpressionBuilder;
+import org.apache.camel.util.ObjectHelper;
+
+/**
+ * Expression builder for collection and list functions used by the simple language.
+ */
+public final class CollectionExpressionBuilder {
+
+ private CollectionExpressionBuilder() {
+ }
+
+ /**
+ * An expression that returns the distinct values from the expressions
+ */
+ public static Expression distinctExpression(String[] values) {
+ return new ExpressionAdapter() {
+
+ private final Expression[] exps = new Expression[values != null ? values.length : 0];
+
+ @Override
+ public void init(CamelContext context) {
+ for (int i = 0; values != null && i < values.length; i++) {
+ Expression exp = context.resolveLanguage("simple").createExpression(values[i]);
+ exp.init(context);
+ exps[i] = exp;
+ }
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ Set<Object> answer = new LinkedHashSet<>();
+ for (Expression exp : exps) {
+ Object o = exp.evaluate(exchange, Object.class);
+ // this may be an object that we can iterate
+ Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
+ for (Object i : it) {
+ answer.add(i);
+ }
+ }
+ return answer;
+ }
+
+ @Override
+ public String toString() {
+ return "distinct(" + Arrays.toString(values) + ")";
+ }
+ };
+ }
+
+ /**
+ * An expression that returns the elements in reverse order
+ */
+ public static Expression reverseExpression(String[] values) {
+ return new ExpressionAdapter() {
+
+ private final Expression[] exps = new Expression[values != null ? values.length : 0];
+
+ @Override
+ public void init(CamelContext context) {
+ for (int i = 0; values != null && i < values.length; i++) {
+ Expression exp = context.resolveLanguage("simple").createExpression(values[i]);
+ exp.init(context);
+ exps[i] = exp;
+ }
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ List<Object> answer = new ArrayList<>();
+ for (Expression exp : exps) {
+ Object o = exp.evaluate(exchange, Object.class);
+ // this may be an object that we can iterate
+ Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
+ for (Object i : it) {
+ answer.add(i);
+ }
+ }
+ Collections.reverse(answer);
+ return answer;
+ }
+
+ @Override
+ public String toString() {
+ return "reverse(" + Arrays.toString(values) + ")";
+ }
+ };
+ }
+
+ /**
+ * An expression that returns the elements in random order
+ */
+ public static Expression shuffleExpression(String[] values) {
+ return new ExpressionAdapter() {
+
+ private final Expression[] exps = new Expression[values != null ? values.length : 0];
+
+ @Override
+ public void init(CamelContext context) {
+ for (int i = 0; values != null && i < values.length; i++) {
+ Expression exp = context.resolveLanguage("simple").createExpression(values[i]);
+ exp.init(context);
+ exps[i] = exp;
+ }
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ List<Object> answer = new ArrayList<>();
+ for (Expression exp : exps) {
+ Object o = exp.evaluate(exchange, Object.class);
+ // this may be an object that we can iterate
+ Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
+ for (Object i : it) {
+ answer.add(i);
+ }
+ }
+ Collections.shuffle(answer);
+ return answer;
+ }
+
+ @Override
+ public String toString() {
+ return "shuffle(" + Arrays.toString(values) + ")";
+ }
+ };
+ }
+
+ /**
+ * An expression that creates an ArrayList
+ */
+ public static Expression listExpression(String[] values) {
+ return new ExpressionAdapter() {
+
+ private final Expression[] exps = new Expression[values != null ? values.length : 0];
+
+ @Override
+ public void init(CamelContext context) {
+ for (int i = 0; values != null && i < values.length; i++) {
+ Expression exp = context.resolveLanguage("simple").createExpression(values[i]);
+ exp.init(context);
+ exps[i] = exp;
+ }
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ List<Object> answer = new ArrayList<>(values != null ? values.length : 0);
+ for (Expression exp : exps) {
+ Object val = exp.evaluate(exchange, Object.class);
+ answer.add(val);
+ }
+ return answer;
+ }
+
+ @Override
+ public String toString() {
+ return "list(" + Arrays.toString(values) + ")";
+ }
+ };
+ }
+
+ /**
+ * An expression that creates an LinkedHashMap
+ */
+ public static Expression mapExpression(String[] pairs) {
+ return new ExpressionAdapter() {
+
+ private final Expression[] keys = new Expression[pairs != null ? pairs.length / 2 : 0];
+ private final Expression[] values = new Expression[pairs != null ? pairs.length / 2 : 0];
+
+ @Override
+ public void init(CamelContext context) {
+ for (int i = 0, j = 0; pairs != null && i < pairs.length - 1; j++) {
+ String key = pairs[i];
+ String value = pairs[i + 1];
+ Expression exp = context.resolveLanguage("simple").createExpression(key);
+ exp.init(context);
+ keys[j] = exp;
+ exp = context.resolveLanguage("simple").createExpression(value);
+ exp.init(context);
+ values[j] = exp;
+ i = i + 2;
+ }
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ Map<String, Object> answer = new LinkedHashMap<>(keys.length);
+ for (int i = 0; i < keys.length; i++) {
+ String key = keys[i].evaluate(exchange, String.class);
+ Object val = values[i].evaluate(exchange, Object.class);
+ answer.put(key, val);
+ }
+ return answer;
+ }
+
+ @Override
+ public String toString() {
+ return "map(" + Arrays.toString(pairs) + ")";
+ }
+ };
+ }
+
+ /**
+ * Joins together the values from the expression
+ */
+ public static Expression joinExpression(final String expression, final String separator, final String prefix) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+
+ @Override
+ public void init(CamelContext context) {
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ exp = ExpressionBuilder.joinExpression(exp, separator, prefix);
+ exp.init(context);
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ return exp.evaluate(exchange, Object.class);
+ }
+
+ @Override
+ public String toString() {
+ if (prefix != null) {
+ return "join(" + expression + "," + separator + "," + prefix + ")";
+ } else {
+ return "join(" + expression + "," + separator + ")";
+ }
+ }
+ };
+ }
+
+ /**
+ * Split the String values from the expression using the given separator
+ */
+ public static Expression splitStringExpression(final String expression, final String separator) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+
+ @Override
+ public void init(CamelContext context) {
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ String text = exp.evaluate(exchange, String.class);
+ if (text == null) {
+ return null;
+ }
+ return text.split(separator);
+ }
+
+ @Override
+ public String toString() {
+ return "split(" + expression + "," + separator + ")";
+ }
+ };
+ }
+
+ /**
+ * Sorts the expression
+ */
+ public static Expression sortExpression(final String expression, final boolean reverse) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+
+ @Override
+ public void init(CamelContext context) {
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ List answer = new ArrayList<>();
+ Object o = exp.evaluate(exchange, Object.class);
+ // this may be an object that we can iterate
+ Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
+ for (Object i : it) {
+ answer.add(i);
+ }
+ Collections.sort(answer);
+ if (reverse) {
+ Collections.reverse(answer);
+ }
+ return answer;
+ }
+
+ @Override
+ public String toString() {
+ return "sort(" + expression + ")";
+ }
+ };
+ }
+
+ /**
+ * For each value in the source expression then apply the function and return a list of responses from each function
+ */
+ public static Expression forEachExpression(final String source, final String function) {
+ return new ExpressionAdapter() {
+ private CamelContext context;
+ private Expression exp1;
+ private Expression exp2;
+
+ @Override
+ public void init(CamelContext context) {
+ this.context = context;
+ exp1 = context.resolveLanguage("simple").createExpression(source);
+ exp1.init(context);
+ exp2 = context.resolveLanguage("simple").createExpression(function);
+ exp2.init(context);
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ List<Object> answer = new ArrayList<>();
+ Object o = exp1.evaluate(exchange, Object.class);
+ Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
+ for (Object i : it) {
+ // use a dummy exchange as the input is to be the message body
+ Exchange dummy = ExchangeHelper.createCopy(exchange, true);
+ dummy.getMessage().setBody(i);
+ Object out = exp2.evaluate(dummy, Object.class);
+ if (out != null) {
+ answer.add(out);
+ }
+ }
+ return answer;
+ }
+
+ @Override
+ public String toString() {
+ return "forEach(" + source + ", " + function + ")";
+ }
+ };
+ }
+
+ /**
+ * Adds the result of the function to the source list
+ */
+ public static Expression listAddExpression(final String source, final String function) {
+ return new ExpressionAdapter() {
+ private CamelContext context;
+ private Expression exp1;
+ private Expression exp2;
+
+ @Override
+ public void init(CamelContext context) {
+ this.context = context;
+ exp1 = context.resolveLanguage("simple").createExpression(source);
+ exp1.init(context);
+ exp2 = context.resolveLanguage("simple").createExpression(function);
+ exp2.init(context);
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ Collection<Object> col = exp1.evaluate(exchange, Collection.class);
+ if (col != null) {
+ Object value = exp2.evaluate(exchange, Object.class);
+ if (value != null) {
+ col.add(value);
+ }
+ }
+ return col;
+ }
+
+ @Override
+ public String toString() {
+ return "listAdd(" + source + ", " + function + ")";
+ }
+ };
+ }
+
+ /**
+ * Removes the result of the function from the source list
+ */
+ public static Expression listRemoveExpression(final String source, final String function) {
+ return new ExpressionAdapter() {
+ private CamelContext context;
+ private Expression exp1;
+ private Expression exp2;
+
+ @Override
+ public void init(CamelContext context) {
+ this.context = context;
+ exp1 = context.resolveLanguage("simple").createExpression(source);
+ exp1.init(context);
+ exp2 = context.resolveLanguage("simple").createExpression(function);
+ exp2.init(context);
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ List<Object> list = exp1.evaluate(exchange, List.class);
+ if (list != null) {
+ Object value = exp2.evaluate(exchange, Object.class);
+ if (value != null) {
+ boolean removed = list.remove(value);
+ if (!removed) {
+ Integer pos;
+ // special name to remove last
+ if ("last".equals(value)) {
+ pos = list.size() - 1;
+ } else {
+ // this may be an integer
+ pos = context.getTypeConverter().tryConvertTo(int.class, exchange, value);
+ }
+ if (pos != null) {
+ if (pos >= 0 && pos < list.size()) {
+ list.remove((int) pos);
+ }
+ }
+ }
+ }
+ }
+ return list;
+ }
+
+ @Override
+ public String toString() {
+ return "listRemove(" + source + ", " + function + ")";
+ }
+ };
+ }
+
+ /**
+ * Adds the result of the function to the source map
+ */
+ public static Expression mapAddExpression(final String source, final String key, final String function) {
+ return new ExpressionAdapter() {
+ private CamelContext context;
+ private Expression exp1;
+ private Expression exp2;
+
+ @Override
+ public void init(CamelContext context) {
+ this.context = context;
+ exp1 = context.resolveLanguage("simple").createExpression(source);
+ exp1.init(context);
+ exp2 = context.resolveLanguage("simple").createExpression(function);
+ exp2.init(context);
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ Map<String, Object> map = exp1.evaluate(exchange, Map.class);
+ if (map != null) {
+ Object value = exp2.evaluate(exchange, Object.class);
+ if (value != null) {
+ map.put(key, value);
+ }
+ }
+ return map;
+ }
+
+ @Override
+ public String toString() {
+ return "mapAdd(" + source + ", " + key + ", " + function + ")";
+ }
+ };
+ }
+
+ /**
+ * Removes the result of the function from the source map
+ */
+ public static Expression mapRemoveExpression(final String source, final String key) {
+ return new ExpressionAdapter() {
+ private CamelContext context;
+ private Expression exp1;
+
+ @Override
+ public void init(CamelContext context) {
+ this.context = context;
+ exp1 = context.resolveLanguage("simple").createExpression(source);
+ exp1.init(context);
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ Map<String, Object> map = exp1.evaluate(exchange, Map.class);
+ if (map != null) {
+ map.remove(key);
+ }
+ return map;
+ }
+
+ @Override
+ public String toString() {
+ return "mapRemove(" + source + ", " + key + ")";
+ }
+ };
+ }
+
+ /**
+ * Filters the values from the source that matches the predicate function
+ */
+ public static Expression filterExpression(final String source, final String function) {
+ return new ExpressionAdapter() {
+ private CamelContext context;
+ private Expression exp1;
+ private Predicate exp2;
+
+ @Override
+ public void init(CamelContext context) {
+ this.context = context;
+ exp1 = context.resolveLanguage("simple").createExpression(source);
+ exp1.init(context);
+ exp2 = context.resolveLanguage("simple").createPredicate(function);
+ exp2.init(context);
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ List<Object> answer = new ArrayList<>();
+ Object o = exp1.evaluate(exchange, Object.class);
+ Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
+ for (Object i : it) {
+ // use a dummy exchange as the input is to be the message body
+ Exchange dummy = ExchangeHelper.createCopy(exchange, true);
+ dummy.getMessage().setBody(i);
+ if (exp2.matches(dummy)) {
+ answer.add(i);
+ }
+ }
+ return answer;
+ }
+
+ @Override
+ public String toString() {
+ return "filter(" + source + ", " + function + ")";
+ }
+ };
+ }
+
+ /**
+ * Sets the message header with the given expression value
+ */
+ public static Expression setHeaderExpression(final String name, final String type, final String expression) {
+ return new ExpressionAdapter() {
+ private ClassResolver classResolver;
+ private Expression exp;
+
+ @Override
+ public void init(CamelContext context) {
+ classResolver = context.getClassResolver();
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ Class<?> clazz = Object.class;
+ if (type != null) {
+ try {
+ clazz = classResolver.resolveMandatoryClass(type);
+ } catch (ClassNotFoundException e) {
+ throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
+ }
+ }
+ Object value = exp.evaluate(exchange, clazz);
+ if (value != null) {
+ exchange.getMessage().setHeader(name, value);
+ } else {
+ exchange.getMessage().removeHeader(name);
+ }
+ // does not return anything
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return "setHeader(" + name + "," + expression + ")";
+ }
+ };
+ }
+
+ /**
+ * Sets the variable with the given expression value
+ */
+ public static Expression setVariableExpression(final String name, final String type, final String expression) {
+ return new ExpressionAdapter() {
+ private ClassResolver classResolver;
+ private Expression exp;
+
+ @Override
+ public void init(CamelContext context) {
+ classResolver = context.getClassResolver();
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ Class<?> clazz = Object.class;
+ if (type != null) {
+ try {
+ clazz = classResolver.resolveMandatoryClass(type);
+ } catch (ClassNotFoundException e) {
+ throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
+ }
+ }
+ Object value = exp.evaluate(exchange, clazz);
+ if (value != null) {
+ exchange.setVariable(name, value);
+ } else {
+ exchange.removeVariable(name);
+ }
+ // does not return anything
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return "setVariable(" + name + "," + expression + ")";
+ }
+ };
+ }
+
+ /**
+ * Returns a range of number between min and max (exclusive)
+ */
+ public static Expression rangeExpression(final String min, final String max) {
+ return new ExpressionAdapter() {
+ private Expression exp1;
+ private Expression exp2;
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ int num1 = exp1.evaluate(exchange, Integer.class);
+ int num2 = exp2.evaluate(exchange, Integer.class);
+ if (num1 >= 0 && num1 <= num2 && num1 != num2) {
+ List<Integer> answer = new ArrayList<>();
+ for (int i = num1; i < num2; i++) {
+ answer.add(i);
+ }
+ return answer;
+ }
+ return null;
+ }
+
+ @Override
+ public void init(CamelContext context) {
+ exp1 = ExpressionBuilder.simpleExpression(min);
+ exp1.init(context);
+ exp2 = ExpressionBuilder.simpleExpression(max);
+ exp2.init(context);
+ }
+
+ @Override
+ public String toString() {
+ return "range(" + min + "," + max + ")";
+ }
+ };
+ }
+}
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/DateExpressionBuilder.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/DateExpressionBuilder.java
new file mode 100644
index 000000000000..fc9a006d0b81
--- /dev/null
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/DateExpressionBuilder.java
@@ -0,0 +1,104 @@
+/*
+ * 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;
+
+import java.util.Date;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Expression;
+import org.apache.camel.support.ExpressionAdapter;
+import org.apache.camel.support.LanguageHelper;
+
+/**
+ * Expression builder for date/time functions used by the simple language.
+ */
+public final class DateExpressionBuilder {
+
+ private static final Pattern OFFSET_PATTERN = Pattern.compile("([+-])([^+-]+)");
+
+ private DateExpressionBuilder() {
+ }
+
+ public static Expression dateExpression(final String command) {
+ return dateExpression(command, null, null);
+ }
+
+ public static Expression dateExpression(final String command, final String pattern) {
+ return dateExpression(command, null, pattern);
+ }
+
+ public static Expression dateExpression(
+ final String commandWithOffsets, final String timezone,
+ final String pattern) {
+ final String command = commandWithOffsets.split("[+-]", 2)[0].trim();
+ final List<Long> offsets = LanguageHelper.captureOffsets(commandWithOffsets, OFFSET_PATTERN);
+
+ return new ExpressionAdapter() {
+ @Override
+ public Object evaluate(Exchange exchange) {
+ Date date = evalDate(exchange, command);
+ Object answer = LanguageHelper.applyDateOffsets(date, offsets, pattern, timezone);
+ if ("millis".equals(command) && answer instanceof Date d) {
+ answer = d.getTime();
+ }
+ return answer;
+ }
+
+ @Override
+ public String toString() {
+ if (timezone != null && pattern != null) {
+ return "date(" + commandWithOffsets + ":" + timezone + ":" + pattern + ")";
+ } else if (pattern != null) {
+ return "date(" + commandWithOffsets + ":" + pattern + ")";
+ } else {
+ return "date(" + commandWithOffsets + ")";
+ }
+ }
+ };
+ }
+
+ private static Date evalDate(Exchange exchange, String command) {
+ Date date;
+ if ("now".equals(command) || "millis".equals(command)) {
+ date = new Date();
+ } else if ("exchangeCreated".equals(command)) {
+ date = LanguageHelper.dateFromExchangeCreated(exchange);
+ } else if (command.startsWith("header.")) {
+ date = LanguageHelper.dateFromHeader(exchange, command, (e, o) -> tryConvertingAsDate(e, o, command));
+ } else if (command.startsWith("variable.")) {
+ date = LanguageHelper.dateFromVariable(exchange, command, (e, o) -> tryConvertingAsDate(e, o, command));
+ } else if (command.startsWith("exchangeProperty.")) {
+ date = LanguageHelper.dateFromExchangeProperty(exchange, command, (e, o) -> tryConvertingAsDate(e, o, command));
+ } else if ("file".equals(command)) {
+ date = LanguageHelper.dateFromFileLastModified(exchange, command);
+ } else {
+ throw new IllegalArgumentException("Command not supported for dateExpression: " + command);
+ }
+ return date;
+ }
+
+ private static Date tryConvertingAsDate(Exchange exchange, Object obj, String command) {
+ final Date date = exchange.getContext().getTypeConverter().tryConvertTo(Date.class, exchange, obj);
+ if (date == null) {
+ throw new IllegalArgumentException("Cannot find Date/long object at command: " + command);
+ }
+
+ return date;
+ }
+}
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/FileExpressionBuilder.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/FileExpressionBuilder.java
new file mode 100644
index 000000000000..a93a5e0136f1
--- /dev/null
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/FileExpressionBuilder.java
@@ -0,0 +1,238 @@
+/*
+ * 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;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Expression;
+import org.apache.camel.support.ExpressionAdapter;
+import org.apache.camel.util.FileUtil;
+
+/**
+ * Expression builder for file-related functions used by the simple language.
+ */
+public final class FileExpressionBuilder {
+
+ private FileExpressionBuilder() {
+ }
+
+ public static Expression fileNameExpression() {
+ return new ExpressionAdapter() {
+ @Override
+ public Object evaluate(Exchange exchange) {
+ return exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
+ }
+
+ @Override
+ public String toString() {
+ return "file:name";
+ }
+ };
+ }
+
+ public static Expression fileOnlyNameExpression() {
+ return new ExpressionAdapter() {
+ @Override
+ public Object evaluate(Exchange exchange) {
+ String answer = exchange.getIn().getHeader(Exchange.FILE_NAME_ONLY, String.class);
+ if (answer == null) {
+ answer = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
+ answer = FileUtil.stripPath(answer);
+ }
+ return answer;
+ }
+
+ @Override
+ public String toString() {
+ return "file:onlyname";
+ }
+ };
+ }
+
+ public static Expression fileNameNoExtensionExpression() {
+ return new ExpressionAdapter() {
+ @Override
+ public Object evaluate(Exchange exchange) {
+ String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
+ return FileUtil.stripExt(name);
+ }
+
+ @Override
+ public String toString() {
+ return "file:name.noext";
+ }
+ };
+ }
+
+ public static Expression fileNameNoExtensionSingleExpression() {
+ return new ExpressionAdapter() {
+ @Override
+ public Object evaluate(Exchange exchange) {
+ String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
+ return FileUtil.stripExt(name, true);
+ }
+
+ @Override
+ public String toString() {
+ return "file:name.noext.single";
+ }
+ };
+ }
+
+ public static Expression fileOnlyNameNoExtensionExpression() {
+ return new ExpressionAdapter() {
+ @Override
+ public Object evaluate(Exchange exchange) {
+ String name = fileOnlyNameExpression().evaluate(exchange, String.class);
+ return FileUtil.stripExt(name);
+ }
+
+ @Override
+ public String toString() {
+ return "file:onlyname.noext";
+ }
+ };
+ }
+
+ public static Expression fileOnlyNameNoExtensionSingleExpression() {
+ return new ExpressionAdapter() {
+ @Override
+ public Object evaluate(Exchange exchange) {
+ String name = fileOnlyNameExpression().evaluate(exchange, String.class);
+ return FileUtil.stripExt(name, true);
+ }
+
+ @Override
+ public String toString() {
+ return "file:onlyname.noext.single";
+ }
+ };
+ }
+
+ public static Expression fileExtensionExpression() {
+ return new ExpressionAdapter() {
+ @Override
+ public Object evaluate(Exchange exchange) {
+ String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
+ return FileUtil.onlyExt(name);
+ }
+
+ @Override
+ public String toString() {
+ return "file:ext";
+ }
+ };
+ }
+
+ public static Expression fileExtensionSingleExpression() {
+ return new ExpressionAdapter() {
+ @Override
+ public Object evaluate(Exchange exchange) {
+ String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
+ return FileUtil.onlyExt(name, true);
+ }
+
+ @Override
+ public String toString() {
+ return "file:ext.single";
+ }
+ };
+ }
+
+ public static Expression fileParentExpression() {
+ return new ExpressionAdapter() {
+ @Override
+ public Object evaluate(Exchange exchange) {
+ return exchange.getIn().getHeader("CamelFileParent", String.class);
+ }
+
+ @Override
+ public String toString() {
+ return "file:parent";
+ }
+ };
+ }
+
+ public static Expression filePathExpression() {
+ return new ExpressionAdapter() {
+ @Override
+ public Object evaluate(Exchange exchange) {
+ return exchange.getIn().getHeader("CamelFilePath", String.class);
+ }
+
+ @Override
+ public String toString() {
+ return "file:path";
+ }
+ };
+ }
+
+ public static Expression fileAbsolutePathExpression() {
+ return new ExpressionAdapter() {
+ @Override
+ public Object evaluate(Exchange exchange) {
+ return exchange.getIn().getHeader("CamelFileAbsolutePath", String.class);
+ }
+
+ @Override
+ public String toString() {
+ return "file:absolute.path";
+ }
+ };
+ }
+
+ public static Expression fileAbsoluteExpression() {
+ return new ExpressionAdapter() {
+ @Override
+ public Object evaluate(Exchange exchange) {
+ return exchange.getIn().getHeader("CamelFileAbsolute", Boolean.class);
+ }
+
+ @Override
+ public String toString() {
+ return "file:absolute";
+ }
+ };
+ }
+
+ public static Expression fileSizeExpression() {
+ return new ExpressionAdapter() {
+ @Override
+ public Object evaluate(Exchange exchange) {
+ return exchange.getIn().getHeader(Exchange.FILE_LENGTH, Long.class);
+ }
+
+ @Override
+ public String toString() {
+ return "file:length";
+ }
+ };
+ }
+
+ public static Expression fileLastModifiedExpression() {
+ return new ExpressionAdapter() {
+ @Override
+ public Object evaluate(Exchange exchange) {
+ return exchange.getIn().getHeader(Exchange.FILE_LAST_MODIFIED, Long.class);
+ }
+
+ @Override
+ public String toString() {
+ return "file:modified";
+ }
+ };
+ }
+}
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/MathExpressionBuilder.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/MathExpressionBuilder.java
new file mode 100644
index 000000000000..bcc830538791
--- /dev/null
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/MathExpressionBuilder.java
@@ -0,0 +1,337 @@
+/*
+ * 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;
+
+import java.util.Arrays;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+import org.apache.camel.Expression;
+import org.apache.camel.support.ExpressionAdapter;
+import org.apache.camel.util.ObjectHelper;
+
+/**
+ * Expression builder for numeric and math functions used by the simple language.
+ */
+public final class MathExpressionBuilder {
+
+ private MathExpressionBuilder() {
+ }
+
+ /**
+ * Converts the given expressions to a number and return the absolute value (uses message body if expression is
+ * null)
+ */
+ public static Expression absExpression(final String expression) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+
+ @Override
+ public void init(CamelContext context) {
+ if (expression != null) {
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ }
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ Long value;
+ if (exp != null) {
+ value = exp.evaluate(exchange, Long.class);
+ } else {
+ value = exchange.getMessage().getBody(Long.class);
+ }
+ if (value != null) {
+ value = Math.abs(value);
+ }
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ if (expression != null) {
+ return "abs(" + expression + ")";
+ } else {
+ return "abs()";
+ }
+ }
+ };
+ }
+
+ /**
+ * Converts the given expressions to a floating number and return the floor value (uses message body if expression
+ * is null)
+ */
+ public static Expression floorExpression(final String expression) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+
+ @Override
+ public void init(CamelContext context) {
+ if (expression != null) {
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ }
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ Double value;
+ if (exp != null) {
+ value = exp.evaluate(exchange, Double.class);
+ } else {
+ value = exchange.getMessage().getBody(Double.class);
+ }
+ if (value != null) {
+ double d = Math.floor(value);
+ return (long) d;
+ }
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ if (expression != null) {
+ return "floor(" + expression + ")";
+ } else {
+ return "floor()";
+ }
+ }
+ };
+ }
+
+ /**
+ * Converts the given expressions to a floating number and return the ceil value (uses message body if expression is
+ * null)
+ */
+ public static Expression ceilExpression(final String expression) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+
+ @Override
+ public void init(CamelContext context) {
+ if (expression != null) {
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ }
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ Double value;
+ if (exp != null) {
+ value = exp.evaluate(exchange, Double.class);
+ } else {
+ value = exchange.getMessage().getBody(Double.class);
+ }
+ if (value != null) {
+ double d = Math.ceil(value);
+ return (long) d;
+ }
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ if (expression != null) {
+ return "ceil(" + expression + ")";
+ } else {
+ return "ceil()";
+ }
+ }
+ };
+ }
+
+ /**
+ * An expression that converts the expressions to number and sum the values
+ */
+ public static Expression sumExpression(String[] numbers) {
+ return new ExpressionAdapter() {
+
+ private final Expression[] exps = new Expression[numbers != null ? numbers.length : 0];
+
+ @Override
+ public void init(CamelContext context) {
+ for (int i = 0; numbers != null && i < numbers.length; i++) {
+ Expression exp = context.resolveLanguage("simple").createExpression(numbers[i]);
+ exp.init(context);
+ exps[i] = exp;
+ }
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ Long answer = null;
+ for (Expression exp : exps) {
+ Object o = exp.evaluate(exchange, Object.class);
+ // this may be an object that we can iterate
+ Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
+ for (Object i : it) {
+ Long val = exchange.getContext().getTypeConverter().tryConvertTo(Long.class, exchange, i);
+ if (val != null) {
+ if (answer == null) {
+ answer = 0L;
+ }
+ answer += val;
+ }
+ }
+ }
+ return answer;
+ }
+
+ @Override
+ public String toString() {
+ return "sum(" + Arrays.toString(numbers) + ")";
+ }
+ };
+ }
+
+ /**
+ * An expression that converts the expressions to number and returns the maximum number
+ */
+ public static Expression maxExpression(String[] numbers) {
+ return new ExpressionAdapter() {
+
+ private final Expression[] exps = new Expression[numbers != null ? numbers.length : 0];
+
+ @Override
+ public void init(CamelContext context) {
+ for (int i = 0; numbers != null && i < numbers.length; i++) {
+ Expression exp = context.resolveLanguage("simple").createExpression(numbers[i]);
+ exp.init(context);
+ exps[i] = exp;
+ }
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ Long answer = null;
+ for (Expression exp : exps) {
+ Object o = exp.evaluate(exchange, Object.class);
+ // this may be an object that we can iterate
+ Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
+ for (Object i : it) {
+ Long val = exchange.getContext().getTypeConverter().tryConvertTo(Long.class, exchange, i);
+ if (val != null) {
+ if (answer == null) {
+ answer = val;
+ }
+ answer = Math.max(answer, val);
+ }
+ }
+ }
+ return answer;
+ }
+
+ @Override
+ public String toString() {
+ return "max(" + Arrays.toString(numbers) + ")";
+ }
+ };
+ }
+
+ /**
+ * An expression that converts the expressions to number and returns the minimum number
+ */
+ public static Expression minExpression(String[] numbers) {
+ return new ExpressionAdapter() {
+
+ private final Expression[] exps = new Expression[numbers != null ? numbers.length : 0];
+
+ @Override
+ public void init(CamelContext context) {
+ for (int i = 0; numbers != null && i < numbers.length; i++) {
+ Expression exp = context.resolveLanguage("simple").createExpression(numbers[i]);
+ exp.init(context);
+ exps[i] = exp;
+ }
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ Long answer = null;
+ for (Expression exp : exps) {
+ Object o = exp.evaluate(exchange, Object.class);
+ // this may be an object that we can iterate
+ Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
+ for (Object i : it) {
+ Long val = exchange.getContext().getTypeConverter().tryConvertTo(Long.class, exchange, i);
+ if (val != null) {
+ if (answer == null) {
+ answer = val;
+ }
+ answer = Math.min(answer, val);
+ }
+ }
+ }
+ return answer;
+ }
+
+ @Override
+ public String toString() {
+ return "min(" + Arrays.toString(numbers) + ")";
+ }
+ };
+ }
+
+ /**
+ * An expression that converts the expressions to number and returns the average number
+ */
+ public static Expression averageExpression(String[] numbers) {
+ return new ExpressionAdapter() {
+
+ private final Expression[] exps = new Expression[numbers != null ? numbers.length : 0];
+
+ @Override
+ public void init(CamelContext context) {
+ for (int i = 0; numbers != null && i < numbers.length; i++) {
+ Expression exp = context.resolveLanguage("simple").createExpression(numbers[i]);
+ exp.init(context);
+ exps[i] = exp;
+ }
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ Long answer = null;
+ int counter = 0;
+ for (Expression exp : exps) {
+ Object o = exp.evaluate(exchange, Object.class);
+ // this may be an object that we can iterate
+ Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
+ for (Object i : it) {
+ Long val = exchange.getContext().getTypeConverter().tryConvertTo(Long.class, exchange, i);
+ if (val != null) {
+ if (answer == null) {
+ answer = 0L;
+ }
+ answer += val;
+ counter++;
+ }
+ }
+ }
+ return answer != null ? answer / counter : null;
+ }
+
+ @Override
+ public String toString() {
+ return "average(" + Arrays.toString(numbers) + ")";
+ }
+ };
+ }
+}
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/MiscExpressionBuilder.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/MiscExpressionBuilder.java
new file mode 100644
index 000000000000..6114be621b1b
--- /dev/null
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/MiscExpressionBuilder.java
@@ -0,0 +1,1095 @@
+/*
+ * 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;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Constructor;
+import java.security.DigestInputStream;
+import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.Exchange;
+import org.apache.camel.Expression;
+import org.apache.camel.Predicate;
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.spi.ClassResolver;
+import org.apache.camel.spi.ExchangeFormatter;
+import org.apache.camel.spi.Language;
+import org.apache.camel.spi.SimpleFunctionRegistry;
+import org.apache.camel.spi.UuidGenerator;
+import org.apache.camel.support.CamelContextHelper;
+import org.apache.camel.support.ClassicUuidGenerator;
+import org.apache.camel.support.DefaultUuidGenerator;
+import org.apache.camel.support.ExpressionAdapter;
+import org.apache.camel.support.LanguageHelper;
+import org.apache.camel.support.MessageHelper;
+import org.apache.camel.support.RandomUuidGenerator;
+import org.apache.camel.support.ResourceHelper;
+import org.apache.camel.support.ShortUuidGenerator;
+import org.apache.camel.support.SimpleUuidGenerator;
+import org.apache.camel.support.builder.ExpressionBuilder;
+import org.apache.camel.support.builder.PredicateBuilder;
+import org.apache.camel.util.IOHelper;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.SkipIterator;
+import org.apache.camel.util.StringHelper;
+import org.apache.camel.util.json.JsonArray;
+import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.Jsonable;
+
+/**
+ * Expression builder for miscellaneous functions used by the simple language.
+ */
+public final class MiscExpressionBuilder {
+
+ private MiscExpressionBuilder() {
+ }
+
+ /**
+ * Returns the message history (including exchange details or not)
+ */
+ public static Expression messageHistoryExpression(final boolean detailed) {
+ return new ExpressionAdapter() {
+
+ private ExchangeFormatter formatter;
+
+ @Override
+ public void init(CamelContext context) {
+ if (detailed) {
+ // use the exchange formatter to log exchange details
+ formatter = getOrCreateExchangeFormatter(context);
+ }
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ return MessageHelper.dumpMessageHistoryStacktrace(exchange, formatter, false);
+ }
+
+ private ExchangeFormatter getOrCreateExchangeFormatter(CamelContext camelContext) {
+ return LanguageHelper.getOrCreateExchangeFormatter(camelContext, formatter);
+ }
+
+ @Override
+ public String toString() {
+ return "messageHistory(" + detailed + ")";
+ }
+ };
+ }
+
+ /**
+ * Returns an iterator to collate (iterate) the given expression
+ */
+ public static Expression collateExpression(final String expression, final int group) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+
+ @Override
+ public void init(CamelContext context) {
+ // first use simple then create the group expression
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ exp = ExpressionBuilder.groupIteratorExpression(exp, null, Integer.toString(group), false);
+ exp.init(context);
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ return exp.evaluate(exchange, Object.class);
+ }
+
+ @Override
+ public String toString() {
+ return "collate(" + expression + "," + group + ")";
+ }
+ };
+ }
+
+ /**
+ * Returns an iterator to collate (iterate) the given expression
+ */
+ public static Expression collateExpression(final String expression, final String group) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+ private Expression num;
+
+ @Override
+ public void init(CamelContext context) {
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ num = context.resolveLanguage("simple").createExpression(group);
+ num.init(context);
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ Integer n = num.evaluate(exchange, Integer.class);
+ Expression grouped = ExpressionBuilder.groupIteratorExpression(exp, null, Integer.toString(n), false);
+ grouped.init(exchange.getContext());
+ return grouped.evaluate(exchange, Object.class);
+ }
+
+ @Override
+ public String toString() {
+ return "collate(" + expression + "," + group + ")";
+ }
+ };
+ }
+
+ /**
+ * Returns an iterator to skip (iterate) the given expression
+ */
+ public static Expression skipExpression(final String expression, final int number) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+
+ @Override
+ public void init(CamelContext context) {
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ return skipIteratorExpression(exp, number).evaluate(exchange, Object.class);
+ }
+
+ @Override
+ public String toString() {
+ return "skip(" + expression + "," + number + ")";
+ }
+ };
+ }
+
+ /**
+ * Returns an iterator to skip (iterate) the given expression
+ */
+ public static Expression skipExpression(final String expression, final String number) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+ private Expression num;
+
+ @Override
+ public void init(CamelContext context) {
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ num = context.resolveLanguage("simple").createExpression(number);
+ num.init(context);
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ Integer n = num.evaluate(exchange, Integer.class);
+ if (n == null) {
+ throw new IllegalArgumentException("skip number expression evaluated to null: " + number);
+ }
+ return skipIteratorExpression(exp, n).evaluate(exchange, Object.class);
+ }
+
+ @Override
+ public String toString() {
+ return "skip(" + expression + "," + number + ")";
+ }
+ };
+ }
+
+ /**
+ * Whether the expression is empty or having a list/map which has no elements.
+ */
+ public static Expression isEmptyExpression(final String expression) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+
+ @Override
+ public void init(CamelContext context) {
+ if (expression != null) {
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ }
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ Object body;
+ if (exp != null) {
+ body = exp.evaluate(exchange, Object.class);
+ } else {
+ body = exchange.getMessage().getBody();
+ }
+ // this may be an object that we can iterate
+ Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(body);
+ for (Object o : it) {
+ if (o != null) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ if (expression != null) {
+ return "isEmpty(" + expression + ")";
+ } else {
+ return "isEmpty()";
+ }
+ }
+ };
+ }
+
+ /**
+ * Whether the expression is an alphabetic String
+ */
+ public static Expression isAlphaExpression(final String expression) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+
+ @Override
+ public void init(CamelContext context) {
+ if (expression != null) {
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ }
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ String body;
+ if (exp != null) {
+ body = exp.evaluate(exchange, String.class);
+ } else {
+ body = exchange.getMessage().getBody(String.class);
+ }
+ if (body == null || body.isBlank()) {
+ return false;
+ }
+ for (int i = 0; i < body.length(); i++) {
+ char ch = body.charAt(i);
+ if (!Character.isLetter(ch)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ if (expression != null) {
+ return "isAlpha(" + expression + ")";
+ } else {
+ return "isAlpha()";
+ }
+ }
+ };
+ }
+
+ /**
+ * Whether the expression is a number (integral or floating)
+ */
+ public static Expression isNumericExpression(final String expression) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+
+ @Override
+ public void init(CamelContext context) {
+ if (expression != null) {
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ }
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ String body;
+ if (exp != null) {
+ body = exp.evaluate(exchange, String.class);
+ } else {
+ body = exchange.getMessage().getBody(String.class);
+ }
+ if (body == null || body.isBlank()) {
+ return false;
+ }
+ for (int i = 0; i < body.length(); i++) {
+ char ch = body.charAt(i);
+ if (!Character.isDigit(ch)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ if (expression != null) {
+ return "isNumeric(" + expression + ")";
+ } else {
+ return "isNumeric()";
+ }
+ }
+ };
+ }
+
+ /**
+ * Whether the expression is an alphabetic or numeric String
+ */
+ public static Expression isAlphaNumericExpression(final String expression) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+
+ @Override
+ public void init(CamelContext context) {
+ if (expression != null) {
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ }
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ String body;
+ if (exp != null) {
+ body = exp.evaluate(exchange, String.class);
+ } else {
+ body = exchange.getMessage().getBody(String.class);
+ }
+ if (body == null || body.isBlank()) {
+ return false;
+ }
+ for (int i = 0; i < body.length(); i++) {
+ char ch = body.charAt(i);
+ if (!Character.isLetterOrDigit(ch)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ if (expression != null) {
+ return "isAlphaNumeric(" + expression + ")";
+ } else {
+ return "isAlphaNumeric()";
+ }
+ }
+ };
+ }
+
+ /**
+ * Returns the opposite result of the predicate
+ */
+ public static Expression isNotPredicate(final String predicate) {
+ return new ExpressionAdapter() {
+ private Predicate pred;
+
+ @Override
+ public void init(CamelContext context) {
+ pred = PredicateBuilder.not(context.resolveLanguage("simple").createPredicate(predicate));
+ pred.init(context);
+ }
+
+ public Object evaluate(Exchange exchange) {
+ return pred.matches(exchange);
+ }
+
+ @Override
+ public String toString() {
+ if (predicate != null) {
+ return "not(" + predicate + ")";
+ } else {
+ return "not()";
+ }
+ }
+ };
+ }
+
+ /**
+ * What kind of type is the expression
+ */
+ public static Expression kindOfTypeExpression(final String expression) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+
+ @Override
+ public void init(CamelContext context) {
+ if (expression != null) {
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ }
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ Object value;
+ if (exp != null) {
+ value = exp.evaluate(exchange, Object.class);
+ } else {
+ value = exchange.getMessage().getBody();
+ }
+ if (value != null) {
+ Class<?> type = value.getClass();
+ if (ObjectHelper.isNumericType(type)) {
+ return "number";
+ } else if (boolean.class == type || Boolean.class == type) {
+ return "boolean";
+ } else if (value instanceof CharSequence) {
+ return "string";
+ } else if (ObjectHelper.isPrimitiveArrayType(type) || value instanceof Collection
+ || value instanceof Map<?, ?>) {
+ return "array";
+ } else {
+ return "object";
+ }
+ }
+ return "null";
+ }
+
+ @Override
+ public String toString() {
+ if (expression != null) {
+ return "kindOfType(" + expression + ")";
+ } else {
+ return "kindOfType()";
+ }
+ }
+ };
+ }
+
+ /**
+ * Throws an exception with the given message.
+ */
+ public static Expression throwExceptionExpression(String msg, String type) {
+ return new ExpressionAdapter() {
+ private Class<?> clazz;
+ private Expression exp;
+
+ @Override
+ public void init(CamelContext context) {
+ if (type == null) {
+ clazz = IllegalArgumentException.class;
+ } else {
+ try {
+ clazz = context.getClassResolver().resolveMandatoryClass(type);
+ } catch (ClassNotFoundException e) {
+ throw CamelExecutionException.wrapRuntimeException(e);
+ }
+ }
+ exp = context.resolveLanguage("simple").createExpression(msg);
+ exp.init(context);
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ String text = exp.evaluate(exchange, String.class);
+ try {
+ // create a new exception of that type, and provide the message
+ Constructor<?> constructor = clazz.getConstructor(String.class);
+ Exception cause = (Exception) constructor.newInstance(text);
+ if (cause instanceof RuntimeException re) {
+ throw re;
+ }
+ throw new RuntimeCamelException(cause);
+ } catch (RuntimeException re) {
+ throw re;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "throwException(" + msg + ")";
+ }
+ };
+ }
+
+ /**
+ * Assert the expression
+ */
+ public static Expression assertExpression(String expression, String message) {
+ return new ExpressionAdapter() {
+ private Predicate pred;
+
+ @Override
+ public void init(CamelContext context) {
+ pred = context.resolveLanguage("simple").createPredicate(expression);
+ pred.init(context);
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ if (!pred.matches(exchange)) {
+ throw new SimpleAssertionException(message);
+ }
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return "assert(" + pred + ", " + message + ")";
+ }
+ };
+ }
+
+ /**
+ * Converts the result of the expression to the given type
+ */
+ public static Expression convertToExpression(final String expression, final String type) {
+ return new ExpressionAdapter() {
+ private Class<?> clazz;
+ private Expression exp;
+
+ @Override
+ public void init(CamelContext context) {
+ try {
+ clazz = context.getClassResolver().resolveMandatoryClass(type);
+ } catch (ClassNotFoundException e) {
+ throw CamelExecutionException.wrapRuntimeException(e);
+ }
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ return exp.evaluate(exchange, clazz);
+ }
+
+ @Override
+ public String toString() {
+ if (expression != null) {
+ return "convertTo(" + expression + ", " + type + ")";
+ } else {
+ return "convertTo(" + type + ")";
+ }
+ }
+ };
+ }
+
+ /**
+ * Converts the result of the expression to the given type and invoking methods on the converted object defined in a
+ * simple OGNL notation
+ */
+ public static Expression convertToOgnlExpression(final String expression, final String type, final String ognl) {
+ return new ExpressionAdapter() {
+ private Class<?> clazz;
+ private Expression exp;
+ private Language bean;
+
+ @Override
+ public void init(CamelContext context) {
+ try {
+ clazz = context.getClassResolver().resolveMandatoryClass(type);
+ } catch (ClassNotFoundException e) {
+ throw CamelExecutionException.wrapRuntimeException(e);
+ }
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ bean = context.resolveLanguage("bean");
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ Object body = exp.evaluate(exchange, clazz);
+ if (body == null) {
+ return null;
+ }
+ Expression ognlExp = bean.createExpression(null, new Object[] { null, body, ognl });
+ ognlExp.init(exchange.getContext());
+ return ognlExp.evaluate(exchange, Object.class);
+ }
+
+ @Override
+ public String toString() {
+ if (expression != null) {
+ return "convertToOgnl(" + expression + ", " + type + ")." + ognl;
+ } else {
+ return "convertToOgnl(" + type + ")." + ognl;
+ }
+ }
+ };
+ }
+
+ /**
+ * A ternary condition expression
+ */
+ public static Expression iifExpression(final String predicate, final String trueValue, final String falseValue) {
+ return new ExpressionAdapter() {
+ private Predicate pred;
+ private Expression expTrue;
+ private Expression expFalse;
+
+ @Override
+ public void init(CamelContext context) {
+ pred = context.resolveLanguage("simple").createPredicate(predicate);
+ pred.init(context);
+ expTrue = context.resolveLanguage("simple").createExpression(trueValue);
+ expTrue.init(context);
+ expFalse = context.resolveLanguage("simple").createExpression(falseValue);
+ expFalse.init(context);
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ if (pred.matches(exchange)) {
+ return expTrue.evaluate(exchange, Object.class);
+ } else {
+ return expFalse.evaluate(exchange, Object.class);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "iif(" + predicate + "," + trueValue + "," + falseValue + ")";
+ }
+ };
+ }
+
+ /**
+ * Hashes the value using the given algorithm
+ */
+ public static Expression hashExpression(final String expression, final String algorithm) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+
+ @Override
+ public void init(CamelContext context) {
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ InputStream is = exp.evaluate(exchange, InputStream.class);
+ if (is != null) {
+ try {
+ // calculate the hash in chunks in case the payload is big
+ MessageDigest digest = MessageDigest.getInstance(algorithm);
+ DigestInputStream dis = new DigestInputStream(is, digest);
+ IOHelper.copy(dis, new OutputStream() {
+ @Override
+ public void write(int b) throws IOException {
+ // ignore
+ }
+ });
+ return StringHelper.bytesToHex(digest.digest());
+ } catch (Exception e) {
+ throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
+ } finally {
+ // reset cached streams so they can be read again
+ MessageHelper.resetStreamCache(exchange.getMessage());
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return "hash(" + expression + "," + algorithm + ")";
+ }
+ };
+ }
+
+ /**
+ * Returns a random number between min and max (exclusive)
+ */
+ public static Expression randomExpression(final String min, final String max) {
+ return new ExpressionAdapter() {
+ private Expression exp1;
+ private Expression exp2;
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ int num1 = exp1.evaluate(exchange, Integer.class);
+ int num2 = exp2.evaluate(exchange, Integer.class);
+ Random random = new Random(); // NOSONAR
+ return random.nextInt(num2 - num1) + num1;
+ }
+
+ @Override
+ public void init(CamelContext context) {
+ exp1 = ExpressionBuilder.simpleExpression(min);
+ exp1.init(context);
+ exp2 = ExpressionBuilder.simpleExpression(max);
+ exp2.init(context);
+ }
+
+ @Override
+ public String toString() {
+ return "random(" + min + "," + max + ")";
+ }
+ };
+ }
+
+ /**
+ * Returns a random number between 0 and max (exclusive)
+ */
+ public static Expression randomExpression(final int max) {
+ return randomExpression(0, max);
+ }
+
+ /**
+ * Returns a random number between min and max (exclusive)
+ */
+ public static Expression randomExpression(final int min, final int max) {
+ return new ExpressionAdapter() {
+ @Override
+ public Object evaluate(Exchange exchange) {
+ Random random = new Random(); // NOSONAR
+ return random.nextInt(max - min) + min;
+ }
+
+ @Override
+ public String toString() {
+ return "random(" + min + "," + max + ")";
+ }
+ };
+ }
+
+ /**
+ * Returns a new empty object of the given type
+ */
+ public static Expression newEmptyExpression(final String type) {
+
+ return new ExpressionAdapter() {
+ @Override
+ public Object evaluate(Exchange exchange) {
+ if ("map".equalsIgnoreCase(type)) {
+ return new LinkedHashMap<>();
+ } else if ("string".equalsIgnoreCase(type)) {
+ return "";
+ } else if ("list".equalsIgnoreCase(type)) {
+ return new ArrayList<>();
+ } else if ("set".equalsIgnoreCase(type)) {
+ return new LinkedHashSet<>();
+ }
+ throw new IllegalArgumentException("function empty(%s) has unknown type".formatted(type));
+ }
+
+ @Override
+ public String toString() {
+ return "empty(%s)".formatted(type);
+ }
+ };
+ }
+
+ /**
+ * Returns an uuid string based on the given generator (default, classic, short, simple)
+ */
+ public static Expression uuidExpression(final String generator) {
+ return new ExpressionAdapter() {
+
+ UuidGenerator uuid;
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ return uuid.generateUuid();
+ }
+
+ @Override
+ public void init(CamelContext context) {
+ if ("classic".equalsIgnoreCase(generator)) {
+ uuid = new ClassicUuidGenerator();
+ } else if ("short".equals(generator)) {
+ uuid = new ShortUuidGenerator();
+ } else if ("simple".equals(generator)) {
+ uuid = new SimpleUuidGenerator();
+ } else if ("random".equals(generator)) {
+ uuid = new RandomUuidGenerator();
+ } else if (generator == null || "default".equals(generator)) {
+ uuid = new DefaultUuidGenerator();
+ } else {
+ // lookup custom generator
+ uuid = CamelContextHelper.mandatoryLookup(context, generator, UuidGenerator.class);
+ }
+ }
+
+ @Override
+ public String toString() {
+ if (generator != null) {
+ return "uuid(" + generator + ")";
+ } else {
+ return "uuid";
+ }
+ }
+ };
+ }
+
+ public static Expression skipIteratorExpression(final Expression expression, final int skip) {
+ return new ExpressionAdapter() {
+ @Override
+ public Object evaluate(Exchange exchange) {
+ // evaluate expression as iterator
+ Iterator<?> it = expression.evaluate(exchange, Iterator.class);
+ ObjectHelper.notNull(it,
+ "expression: " + expression + " evaluated on " + exchange + " must return an java.util.Iterator");
+ return new SkipIterator(it, skip);
+ }
+
+ @Override
+ public String toString() {
+ return "skip " + expression + " " + skip + " times";
+ }
+ };
+ }
+
+ /**
+ * Returns the expression for the {@code null} value
+ */
+ public static Expression nullExpression() {
+ return new ExpressionAdapter() {
+ @Override
+ public Object evaluate(Exchange exchange) {
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return "null";
+ }
+ };
+ }
+
+ /**
+ * Returns an expression that caches the evaluation of another expression and returns the cached value, to avoid
+ * re-evaluating the expression.
+ *
+ * @param expression the target expression to cache
+ * @return the cached value
+ */
+ public static Expression cacheExpression(final Expression expression) {
+ return new ExpressionAdapter() {
+ private final AtomicReference<Object> cache = new AtomicReference<>();
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ Object answer = cache.get();
+ if (answer == null) {
+ answer = expression.evaluate(exchange, Object.class);
+ cache.set(answer);
+ }
+ return answer;
+ }
+
+ @Override
+ public String toString() {
+ return expression.toString();
+ }
+ };
+ }
+
+ /**
+ * Loads the given resource from classpath
+ */
+ public static Expression loadExpression(final String expression) {
+ return new ExpressionAdapter() {
+
+ private Expression exp;
+
+ @Override
+ public void init(CamelContext context) {
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ String name = exp.evaluate(exchange, String.class);
+ if (name != null) {
+ name = name.trim();
+ String part = StringHelper.after(name, ":", name);
+ boolean optional = part.endsWith("?optional=true");
+ if (optional) {
+ part = part.substring(0, part.length() - 14);
+ }
+ if (part.endsWith("?optional=false")) {
+ part = part.substring(0, part.length() - 15);
+ }
+ try {
+ InputStream is;
+ if (!optional) {
+ is = ResourceHelper.resolveMandatoryResourceAsInputStream(exchange.getContext(), part);
+ } else {
+ is = ResourceHelper.resolveResourceAsInputStream(exchange.getContext(), part);
+ }
+ if (is == null) {
+ return null;
+ }
+ return IOHelper.loadText(is, false);
+ } catch (Exception e) {
+ throw RuntimeCamelException.wrapRuntimeException(e);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return "load(" + expression + ")";
+ }
+ };
+ }
+
+ /**
+ * Returns an expression for a type value
+ *
+ * @param name the type name
+ * @return an expression object which will return the type value
+ */
+ public static Expression typeExpression(final String name) {
+ return new ExpressionAdapter() {
+ private ClassResolver classResolver;
+ private Expression exp;
+
+ @Override
+ public void init(CamelContext context) {
+ classResolver = context.getClassResolver();
+ exp = ExpressionBuilder.simpleExpression(name);
+ exp.init(context);
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ // it may refer to a class type
+ String text = exp.evaluate(exchange, String.class);
+ Class<?> type = classResolver.resolveClass(text);
+ if (type != null) {
+ return type;
+ }
+
+ int pos = text.lastIndexOf('.');
+ if (pos > 0) {
+ String before = text.substring(0, pos);
+ String after = text.substring(pos + 1);
+ type = classResolver.resolveClass(before);
+ if (type != null) {
+ // special for enum constants
+ if (type.isEnum()) {
+ Class<Enum<?>> enumClass = (Class<Enum<?>>) type;
+ for (Enum<?> enumValue : enumClass.getEnumConstants()) {
+ if (enumValue.name().equalsIgnoreCase(after)) {
+ return type.cast(enumValue);
+ }
+ }
+ throw CamelExecutionException.wrapCamelExecutionException(exchange,
+ new ClassNotFoundException("Cannot find enum: " + after + " on type: " + type));
+ } else {
+ // we assume it is a field constant
+ Object answer = ObjectHelper.lookupConstantFieldValue(type, after);
+ if (answer != null) {
+ return answer;
+ }
+ }
+ }
+ }
+
+ throw CamelExecutionException.wrapCamelExecutionException(exchange,
+ new ClassNotFoundException("Cannot find type: " + text));
+ }
+
+ @Override
+ public String toString() {
+ return "type:" + name;
+ }
+ };
+ }
+
+ /**
+ * Executes a custom simple function
+ */
+ public static Expression customFunction(final String name, final String parameter) {
+ return new ExpressionAdapter() {
+ private Expression func;
+ private Expression exp;
+
+ @Override
+ public void init(CamelContext context) {
+ super.init(context);
+ SimpleFunctionRegistry registry
+ = context.getCamelContextExtension().getContextPlugin(SimpleFunctionRegistry.class);
+ func = registry.getFunction(name);
+ if (func == null) {
+ throw new IllegalArgumentException("No custom simple function with name: " + name);
+ }
+ exp = ExpressionBuilder.simpleExpression(parameter);
+ exp.init(context);
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ final Object originalBody = exchange.getMessage().getBody();
+ try {
+ Object input = exp.evaluate(exchange, Object.class);
+ exchange.getMessage().setBody(input);
+ return func.evaluate(exchange, Object.class);
+ } finally {
+ exchange.getMessage().setBody(originalBody);
+ }
+ }
+
+ public String toString() {
+ return "function(" + name + ")";
+ }
+ };
+ }
+
+ /**
+ * Evaluates the simple jsonpath with the source input
+ */
+ public static Expression simpleJsonPathExpression(String source, String path) {
+ return new ExpressionAdapter() {
+ private Expression input;
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ Jsonable j = input.evaluate(exchange, Jsonable.class);
+ if (j instanceof JsonObject jo) {
+ return jo.path(path);
+ } else if (j instanceof JsonArray ja) {
+ // wrap array in pseudo root to leverage json-path here
+ JsonObject jo = new JsonObject();
+ jo.put("_root_", ja);
+ return jo.path("_root_." + path);
+ }
+ return null;
+ }
+
+ @Override
+ public void init(CamelContext context) {
+ super.init(context);
+ input = ExpressionBuilder.singleInputExpression(source);
+ input.init(context);
+ }
+
+ @Override
+ public String toString() {
+ return "simpleJsonpath[" + path + "]";
+ }
+ };
+ }
+}
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/OgnlExpressionBuilder.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/OgnlExpressionBuilder.java
new file mode 100644
index 000000000000..de266e2bd43c
--- /dev/null
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/OgnlExpressionBuilder.java
@@ -0,0 +1,476 @@
+/*
+ * 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;
+
+import java.util.List;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.Exchange;
+import org.apache.camel.ExchangePropertyKey;
+import org.apache.camel.Expression;
+import org.apache.camel.InvalidPayloadException;
+import org.apache.camel.spi.ClassResolver;
+import org.apache.camel.spi.Language;
+import org.apache.camel.support.ExchangeHelper;
+import org.apache.camel.support.ExpressionAdapter;
+import org.apache.camel.support.builder.ExpressionBuilder;
+import org.apache.camel.util.OgnlHelper;
+import org.apache.camel.util.StringHelper;
+
+/**
+ * Expression builder for OGNL-based navigation functions used by the simple language.
+ */
+public final class OgnlExpressionBuilder {
+
+ private OgnlExpressionBuilder() {
+ }
+
+ /**
+ * Returns the expression for the exchanges inbound message header invoking methods defined in a simple OGNL
+ * notation
+ *
+ * @param ognl methods to invoke on the header in a simple OGNL syntax
+ */
+ public static Expression headersOgnlExpression(final String ognl) {
+ return new KeyedOgnlExpressionAdapter(
+ ognl, "headerOgnl(" + ognl + ")",
+ (exchange, exp) -> {
+ String text = exp.evaluate(exchange, String.class);
+ return exchange.getIn().getHeader(text);
+ });
+ }
+
+ /**
+ * Returns the expression for the variable invoking methods defined in a simple OGNL notation
+ *
+ * @param ognl methods to invoke on the variable in a simple OGNL syntax
+ */
+ public static Expression variablesOgnlExpression(final String ognl) {
+ return new KeyedOgnlExpressionAdapter(
+ ognl, "variableOgnl(" + ognl + ")",
+ (exchange, exp) -> {
+ String text = exp.evaluate(exchange, String.class);
+ return ExchangeHelper.getVariable(exchange, text);
+ });
+ }
+
+ /**
+ * Returns the expression for the exchanges inbound message body converted to the given type and invoking methods on
+ * the converted body defined in a simple OGNL notation
+ */
+ public static Expression mandatoryBodyOgnlExpression(final String name, final String ognl) {
+ return new ExpressionAdapter() {
+ private ClassResolver classResolver;
+ private Expression exp;
+ private Language bean;
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ String text = exp.evaluate(exchange, String.class);
+ Class<?> type;
+ try {
+ type = classResolver.resolveMandatoryClass(text);
+ } catch (ClassNotFoundException e) {
+ throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
+ }
+ Object body;
+ try {
+ body = exchange.getIn().getMandatoryBody(type);
+ } catch (InvalidPayloadException e) {
+ throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
+ }
+ Expression ognlExp = bean.createExpression(null, new Object[] { null, body, ognl });
+ ognlExp.init(exchange.getContext());
+ return ognlExp.evaluate(exchange, Object.class);
+ }
+
+ @Override
+ public void init(CamelContext context) {
+ classResolver = context.getClassResolver();
+ exp = ExpressionBuilder.simpleExpression(name);
+ exp.init(context);
+ bean = context.resolveLanguage("bean");
+ }
+
+ @Override
+ public String toString() {
+ return "mandatoryBodyAs[" + name + "](" + ognl + ")";
+ }
+ };
+ }
+
+ /**
+ * Returns the expression for the exchanges inbound message body converted to the given type
+ */
+ public static Expression mandatoryBodyExpression(final String name) {
+ return new ExpressionAdapter() {
+ private ClassResolver classResolver;
+ private Expression exp;
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ String text = exp.evaluate(exchange, String.class);
+ Class<?> type;
+ try {
+ type = classResolver.resolveMandatoryClass(text);
+ } catch (ClassNotFoundException e) {
+ throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
+ }
+ try {
+ return exchange.getIn().getMandatoryBody(type);
+ } catch (InvalidPayloadException e) {
+ throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
+ }
+ }
+
+ @Override
+ public void init(CamelContext context) {
+ classResolver = context.getClassResolver();
+ exp = ExpressionBuilder.simpleExpression(name);
+ exp.init(context);
+ }
+
+ @Override
+ public String toString() {
+ return "mandatoryBodyAs[" + name + "]";
+ }
+ };
+ }
+
+ /**
+ * Returns the expression for the message converted to the given type and invoking methods on the converted message
+ * defined in a simple OGNL notation
+ */
+ public static Expression messageOgnlExpression(final String name, final String ognl) {
+ return new ExpressionAdapter() {
+ private ClassResolver classResolver;
+ private Expression exp;
+ private Language bean;
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ String text = exp.evaluate(exchange, String.class);
+ Class<?> type;
+ try {
+ type = classResolver.resolveMandatoryClass(text);
+ } catch (ClassNotFoundException e) {
+ throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
+ }
+ Object msg = exchange.getMessage(type);
+ if (msg != null) {
+ // ognl is able to evaluate method name if it contains nested functions
+ // so we should not eager evaluate ognl as a string
+ Expression ognlExp = bean.createExpression(null, new Object[] { null, msg, ognl });
+ ognlExp.init(exchange.getContext());
+ return ognlExp.evaluate(exchange, Object.class);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void init(CamelContext context) {
+ classResolver = context.getClassResolver();
+ exp = ExpressionBuilder.simpleExpression(name);
+ exp.init(context);
+ bean = context.resolveLanguage("bean");
+ }
+
+ @Override
+ public String toString() {
+ return "messageOgnlAs[" + name + "](" + ognl + ")";
+ }
+ };
+ }
+
+ /**
+ * Returns the expression for the exchanges inbound message body converted to the given type and invoking methods on
+ * the converted body defined in a simple OGNL notation
+ */
+ public static Expression bodyOgnlExpression(final String name, final String ognl) {
+ return new ExpressionAdapter() {
+ private ClassResolver classResolver;
+ private Expression exp;
+ private Language bean;
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ String text = exp.evaluate(exchange, String.class);
+ Class<?> type;
+ try {
+ type = classResolver.resolveMandatoryClass(text);
+ } catch (ClassNotFoundException e) {
+ throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
+ }
+ Object body = exchange.getIn().getBody(type);
+ if (body != null) {
+ // ognl is able to evaluate method name if it contains nested functions
+ // so we should not eager evaluate ognl as a string
+ Expression ognlExp = bean.createExpression(null, new Object[] { null, body, ognl });
+ ognlExp.init(exchange.getContext());
+ return ognlExp.evaluate(exchange, Object.class);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void init(CamelContext context) {
+ classResolver = context.getClassResolver();
+ exp = ExpressionBuilder.simpleExpression(name);
+ exp.init(context);
+ bean = context.resolveLanguage("bean");
+ }
+
+ @Override
+ public String toString() {
+ return "bodyOgnlAs[" + name + "](" + ognl + ")";
+ }
+ };
+ }
+
+ /**
+ * Returns the expression for the exchange invoking methods defined in a simple OGNL notation
+ *
+ * @param ognl methods to invoke on the exchange in a simple OGNL syntax
+ */
+ public static Expression exchangeOgnlExpression(final String ognl) {
+ return new ExpressionAdapter() {
+ private Language bean;
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ // ognl is able to evaluate method name if it contains nested functions
+ // so we should not eager evaluate ognl as a string
+ Expression ognlExp = bean.createExpression(null, new Object[] { null, exchange, ognl });
+ ognlExp.init(exchange.getContext());
+ return ognlExp.evaluate(exchange, Object.class);
+ }
+
+ @Override
+ public void init(CamelContext context) {
+ bean = context.resolveLanguage("bean");
+ }
+
+ @Override
+ public String toString() {
+ return "exchangeOgnl(" + ognl + ")";
+ }
+ };
+ }
+
+ /**
+ * Returns the expression for the exchanges camelContext invoking methods defined in a simple OGNL notation
+ *
+ * @param ognl methods to invoke on the context in a simple OGNL syntax
+ */
+ public static Expression camelContextOgnlExpression(final String ognl) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+
+ @Override
+ public void init(CamelContext context) {
+ exp = ExpressionBuilder.beanExpression(context, ognl);
+ exp.init(context);
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ // ognl is able to evaluate method name if it contains nested functions
+ // so we should not eager evaluate ognl as a string
+ return exp.evaluate(exchange, Object.class);
+ }
+
+ @Override
+ public String toString() {
+ return "camelContextOgnl(" + ognl + ")";
+ }
+ };
+ }
+
+ /**
+ * Returns the expression for the exchanges inbound message body invoking methods defined in a simple OGNL notation
+ *
+ * @param ognl methods to invoke on the body in a simple OGNL syntax
+ */
+ public static Expression bodyOgnlExpression(final String ognl) {
+ return new ExpressionAdapter() {
+ private Language bean;
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ Object body = exchange.getIn().getBody();
+ if (body == null) {
+ return null;
+ }
+ Expression ognlExp = bean.createExpression(null, new Object[] { null, body, ognl });
+ ognlExp.init(exchange.getContext());
+ return ognlExp.evaluate(exchange, Object.class);
+ }
+
+ @Override
+ public void init(CamelContext context) {
+ bean = context.resolveLanguage("bean");
+ }
+
+ @Override
+ public String toString() {
+ return "bodyOgnl(" + ognl + ")";
+ }
+ };
+ }
+
+ /**
+ * Returns an expression for the property value of exchange with the given name invoking methods defined in a simple
+ * OGNL notation
+ *
+ * @param ognl methods to invoke on the property in a simple OGNL syntax
+ */
+ public static Expression propertyOgnlExpression(final String ognl) {
+ return new KeyedOgnlExpressionAdapter(
+ ognl, "propertyOgnl(" + ognl + ")",
+ (exchange, exp) -> {
+ String text = exp.evaluate(exchange, String.class);
+ return exchange.getProperty(text);
+ });
+ }
+
+ /**
+ * Returns the expression for the exchange's exception invoking methods defined in a simple OGNL notation
+ *
+ * @param ognl methods to invoke on the body in a simple OGNL syntax
+ */
+ public static Expression exchangeExceptionOgnlExpression(final String ognl) {
+ return new ExpressionAdapter() {
+ private Language bean;
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ Object exception = exchange.getException();
+ if (exception == null) {
+ exception = exchange.getProperty(ExchangePropertyKey.EXCEPTION_CAUGHT, Exception.class);
+ }
+
+ if (exception == null) {
+ return null;
+ }
+
+ // ognl is able to evaluate method name if it contains nested functions
+ // so we should not eager evaluate ognl as a string
+ Expression ognlExp = bean.createExpression(null, new Object[] { null, exception, ognl });
+ ognlExp.init(exchange.getContext());
+ return ognlExp.evaluate(exchange, Object.class);
+ }
+
+ @Override
+ public void init(CamelContext context) {
+ bean = context.resolveLanguage("bean");
+ }
+
+ @Override
+ public String toString() {
+ return "exchangeExceptionOgnl(" + ognl + ")";
+ }
+ };
+ }
+
+ /**
+ * Expression adapter for OGNL expression from Message Header or Exchange property
+ */
+ public static class KeyedOgnlExpressionAdapter extends ExpressionAdapter {
+ private final String ognl;
+ private final String toStringValue;
+ private final KeyedEntityRetrievalStrategy keyedEntityRetrievalStrategy;
+ private String key;
+ private final String method;
+ private Expression keyExpression;
+ private Expression ognlExpression;
+ private Language beanLanguage;
+
+ public KeyedOgnlExpressionAdapter(String ognl, String toStringValue,
+ KeyedEntityRetrievalStrategy keyedEntityRetrievalStrategy) {
+ this.ognl = ognl;
+ this.toStringValue = toStringValue;
+ this.keyedEntityRetrievalStrategy = keyedEntityRetrievalStrategy;
+
+ // Split ognl except when this is not a Map, Array
+ // and we would like to keep the dots within the key name
+ List<String> methods = OgnlHelper.splitOgnl(ognl);
+
+ key = methods.get(0);
+ String keySuffix = "";
+ // if ognl starts with a key inside brackets (eg: [foo.bar])
+ // remove starting and ending brackets from key
+ if (key.startsWith("[") && key.endsWith("]")) {
+ key = StringHelper.removeLeadingAndEndingQuotes(key.substring(1, key.length() - 1));
+ keySuffix = StringHelper.after(methods.get(0), key);
+ }
+ // remove any OGNL operators so we got the pure key name
+ key = OgnlHelper.removeOperators(key);
+ // and this may be the last remainder method to try as OGNL if there are no exchange properties with those key names
+ method = StringHelper.after(ognl, key + keySuffix);
+ }
+
+ @Override
+ public void init(CamelContext context) {
+ beanLanguage = context.resolveLanguage("bean");
+ ognlExpression = ExpressionBuilder.simpleExpression(ognl);
+ ognlExpression.init(context);
+ // key must be lazy eval as it only used in special situations
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ // try with full name first
+ Object property = keyedEntityRetrievalStrategy.getKeyedEntity(exchange, ognlExpression);
+ if (property != null) {
+ return property;
+ }
+
+ // key must be lazy eval as it only used in special situations
+ if (keyExpression == null) {
+ keyExpression = ExpressionBuilder.simpleExpression(key);
+ keyExpression.init(exchange.getContext());
+ }
+
+ property = keyedEntityRetrievalStrategy.getKeyedEntity(exchange, keyExpression);
+ if (property == null) {
+ return null;
+ }
+ if (method != null) {
+ Expression exp = beanLanguage.createExpression(null, new Object[] { null, property, method });
+ exp.init(exchange.getContext());
+ return exp.evaluate(exchange, Object.class);
+ } else {
+ return property;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return toStringValue;
+ }
+
+ /**
+ * Strategy to retrieve the value based on the key
+ */
+ public interface KeyedEntityRetrievalStrategy {
+ Object getKeyedEntity(Exchange exchange, Expression key);
+ }
+ }
+}
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionBuilder.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionBuilder.java
deleted file mode 100644
index 81ff6c0225a7..000000000000
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionBuilder.java
+++ /dev/null
@@ -1,3523 +0,0 @@
-/*
- * 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;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.lang.reflect.Constructor;
-import java.security.DigestInputStream;
-import java.security.MessageDigest;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Random;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.regex.Pattern;
-
-import org.apache.camel.CamelContext;
-import org.apache.camel.CamelExecutionException;
-import org.apache.camel.Exchange;
-import org.apache.camel.ExchangePropertyKey;
-import org.apache.camel.Expression;
-import org.apache.camel.InvalidPayloadException;
-import org.apache.camel.Predicate;
-import org.apache.camel.RuntimeCamelException;
-import org.apache.camel.StreamCache;
-import org.apache.camel.spi.ClassResolver;
-import org.apache.camel.spi.ExchangeFormatter;
-import org.apache.camel.spi.Language;
-import org.apache.camel.spi.SimpleFunctionRegistry;
-import org.apache.camel.spi.UuidGenerator;
-import org.apache.camel.support.CamelContextHelper;
-import org.apache.camel.support.ClassicUuidGenerator;
-import org.apache.camel.support.DefaultUuidGenerator;
-import org.apache.camel.support.ExchangeHelper;
-import org.apache.camel.support.ExpressionAdapter;
-import org.apache.camel.support.LanguageHelper;
-import org.apache.camel.support.MessageHelper;
-import org.apache.camel.support.RandomUuidGenerator;
-import org.apache.camel.support.ResourceHelper;
-import org.apache.camel.support.ShortUuidGenerator;
-import org.apache.camel.support.SimpleUuidGenerator;
-import org.apache.camel.support.builder.ExpressionBuilder;
-import org.apache.camel.support.builder.PredicateBuilder;
-import org.apache.camel.util.FileUtil;
-import org.apache.camel.util.IOHelper;
-import org.apache.camel.util.ObjectHelper;
-import org.apache.camel.util.OgnlHelper;
-import org.apache.camel.util.SkipIterator;
-import org.apache.camel.util.StringHelper;
-import org.apache.camel.util.StringQuoteHelper;
-import org.apache.camel.util.json.JsonArray;
-import org.apache.camel.util.json.JsonObject;
-import org.apache.camel.util.json.Jsonable;
-
-/**
- * Expression builder used by the simple language.
- */
-public final class SimpleExpressionBuilder {
-
- private static final Pattern OFFSET_PATTERN = Pattern.compile("([+-])([^+-]+)");
-
- private SimpleExpressionBuilder() {
- }
-
- /**
- * Returns the expression for the exchanges inbound message header invoking methods defined in a simple OGNL
- * notation
- *
- * @param ognl methods to invoke on the header in a simple OGNL syntax
- */
- public static Expression headersOgnlExpression(final String ognl) {
- return new KeyedOgnlExpressionAdapter(
- ognl, "headerOgnl(" + ognl + ")",
- (exchange, exp) -> {
- String text = exp.evaluate(exchange, String.class);
- return exchange.getIn().getHeader(text);
- });
- }
-
- /**
- * Returns the expression for the variable invoking methods defined in a simple OGNL notation
- *
- * @param ognl methods to invoke on the variable in a simple OGNL syntax
- */
- public static Expression variablesOgnlExpression(final String ognl) {
- return new KeyedOgnlExpressionAdapter(
- ognl, "variableOgnl(" + ognl + ")",
- (exchange, exp) -> {
- String text = exp.evaluate(exchange, String.class);
- return ExchangeHelper.getVariable(exchange, text);
- });
- }
-
- /**
- * Returns the message history (including exchange details or not)
- */
- public static Expression messageHistoryExpression(final boolean detailed) {
- return new ExpressionAdapter() {
-
- private ExchangeFormatter formatter;
-
- @Override
- public void init(CamelContext context) {
- if (detailed) {
- // use the exchange formatter to log exchange details
- formatter = getOrCreateExchangeFormatter(context);
- }
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- return MessageHelper.dumpMessageHistoryStacktrace(exchange, formatter, false);
- }
-
- private ExchangeFormatter getOrCreateExchangeFormatter(CamelContext camelContext) {
- return LanguageHelper.getOrCreateExchangeFormatter(camelContext, formatter);
- }
-
- @Override
- public String toString() {
- return "messageHistory(" + detailed + ")";
- }
- };
- }
-
- /**
- * Returns an iterator to collate (iterate) the given expression
- */
- public static Expression collateExpression(final String expression, final int group) {
- return new ExpressionAdapter() {
- private Expression exp;
-
- @Override
- public void init(CamelContext context) {
- // first use simple then create the group expression
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- exp = ExpressionBuilder.groupIteratorExpression(exp, null, Integer.toString(group), false);
- exp.init(context);
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- return exp.evaluate(exchange, Object.class);
- }
-
- @Override
- public String toString() {
- return "collate(" + expression + "," + group + ")";
- }
- };
- }
-
- /**
- * Returns an iterator to collate (iterate) the given expression
- */
- public static Expression collateExpression(final String expression, final String group) {
- return new ExpressionAdapter() {
- private Expression exp;
- private Expression num;
-
- @Override
- public void init(CamelContext context) {
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- num = context.resolveLanguage("simple").createExpression(group);
- num.init(context);
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- Integer n = num.evaluate(exchange, Integer.class);
- exp = ExpressionBuilder.groupIteratorExpression(exp, null, Integer.toString(n), false);
- exp.init(exchange.getContext());
- return exp.evaluate(exchange, Object.class);
- }
-
- @Override
- public String toString() {
- return "collate(" + expression + "," + group + ")";
- }
- };
- }
-
- /**
- * Returns an iterator to skip (iterate) the given expression
- */
- public static Expression skipExpression(final String expression, final int number) {
- return new ExpressionAdapter() {
- private Expression exp;
-
- @Override
- public void init(CamelContext context) {
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- return skipIteratorExpression(exp, number).evaluate(exchange, Object.class);
- }
-
- @Override
- public String toString() {
- return "skip(" + expression + "," + number + ")";
- }
- };
- }
-
- /**
- * Returns an iterator to skip (iterate) the given expression
- */
- public static Expression skipExpression(final String expression, final String number) {
- return new ExpressionAdapter() {
- private Expression exp;
- private Expression num;
-
- @Override
- public void init(CamelContext context) {
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- num = context.resolveLanguage("simple").createExpression(number);
- num.init(context);
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- int n = num.evaluate(exchange, Integer.class);
- return skipIteratorExpression(exp, n).evaluate(exchange, Object.class);
- }
-
- @Override
- public String toString() {
- return "skip(" + expression + "," + number + ")";
- }
- };
- }
-
- /**
- * Converts the given expressions to a number and return the absolute value (uses message body if expression is
- * null)
- */
- public static Expression absExpression(final String expression) {
- return new ExpressionAdapter() {
- private Expression exp;
-
- @Override
- public void init(CamelContext context) {
- if (expression != null) {
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- }
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- Long value;
- if (exp != null) {
- value = exp.evaluate(exchange, Long.class);
- } else {
- value = exchange.getMessage().getBody(Long.class);
- }
- if (value != null) {
- value = Math.abs(value);
- }
- return value;
- }
-
- @Override
- public String toString() {
- if (expression != null) {
- return "abs(" + expression + ")";
- } else {
- return "abs()";
- }
- }
- };
- }
-
- /**
- * Converts the given expressions to a floating number and return the floor value (uses message body if expression
- * is null)
- */
- public static Expression floorExpression(final String expression) {
- return new ExpressionAdapter() {
- private Expression exp;
-
- @Override
- public void init(CamelContext context) {
- if (expression != null) {
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- }
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- Double value;
- if (exp != null) {
- value = exp.evaluate(exchange, Double.class);
- } else {
- value = exchange.getMessage().getBody(Double.class);
- }
- if (value != null) {
- double d = Math.floor(value);
- return (int) d;
- }
- return value;
- }
-
- @Override
- public String toString() {
- if (expression != null) {
- return "floor(" + expression + ")";
- } else {
- return "floor()";
- }
- }
- };
- }
-
- /**
- * Converts the given expressions to a floating number and return the ceil value (uses message body if expression is
- * null)
- */
- public static Expression ceilExpression(final String expression) {
- return new ExpressionAdapter() {
- private Expression exp;
-
- @Override
- public void init(CamelContext context) {
- if (expression != null) {
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- }
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- Double value;
- if (exp != null) {
- value = exp.evaluate(exchange, Double.class);
- } else {
- value = exchange.getMessage().getBody(Double.class);
- }
- if (value != null) {
- double d = Math.ceil(value);
- return (int) d;
- }
- return value;
- }
-
- @Override
- public String toString() {
- if (expression != null) {
- return "ceil(" + expression + ")";
- } else {
- return "ceil()";
- }
- }
- };
- }
-
- /**
- * Whether the expression is empty or having a list/map which has no elements.
- */
- public static Expression isEmptyExpression(final String expression) {
- return new ExpressionAdapter() {
- private Expression exp;
-
- @Override
- public void init(CamelContext context) {
- if (expression != null) {
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- }
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- Object body;
- if (exp != null) {
- body = exp.evaluate(exchange, Object.class);
- } else {
- body = exchange.getMessage().getBody();
- }
- // this may be an object that we can iterate
- Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(body);
- for (Object o : it) {
- if (o != null) {
- return false;
- }
- }
- return true;
- }
-
- @Override
- public String toString() {
- if (expression != null) {
- return "isEmpty(" + expression + ")";
- } else {
- return "isEmpty()";
- }
- }
- };
- }
-
- /**
- * Whether the expression is an alphabetic String
- */
- public static Expression isAlphaExpression(final String expression) {
- return new ExpressionAdapter() {
- private Expression exp;
-
- @Override
- public void init(CamelContext context) {
- if (expression != null) {
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- }
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- String body;
- if (exp != null) {
- body = exp.evaluate(exchange, String.class);
- } else {
- body = exchange.getMessage().getBody(String.class);
- }
- if (body == null || body.isBlank()) {
- return false;
- }
- for (int i = 0; i < body.length(); i++) {
- char ch = body.charAt(i);
- if (!Character.isLetter(ch)) {
- return false;
- }
- }
- return true;
- }
-
- @Override
- public String toString() {
- if (expression != null) {
- return "isAlpha(" + expression + ")";
- } else {
- return "isAlpha()";
- }
- }
- };
- }
-
- /**
- * Whether the expression is a number (integral or floating)
- */
- public static Expression isNumericExpression(final String expression) {
- return new ExpressionAdapter() {
- private Expression exp;
-
- @Override
- public void init(CamelContext context) {
- if (expression != null) {
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- }
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- String body;
- if (exp != null) {
- body = exp.evaluate(exchange, String.class);
- } else {
- body = exchange.getMessage().getBody(String.class);
- }
- if (body == null || body.isBlank()) {
- return false;
- }
- for (int i = 0; i < body.length(); i++) {
- char ch = body.charAt(i);
- if (!Character.isDigit(ch)) {
- return false;
- }
- }
- return true;
- }
-
- @Override
- public String toString() {
- if (expression != null) {
- return "isNumeric(" + expression + ")";
- } else {
- return "isNumeric()";
- }
- }
- };
- }
-
- /**
- * Whether the expression is an alphabetic or numeric String
- */
- public static Expression isAlphaNumericExpression(final String expression) {
- return new ExpressionAdapter() {
- private Expression exp;
-
- @Override
- public void init(CamelContext context) {
- if (expression != null) {
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- }
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- String body;
- if (exp != null) {
- body = exp.evaluate(exchange, String.class);
- } else {
- body = exchange.getMessage().getBody(String.class);
- }
- if (body == null || body.isBlank()) {
- return false;
- }
- for (int i = 0; i < body.length(); i++) {
- char ch = body.charAt(i);
- if (!Character.isLetterOrDigit(ch)) {
- return false;
- }
- }
- return true;
- }
-
- @Override
- public String toString() {
- if (expression != null) {
- return "isAlphaNumeric(" + expression + ")";
- } else {
- return "isAlphaNumeric()";
- }
- }
- };
- }
-
- /**
- * Returns the opposite result of the predicate
- */
- public static Expression isNotPredicate(final String predicate) {
- return new ExpressionAdapter() {
- private Predicate pred;
-
- @Override
- public void init(CamelContext context) {
- pred = PredicateBuilder.not(context.resolveLanguage("simple").createPredicate(predicate));
- pred.init(context);
- }
-
- public Object evaluate(Exchange exchange) {
- return pred.matches(exchange);
- }
-
- @Override
- public String toString() {
- if (predicate != null) {
- return "not(" + predicate + ")";
- } else {
- return "not()";
- }
- }
- };
- }
-
- /**
- * What kind of type is the expression
- */
- public static Expression kindOfTypeExpression(final String expression) {
- return new ExpressionAdapter() {
- private Expression exp;
-
- @Override
- public void init(CamelContext context) {
- if (expression != null) {
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- }
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- Object value;
- if (exp != null) {
- value = exp.evaluate(exchange, Object.class);
- } else {
- value = exchange.getMessage().getBody();
- }
- if (value != null) {
- Class<?> type = value.getClass();
- if (ObjectHelper.isNumericType(type)) {
- return "number";
- } else if (boolean.class == type || Boolean.class == type) {
- return "boolean";
- } else if (value instanceof CharSequence) {
- return "string";
- } else if (ObjectHelper.isPrimitiveArrayType(type) || value instanceof Collection
- || value instanceof Map<?, ?>) {
- return "array";
- } else {
- return "object";
- }
- }
- return "null";
- }
-
- @Override
- public String toString() {
- if (expression != null) {
- return "kindOfType(" + expression + ")";
- } else {
- return "kindOfType()";
- }
- }
- };
- }
-
- /**
- * Safe quotes the given expressions (uses message body if expression is null) if necessary.
- */
- public static Expression safeQuoteExpression(final String expression) {
- return new ExpressionAdapter() {
- private Expression exp;
-
- @Override
- public void init(CamelContext context) {
- if (expression != null) {
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- }
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- Object value;
- if (exp != null) {
- value = exp.evaluate(exchange, Object.class);
- } else {
- value = exchange.getMessage().getBody(Object.class);
- }
- if (value != null) {
- String type = kindOfType(value);
- if ("string".equals(type) || "array".equals(type) || "object".equals(type)) {
- String body = exchange.getContext().getTypeConverter().tryConvertTo(String.class, exchange, value);
- body = StringHelper.removeLeadingAndEndingQuotes(body);
- value = StringQuoteHelper.doubleQuote(body);
- }
- }
- return value;
- }
-
- private String kindOfType(Object value) {
- Class<?> type = value.getClass();
- if (ObjectHelper.isNumericType(type)) {
- return "number";
- } else if (boolean.class == type || Boolean.class == type) {
- return "boolean";
- } else if (value instanceof CharSequence) {
- return "string";
- } else if (ObjectHelper.isPrimitiveArrayType(type) || value instanceof Collection
- || value instanceof Map<?, ?>) {
- return "array";
- } else {
- return "object";
- }
- }
-
- @Override
- public String toString() {
- if (expression != null) {
- return "safeQuote(" + expression + ")";
- } else {
- return "safeQuote()";
- }
- }
- };
- }
-
- /**
- * Double quotes the given expressions (uses message body if expression is null)
- */
- public static Expression quoteExpression(final String expression) {
- return new ExpressionAdapter() {
- private Expression exp;
-
- @Override
- public void init(CamelContext context) {
- if (expression != null) {
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- }
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- String value;
- if (exp != null) {
- value = exp.evaluate(exchange, String.class);
- } else {
- value = exchange.getMessage().getBody(String.class);
- }
- if (value != null && !StringHelper.isDoubleQuoted(value)) {
- value = StringHelper.removeLeadingAndEndingQuotes(value);
- value = StringQuoteHelper.doubleQuote(value);
- }
- return value;
- }
-
- @Override
- public String toString() {
- if (expression != null) {
- return "quote(" + expression + ")";
- } else {
- return "quote()";
- }
- }
- };
- }
-
- /**
- * Un quotes the given expressions (uses message body if expression is null)
- */
- public static Expression unquoteExpression(final String expression) {
- return new ExpressionAdapter() {
- private Expression exp;
-
- @Override
- public void init(CamelContext context) {
- if (expression != null) {
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- }
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- String value;
- if (exp != null) {
- value = exp.evaluate(exchange, String.class);
- } else {
- value = exchange.getMessage().getBody(String.class);
- }
- if (value != null) {
- value = StringHelper.removeLeadingAndEndingQuotes(value);
- }
- return value;
- }
-
- @Override
- public String toString() {
- if (expression != null) {
- return "unquote(" + expression + ")";
- } else {
- return "unquote()";
- }
- }
- };
- }
-
- /**
- * Trims the given expressions (uses message body if expression is null)
- */
- public static Expression trimExpression(final String expression) {
- return new ExpressionAdapter() {
- private Expression exp;
-
- @Override
- public void init(CamelContext context) {
- if (expression != null) {
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- }
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- String value;
- if (exp != null) {
- value = exp.evaluate(exchange, String.class);
- } else {
- value = exchange.getMessage().getBody(String.class);
- }
- if (value != null) {
- value = value.trim();
- }
- return value;
- }
-
- @Override
- public String toString() {
- if (expression != null) {
- return "trim(" + expression + ")";
- } else {
- return "trim()";
- }
- }
- };
- }
-
- /**
- * Capitalizes the given expressions (uses message body if expression is null)
- */
- public static Expression capitalizeExpression(final String expression) {
- return new ExpressionAdapter() {
- private Expression exp;
-
- @Override
- public void init(CamelContext context) {
- if (expression != null) {
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- }
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- String value;
- if (exp != null) {
- value = exp.evaluate(exchange, String.class);
- } else {
- value = exchange.getMessage().getBody(String.class);
- }
- if (value != null) {
- value = StringHelper.capitalizeAll(value);
- }
- return value;
- }
-
- @Override
- public String toString() {
- if (expression != null) {
- return "capitalize(" + expression + ")";
- } else {
- return "capitalize()";
- }
- }
- };
- }
-
- /**
- * Pad the expression
- */
- public static Expression padExpression(final String expression, final String length, final String separator) {
- return new ExpressionAdapter() {
- private Expression exp;
- private Expression len;
-
- @Override
- public void init(CamelContext context) {
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- len = context.resolveLanguage("simple").createExpression(length);
- len.init(context);
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- String answer = exp.evaluate(exchange, String.class);
- Integer width = len.evaluate(exchange, Integer.class);
- String sep = separator;
- if (sep == null || sep.isEmpty()) {
- sep = " ";
- }
-
- int max = Math.abs(width);
- while (max > answer.length()) {
- if (width > 0) {
- answer = answer + sep;
- } else {
- answer = sep + answer;
- }
- }
- return answer;
- }
-
- @Override
- public String toString() {
- return "pad(" + exp + "," + length + ")";
- }
- };
- }
-
- /**
- * String concats the two expressions.
- */
- public static Expression concatExpression(final String right, final String left, String separator) {
- return new ExpressionAdapter() {
- private Expression exp1;
- private Expression exp2;
-
- @Override
- public void init(CamelContext context) {
- exp1 = context.resolveLanguage("simple").createExpression(right);
- exp1.init(context);
- exp2 = context.resolveLanguage("simple").createExpression(left);
- exp2.init(context);
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- String value1 = exp1.evaluate(exchange, String.class);
- String value2 = exp2.evaluate(exchange, String.class);
- if (value1 != null && value2 != null) {
- return value1 + (separator != null ? separator : "") + value2;
- } else {
- return value1 != null ? value1 : value2;
- }
- }
-
- @Override
- public String toString() {
- return "concat(" + right + "," + left + ")";
- }
- };
- }
-
- /**
- * Throws an exception with the given message.
- */
- public static Expression throwExceptionExpression(String msg, String type) {
- return new ExpressionAdapter() {
- private Class<?> clazz;
- private Expression exp;
-
- @Override
- public void init(CamelContext context) {
- if (type == null) {
- clazz = IllegalArgumentException.class;
- } else {
- try {
- clazz = context.getClassResolver().resolveMandatoryClass(type);
- } catch (ClassNotFoundException e) {
- throw CamelExecutionException.wrapRuntimeException(e);
- }
- }
- exp = context.resolveLanguage("simple").createExpression(msg);
- exp.init(context);
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- try {
- String text = exp.evaluate(exchange, String.class);
- // create a new exception to that type, and provide the message as
- Constructor<?> constructor = clazz.getConstructor(String.class);
- Exception cause = (Exception) constructor.newInstance(text);
- if (cause instanceof RuntimeException re) {
- throw re;
- } else {
- RuntimeException re = new RuntimeCamelException(cause);
- throw re;
- }
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public String toString() {
- return "throwException(" + msg + ")";
- }
- };
- }
-
- /**
- * Assert the expression
- */
- public static Expression assertExpression(String expression, String message) {
- return new ExpressionAdapter() {
- private Predicate pred;
-
- @Override
- public void init(CamelContext context) {
- pred = context.resolveLanguage("simple").createPredicate(expression);
- pred.init(context);
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- if (!pred.matches(exchange)) {
- throw new SimpleAssertionException(message);
- }
- return null;
- }
-
- @Override
- public String toString() {
- return "assert(" + pred + ", " + message + ")";
- }
- };
- }
-
- /**
- * Converts the result of the expression to the given type
- */
- public static Expression convertToExpression(final String expression, final String type) {
- return new ExpressionAdapter() {
- private Class<?> clazz;
- private Expression exp;
-
- @Override
- public void init(CamelContext context) {
- try {
- clazz = context.getClassResolver().resolveMandatoryClass(type);
- } catch (ClassNotFoundException e) {
- throw CamelExecutionException.wrapRuntimeException(e);
- }
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- return exp.evaluate(exchange, clazz);
- }
-
- @Override
- public String toString() {
- if (expression != null) {
- return "convertTo(" + expression + ", " + type + ")";
- } else {
- return "convertTo(" + type + ")";
- }
- }
- };
- }
-
- /**
- * Converts the result of the expression to the given type and invoking methods on the converted object defined in a
- * simple OGNL notation
- */
- public static Expression convertToOgnlExpression(final String expression, final String type, final String ognl) {
- return new ExpressionAdapter() {
- private Class<?> clazz;
- private Expression exp;
- private Language bean;
-
- @Override
- public void init(CamelContext context) {
- try {
- clazz = context.getClassResolver().resolveMandatoryClass(type);
- } catch (ClassNotFoundException e) {
- throw CamelExecutionException.wrapRuntimeException(e);
- }
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- bean = context.resolveLanguage("bean");
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- Object body = exp.evaluate(exchange, clazz);
- if (body == null) {
- return null;
- }
- Expression ognlExp = bean.createExpression(null, new Object[] { null, body, ognl });
- ognlExp.init(exchange.getContext());
- return ognlExp.evaluate(exchange, Object.class);
- }
-
- @Override
- public String toString() {
- if (expression != null) {
- return "convertToOgnl(" + expression + ", " + type + ")." + ognl;
- } else {
- return "convertToOgnl(" + type + ")." + ognl;
- }
- }
- };
- }
-
- /**
- * Uppercases the given expressions (uses message body if expression is null)
- */
- public static Expression uppercaseExpression(final String expression) {
- return new ExpressionAdapter() {
- private Expression exp;
-
- @Override
- public void init(CamelContext context) {
- if (expression != null) {
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- }
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- String value;
- if (exp != null) {
- value = exp.evaluate(exchange, String.class);
- } else {
- value = exchange.getMessage().getBody(String.class);
- }
- if (value != null) {
- value = value.toUpperCase(Locale.ENGLISH);
- }
- return value;
- }
-
- @Override
- public String toString() {
- if (expression != null) {
- return "uppercase(" + expression + ")";
- } else {
- return "uppercase()";
- }
- }
- };
- }
-
- /**
- * Lowercases the given expressions (uses message body if expression is null)
- */
- public static Expression lowercaseExpression(final String expression) {
- return new ExpressionAdapter() {
- private Expression exp;
-
- @Override
- public void init(CamelContext context) {
- if (expression != null) {
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- }
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- String value;
- if (exp != null) {
- value = exp.evaluate(exchange, String.class);
- } else {
- value = exchange.getMessage().getBody(String.class);
- }
- if (value != null) {
- value = value.toLowerCase(Locale.ENGLISH);
- }
- return value;
- }
-
- @Override
- public String toString() {
- if (expression != null) {
- return "lowercase(" + expression + ")";
- } else {
- return "lowercase()";
- }
- }
- };
- }
-
- /**
- * Returns the size of the expression (number of elements in collection/map; otherwise 1)
- */
- public static Expression sizeExpression(final String expression) {
- return new ExpressionAdapter() {
- private Expression exp;
-
- @Override
- public void init(CamelContext context) {
- if (expression != null) {
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- }
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- Object body;
- if (exp != null) {
- body = exp.evaluate(exchange, Object.class);
- } else {
- body = exchange.getMessage().getBody();
- }
- if (body != null) {
- if (body instanceof byte[] arr) {
- return arr.length;
- } else if (body instanceof char[] arr) {
- return arr.length;
- } else if (body instanceof int[] arr) {
- return arr.length;
- } else if (body instanceof long[] arr) {
- return arr.length;
- } else if (body instanceof double[] arr) {
- return arr.length;
- } else if (body instanceof String[] arr) {
- return arr.length;
- } else if (body instanceof Collection<?> c) {
- return c.size();
- } else if (body instanceof Map<?, ?> m) {
- return m.size();
- } else {
- return 1;
- }
- }
- return 0;
- }
-
- @Override
- public String toString() {
- if (expression != null) {
- return "size(" + expression + ")";
- } else {
- return "size()";
- }
- }
- };
- }
-
- /**
- * Returns the length of the expression (length of payload in bytes)
- */
- public static Expression lengthExpression(final String expression) {
- return new ExpressionAdapter() {
- private Expression exp;
-
- @Override
- public void init(CamelContext context) {
- if (expression != null) {
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- }
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- Object body;
- if (exp != null) {
- body = exp.evaluate(exchange, Object.class);
- } else {
- body = exchange.getMessage().getBody();
- }
- try {
- if (body instanceof byte[] arr) {
- return arr.length;
- } else if (body instanceof char[] arr) {
- return arr.length;
- } else if (body instanceof int[] arr) {
- return arr.length;
- } else if (body instanceof long[] arr) {
- return arr.length;
- } else if (body instanceof double[] arr) {
- return arr.length;
- } else if (body instanceof String[] arr) {
- return arr.length;
- } else if (body instanceof StreamCache sc) {
- return (int) sc.length();
- } else {
- // first read as stream
- InputStream is = null;
- try {
- is = exchange.getContext().getTypeConverter().tryConvertTo(InputStream.class, exchange, body);
- int len = 0;
- while (is.read() != -1) {
- len++;
- }
- return len;
- } catch (Exception e) {
- // ignore
- } finally {
- IOHelper.close(is);
- }
- // fallback to use string based
- String data = exchange.getContext().getTypeConverter().tryConvertTo(String.class, exchange, body);
- if (data != null) {
- return data.length();
- }
- }
- } finally {
- if (body instanceof StreamCache streamCache) {
- streamCache.reset();
- }
- }
- return null;
- }
-
- @Override
- public String toString() {
- if (expression != null) {
- return "length(" + expression + ")";
- } else {
- return "length()";
- }
- }
- };
- }
-
- /**
- * Normalizes the whitespaces in the given expressions (uses message body if expression is null)
- */
- public static Expression normalizeWhitespaceExpression(final String expression) {
- return new ExpressionAdapter() {
- private Expression exp;
-
- @Override
- public void init(CamelContext context) {
- if (expression != null) {
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- }
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- String value;
- if (exp != null) {
- value = exp.evaluate(exchange, String.class);
- } else {
- value = exchange.getMessage().getBody(String.class);
- }
- if (value != null) {
- value = StringHelper.normalizeWhitespace(value);
- }
- return value;
- }
-
- @Override
- public String toString() {
- if (expression != null) {
- return "normalizeWhitespace(" + expression + ")";
- } else {
- return "normalizeWhitespace()";
- }
- }
- };
- }
-
- /**
- * A ternary condition expression
- */
- public static Expression iifExpression(final String predicate, final String trueValue, final String falseValue) {
- return new ExpressionAdapter() {
- private Predicate pred;
- private Expression expTrue;
- private Expression expFalse;
-
- @Override
- public void init(CamelContext context) {
- pred = context.resolveLanguage("simple").createPredicate(predicate);
- pred.init(context);
- expTrue = context.resolveLanguage("simple").createExpression(trueValue);
- expTrue.init(context);
- expFalse = context.resolveLanguage("simple").createExpression(falseValue);
- expFalse.init(context);
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- if (pred.matches(exchange)) {
- return expTrue.evaluate(exchange, Object.class);
- } else {
- return expFalse.evaluate(exchange, Object.class);
- }
- }
-
- @Override
- public String toString() {
- return "iif(" + predicate + "," + trueValue + "," + falseValue + ")";
- }
- };
- }
-
- /**
- * An expression that returns the distinct values from the expressions
- */
- public static Expression distinctExpression(String[] values) {
- return new ExpressionAdapter() {
-
- private final Expression[] exps = new Expression[values != null ? values.length : 0];
-
- @Override
- public void init(CamelContext context) {
- for (int i = 0; values != null && i < values.length; i++) {
- Expression exp = context.resolveLanguage("simple").createExpression(values[i]);
- exp.init(context);
- exps[i] = exp;
- }
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- Set<Object> answer = new LinkedHashSet<>();
- for (Expression exp : exps) {
- Object o = exp.evaluate(exchange, Object.class);
- // this may be an object that we can iterate
- Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
- for (Object i : it) {
- answer.add(i);
- }
- }
- return answer;
- }
-
- @Override
- public String toString() {
- return "distinct(" + Arrays.toString(values) + ")";
- }
- };
- }
-
- /**
- * An expression that returns the elements in reverse order
- */
- public static Expression reverseExpression(String[] values) {
- return new ExpressionAdapter() {
-
- private final Expression[] exps = new Expression[values != null ? values.length : 0];
-
- @Override
- public void init(CamelContext context) {
- for (int i = 0; values != null && i < values.length; i++) {
- Expression exp = context.resolveLanguage("simple").createExpression(values[i]);
- exp.init(context);
- exps[i] = exp;
- }
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- List<Object> answer = new ArrayList<>();
- for (Expression exp : exps) {
- Object o = exp.evaluate(exchange, Object.class);
- // this may be an object that we can iterate
- Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
- for (Object i : it) {
- answer.add(i);
- }
- }
- Collections.reverse(answer);
- return answer;
- }
-
- @Override
- public String toString() {
- return "reverse(" + Arrays.toString(values) + ")";
- }
- };
- }
-
- /**
- * An expression that returns the elements in random order
- */
- public static Expression shuffleExpression(String[] values) {
- return new ExpressionAdapter() {
-
- private final Expression[] exps = new Expression[values != null ? values.length : 0];
-
- @Override
- public void init(CamelContext context) {
- for (int i = 0; values != null && i < values.length; i++) {
- Expression exp = context.resolveLanguage("simple").createExpression(values[i]);
- exp.init(context);
- exps[i] = exp;
- }
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- List<Object> answer = new ArrayList<>();
- for (Expression exp : exps) {
- Object o = exp.evaluate(exchange, Object.class);
- // this may be an object that we can iterate
- Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
- for (Object i : it) {
- answer.add(i);
- }
- }
- Collections.shuffle(answer);
- return answer;
- }
-
- @Override
- public String toString() {
- return "reverse(" + Arrays.toString(values) + ")";
- }
- };
- }
-
- /**
- * An expression that converts the expressions to number and sum the values
- */
- public static Expression sumExpression(String[] numbers) {
- return new ExpressionAdapter() {
-
- private final Expression[] exps = new Expression[numbers != null ? numbers.length : 0];
-
- @Override
- public void init(CamelContext context) {
- for (int i = 0; numbers != null && i < numbers.length; i++) {
- Expression exp = context.resolveLanguage("simple").createExpression(numbers[i]);
- exp.init(context);
- exps[i] = exp;
- }
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- Long answer = null;
- for (Expression exp : exps) {
- Object o = exp.evaluate(exchange, Object.class);
- // this may be an object that we can iterate
- Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
- for (Object i : it) {
- Long val = exchange.getContext().getTypeConverter().tryConvertTo(Long.class, exchange, i);
- if (val != null) {
- if (answer == null) {
- answer = 0L;
- }
- answer += val;
- }
- }
- }
- return answer;
- }
-
- @Override
- public String toString() {
- return "sum(" + Arrays.toString(numbers) + ")";
- }
- };
- }
-
- /**
- * An expression that converts the expressions to number and returns the maximum number
- */
- public static Expression maxExpression(String[] numbers) {
- return new ExpressionAdapter() {
-
- private final Expression[] exps = new Expression[numbers != null ? numbers.length : 0];
-
- @Override
- public void init(CamelContext context) {
- for (int i = 0; numbers != null && i < numbers.length; i++) {
- Expression exp = context.resolveLanguage("simple").createExpression(numbers[i]);
- exp.init(context);
- exps[i] = exp;
- }
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- Long answer = null;
- for (Expression exp : exps) {
- Object o = exp.evaluate(exchange, Object.class);
- // this may be an object that we can iterate
- Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
- for (Object i : it) {
- Long val = exchange.getContext().getTypeConverter().tryConvertTo(Long.class, exchange, i);
- if (val != null) {
- if (answer == null) {
- answer = val;
- }
- answer = Math.max(answer, val);
- }
- }
- }
- return answer;
- }
-
- @Override
- public String toString() {
- return "max(" + Arrays.toString(numbers) + ")";
- }
- };
- }
-
- /**
- * An expression that converts the expressions to number and returns the minimum number
- */
- public static Expression minExpression(String[] numbers) {
- return new ExpressionAdapter() {
-
- private final Expression[] exps = new Expression[numbers != null ? numbers.length : 0];
-
- @Override
- public void init(CamelContext context) {
- for (int i = 0; numbers != null && i < numbers.length; i++) {
- Expression exp = context.resolveLanguage("simple").createExpression(numbers[i]);
- exp.init(context);
- exps[i] = exp;
- }
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- Long answer = null;
- for (Expression exp : exps) {
- Object o = exp.evaluate(exchange, Object.class);
- // this may be an object that we can iterate
- Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
- for (Object i : it) {
- Long val = exchange.getContext().getTypeConverter().tryConvertTo(Long.class, exchange, i);
- if (val != null) {
- if (answer == null) {
- answer = val;
- }
- answer = Math.min(answer, val);
- }
- }
- }
- return answer;
- }
-
- @Override
- public String toString() {
- return "min(" + Arrays.toString(numbers) + ")";
- }
- };
- }
-
- /**
- * An expression that converts the expressions to number and returns the average number
- */
- public static Expression averageExpression(String[] numbers) {
- return new ExpressionAdapter() {
-
- private final Expression[] exps = new Expression[numbers != null ? numbers.length : 0];
-
- @Override
- public void init(CamelContext context) {
- for (int i = 0; numbers != null && i < numbers.length; i++) {
- Expression exp = context.resolveLanguage("simple").createExpression(numbers[i]);
- exp.init(context);
- exps[i] = exp;
- }
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- Long answer = null;
- int counter = 0;
- for (Expression exp : exps) {
- Object o = exp.evaluate(exchange, Object.class);
- // this may be an object that we can iterate
- Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
- for (Object i : it) {
- Long val = exchange.getContext().getTypeConverter().tryConvertTo(Long.class, exchange, i);
- if (val != null) {
- if (answer == null) {
- answer = 0L;
- }
- answer += val;
- counter++;
- }
- }
- }
- return answer != null ? answer / counter : null;
- }
-
- @Override
- public String toString() {
- return "average(" + Arrays.toString(numbers) + ")";
- }
- };
- }
-
- /**
- * An expression that creates an ArrayList
- */
- public static Expression listExpression(String[] values) {
- return new ExpressionAdapter() {
-
- private final Expression[] exps = new Expression[values != null ? values.length : 0];
-
- @Override
- public void init(CamelContext context) {
- for (int i = 0; values != null && i < values.length; i++) {
- Expression exp = context.resolveLanguage("simple").createExpression(values[i]);
- exp.init(context);
- exps[i] = exp;
- }
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- List<Object> answer = new ArrayList<>(values != null ? values.length : 0);
- for (Expression exp : exps) {
- Object val = exp.evaluate(exchange, Object.class);
- answer.add(val);
- }
- return answer;
- }
-
- @Override
- public String toString() {
- return "list(" + Arrays.toString(values) + ")";
- }
- };
- }
-
- /**
- * An expression that creates an LinkedHashMap
- */
- public static Expression mapExpression(String[] pairs) {
- return new ExpressionAdapter() {
-
- private final Expression[] keys = new Expression[pairs != null ? pairs.length / 2 : 0];
- private final Expression[] values = new Expression[pairs != null ? pairs.length / 2 : 0];
-
- @Override
- public void init(CamelContext context) {
- for (int i = 0, j = 0; pairs != null && i < pairs.length - 1; j++) {
- String key = pairs[i];
- String value = pairs[i + 1];
- Expression exp = context.resolveLanguage("simple").createExpression(key);
- exp.init(context);
- keys[j] = exp;
- exp = context.resolveLanguage("simple").createExpression(value);
- exp.init(context);
- values[j] = exp;
- i = i + 2;
- }
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- Map<String, Object> answer = new LinkedHashMap<>(keys.length);
- for (int i = 0; i < keys.length; i++) {
- String key = keys[i].evaluate(exchange, String.class);
- Object val = values[i].evaluate(exchange, Object.class);
- answer.put(key, val);
- }
- return answer;
- }
-
- @Override
- public String toString() {
- return "map(" + Arrays.toString(values) + ")";
- }
- };
- }
-
- /**
- * Joins together the values from the expression
- */
- public static Expression joinExpression(final String expression, final String separator, final String prefix) {
- return new ExpressionAdapter() {
- private Expression exp;
-
- @Override
- public void init(CamelContext context) {
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- exp = ExpressionBuilder.joinExpression(exp, separator, prefix);
- exp.init(context);
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- return exp.evaluate(exchange, Object.class);
- }
-
- @Override
- public String toString() {
- if (prefix != null) {
- return "join(" + expression + "," + separator + "," + prefix + ")";
- } else {
- return "join(" + expression + "," + separator + ")";
- }
- }
- };
- }
-
- /**
- * Split the String values from the expression using the given separator
- */
- public static Expression splitStringExpression(final String expression, final String separator) {
- return new ExpressionAdapter() {
- private Expression exp;
-
- @Override
- public void init(CamelContext context) {
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- String text = exp.evaluate(exchange, String.class);
- if (text == null) {
- return null;
- }
- return text.split(separator);
- }
-
- @Override
- public String toString() {
- return "split(" + expression + "," + separator + ")";
- }
- };
- }
-
- /**
- * Sorts the expression
- */
- public static Expression sortExpression(final String expression, final boolean reverse) {
- return new ExpressionAdapter() {
- private Expression exp;
-
- @Override
- public void init(CamelContext context) {
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- List answer = new ArrayList<>();
- Object o = exp.evaluate(exchange, Object.class);
- // this may be an object that we can iterate
- Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
- for (Object i : it) {
- answer.add(i);
- }
- Collections.sort(answer);
- if (reverse) {
- Collections.reverse(answer);
- }
- return answer;
- }
-
- @Override
- public String toString() {
- return "sort(" + expression + ")";
- }
- };
- }
-
- /**
- * For each value in the source expression then apply the function and return a list of responses from each function
- */
- public static Expression forEachExpression(final String source, final String function) {
- return new ExpressionAdapter() {
- private CamelContext context;
- private Expression exp1;
- private Expression exp2;
-
- @Override
- public void init(CamelContext context) {
- this.context = context;
- exp1 = context.resolveLanguage("simple").createExpression(source);
- exp1.init(context);
- exp2 = context.resolveLanguage("simple").createExpression(function);
- exp2.init(context);
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- List<Object> answer = new ArrayList<>();
- Object o = exp1.evaluate(exchange, Object.class);
- Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
- for (Object i : it) {
- // use a dummy exchange as the input is to be the message body
- Exchange dummy = ExchangeHelper.createCopy(exchange, true);
- dummy.getMessage().setBody(i);
- Object out = exp2.evaluate(dummy, Object.class);
- if (out != null) {
- answer.add(out);
- }
- }
- return answer;
- }
-
- @Override
- public String toString() {
- return "forEach(" + source + ", " + function + ")";
- }
- };
- }
-
- /**
- * Adds the result of the function to the source list
- */
- public static Expression listAddExpression(final String source, final String function) {
- return new ExpressionAdapter() {
- private CamelContext context;
- private Expression exp1;
- private Expression exp2;
-
- @Override
- public void init(CamelContext context) {
- this.context = context;
- exp1 = context.resolveLanguage("simple").createExpression(source);
- exp1.init(context);
- exp2 = context.resolveLanguage("simple").createExpression(function);
- exp2.init(context);
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- Collection<Object> col = exp1.evaluate(exchange, Collection.class);
- if (col != null) {
- Object value = exp2.evaluate(exchange, Object.class);
- if (value != null) {
- col.add(value);
- }
- }
- return col;
- }
-
- @Override
- public String toString() {
- return "listAdd(" + source + ", " + function + ")";
- }
- };
- }
-
- /**
- * Removes the result of the function from the source list
- */
- public static Expression listRemoveExpression(final String source, final String function) {
- return new ExpressionAdapter() {
- private CamelContext context;
- private Expression exp1;
- private Expression exp2;
-
- @Override
- public void init(CamelContext context) {
- this.context = context;
- exp1 = context.resolveLanguage("simple").createExpression(source);
- exp1.init(context);
- exp2 = context.resolveLanguage("simple").createExpression(function);
- exp2.init(context);
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- List<Object> list = exp1.evaluate(exchange, List.class);
- if (list != null) {
- Object value = exp2.evaluate(exchange, Object.class);
- if (value != null) {
- boolean removed = list.remove(value);
- if (!removed) {
- Integer pos;
- // special name to remove last
- if ("last".equals(value)) {
- pos = list.size() - 1;
- } else {
- // this may be an integer
- pos = context.getTypeConverter().tryConvertTo(int.class, exchange, value);
- }
- if (pos != null) {
- if (pos >= 0 && pos < list.size()) {
- list.remove((int) pos);
- }
- }
- }
- }
- }
- return list;
- }
-
- @Override
- public String toString() {
- return "listRemove(" + source + ", " + function + ")";
- }
- };
- }
-
- /**
- * Adds the result of the function to the source map
- */
- public static Expression mapAddExpression(final String source, final String key, final String function) {
- return new ExpressionAdapter() {
- private CamelContext context;
- private Expression exp1;
- private Expression exp2;
-
- @Override
- public void init(CamelContext context) {
- this.context = context;
- exp1 = context.resolveLanguage("simple").createExpression(source);
- exp1.init(context);
- exp2 = context.resolveLanguage("simple").createExpression(function);
- exp2.init(context);
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- Map<String, Object> map = exp1.evaluate(exchange, Map.class);
- if (map != null) {
- Object value = exp2.evaluate(exchange, Object.class);
- if (value != null) {
- map.put(key, value);
- }
- }
- return map;
- }
-
- @Override
- public String toString() {
- return "mapAdd(" + source + ", " + key + ", " + function + ")";
- }
- };
- }
-
- /**
- * Removes the result of the function from the source map
- */
- public static Expression mapRemoveExpression(final String source, final String key) {
- return new ExpressionAdapter() {
- private CamelContext context;
- private Expression exp1;
-
- @Override
- public void init(CamelContext context) {
- this.context = context;
- exp1 = context.resolveLanguage("simple").createExpression(source);
- exp1.init(context);
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- Map<String, Object> map = exp1.evaluate(exchange, Map.class);
- if (map != null) {
- map.remove(key);
- }
- return map;
- }
-
- @Override
- public String toString() {
- return "mapRemove(" + source + ", " + key + ")";
- }
- };
- }
-
- /**
- * Filters the values from the source that matches the predicate function
- */
- public static Expression filterExpression(final String source, final String function) {
- return new ExpressionAdapter() {
- private CamelContext context;
- private Expression exp1;
- private Predicate exp2;
-
- @Override
- public void init(CamelContext context) {
- this.context = context;
- exp1 = context.resolveLanguage("simple").createExpression(source);
- exp1.init(context);
- exp2 = context.resolveLanguage("simple").createPredicate(function);
- exp2.init(context);
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- List<Object> answer = new ArrayList<>();
- Object o = exp1.evaluate(exchange, Object.class);
- Iterable<?> it = org.apache.camel.support.ObjectHelper.createIterable(o);
- for (Object i : it) {
- // use a dummy exchange as the input is to be the message body
- Exchange dummy = ExchangeHelper.createCopy(exchange, true);
- dummy.getMessage().setBody(i);
- if (exp2.matches(dummy)) {
- answer.add(i);
- }
- }
- return answer;
- }
-
- @Override
- public String toString() {
- return "filter(" + source + ", " + function + ")";
- }
- };
- }
-
- /**
- * Sets the message header with the given expression value
- */
- public static Expression setHeaderExpression(final String name, final String type, final String expression) {
- return new ExpressionAdapter() {
- private ClassResolver classResolver;
- private Expression exp;
-
- @Override
- public void init(CamelContext context) {
- classResolver = context.getClassResolver();
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- Class<?> clazz = Object.class;
- if (type != null) {
- try {
- clazz = classResolver.resolveMandatoryClass(type);
- } catch (ClassNotFoundException e) {
- throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
- }
- }
- Object value = exp.evaluate(exchange, clazz);
- if (value != null) {
- exchange.getMessage().setHeader(name, value);
- } else {
- exchange.getMessage().removeHeader(name);
- }
- // does not return anything
- return null;
- }
-
- @Override
- public String toString() {
- return "setHeader(" + name + "," + expression + ")";
- }
- };
- }
-
- /**
- * Sets the variable with the given expression value
- */
- public static Expression setVariableExpression(final String name, final String type, final String expression) {
- return new ExpressionAdapter() {
- private ClassResolver classResolver;
- private Expression exp;
-
- @Override
- public void init(CamelContext context) {
- classResolver = context.getClassResolver();
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- Class<?> clazz = Object.class;
- if (type != null) {
- try {
- clazz = classResolver.resolveMandatoryClass(type);
- } catch (ClassNotFoundException e) {
- throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
- }
- }
- Object value = exp.evaluate(exchange, clazz);
- if (value != null) {
- exchange.setVariable(name, value);
- } else {
- exchange.removeVariable(name);
- }
- // does not return anything
- return null;
- }
-
- @Override
- public String toString() {
- return "setVariable(" + name + "," + expression + ")";
- }
- };
- }
-
- /**
- * Replaces string values from the expression
- */
- public static Expression replaceExpression(final String expression, final String from, final String to) {
- return new ExpressionAdapter() {
- private Expression exp;
-
- @Override
- public void init(CamelContext context) {
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- exp = ExpressionBuilder.replaceAll(exp, from, to);
- exp.init(context);
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- return exp.evaluate(exchange, Object.class);
- }
-
- @Override
- public String toString() {
- return "replace(" + expression + "," + from + "," + to + ")";
- }
- };
- }
-
- /**
- * Substring string values from the expression
- */
- public static Expression substringExpression(final String expression, final String head, final String tail) {
- return new ExpressionAdapter() {
- private Expression exp;
- private Expression exp1;
- private Expression exp2;
-
- @Override
- public void init(CamelContext context) {
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- exp1 = ExpressionBuilder.simpleExpression(head);
- exp1.init(context);
- exp2 = ExpressionBuilder.simpleExpression(tail);
- exp2.init(context);
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- int num1 = exp1.evaluate(exchange, Integer.class);
- int num2 = exp2.evaluate(exchange, Integer.class);
- if (num1 < 0 && num2 == 0) {
- // if there is only one value and its negative then we want to clip from tail
- num2 = num1;
- num1 = 0;
- }
- num1 = Math.abs(num1);
- num2 = Math.abs(num2);
- return ExpressionBuilder.substring(exp, num1, num2).evaluate(exchange, Object.class);
- }
-
- @Override
- public String toString() {
- return "substring(" + expression + "," + head + "," + tail + ")";
- }
- };
- }
-
- /**
- * Returns the substring from the given expression that comes before
- */
- public static Expression substringBeforeExpression(final String expression, final String before) {
- return new ExpressionAdapter() {
- private Expression exp;
- private Expression expBefore;
-
- @Override
- public void init(CamelContext context) {
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- expBefore = ExpressionBuilder.simpleExpression(before);
- expBefore.init(context);
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- String body = exp.evaluate(exchange, String.class);
- if (body == null) {
- return null;
- }
- String bef = expBefore.evaluate(exchange, String.class);
- return StringHelper.before(body, bef);
- }
-
- @Override
- public String toString() {
- return "substringBefore(" + expression + "," + before + ")";
- }
- };
- }
-
- /**
- * Returns the substring from the given expression that comes after
- */
- public static Expression substringAfterExpression(final String expression, final String after) {
- return new ExpressionAdapter() {
- private Expression exp;
- private Expression expAfter;
-
- @Override
- public void init(CamelContext context) {
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- expAfter = ExpressionBuilder.simpleExpression(after);
- expAfter.init(context);
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- String body = exp.evaluate(exchange, String.class);
- if (body == null) {
- return null;
- }
- String aft = expAfter.evaluate(exchange, String.class);
- return StringHelper.after(body, aft);
- }
-
- @Override
- public String toString() {
- return "substringAfter(" + expression + "," + after + ")";
- }
- };
- }
-
- /**
- * Returns the substring from the given expression that are between after and before
- */
- public static Expression substringBetweenExpression(
- final String expression, final String after,
- final String before) {
- return new ExpressionAdapter() {
- private Expression exp;
- private Expression expAfter;
- private Expression expBefore;
-
- @Override
- public void init(CamelContext context) {
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- expAfter = ExpressionBuilder.simpleExpression(after);
- expAfter.init(context);
- expBefore = ExpressionBuilder.simpleExpression(before);
- expBefore.init(context);
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- String body = exp.evaluate(exchange, String.class);
- if (body == null) {
- return null;
- }
- String aft = expAfter.evaluate(exchange, String.class);
- String bef = expBefore.evaluate(exchange, String.class);
- return StringHelper.between(body, aft, bef);
- }
-
- @Override
- public String toString() {
- return "substringBetween(" + expression + "," + after + "," + before + ")";
- }
- };
- }
-
- /**
- * Whether the expression matches the pattern
- */
- public static Expression containsExpression(final String expression, final String pattern) {
- return new ExpressionAdapter() {
- private Expression right;
- private Expression left;
-
- @Override
- public void init(CamelContext context) {
- left = context.resolveLanguage("simple").createExpression(expression);
- left.init(context);
- right = context.resolveLanguage("simple").createExpression(pattern);
- right.init(context);
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- return PredicateBuilder.containsIgnoreCase(left, right).matches(exchange);
- }
-
- @Override
- public String toString() {
- return "contains(" + expression + "," + pattern + ")";
- }
- };
- }
-
- /**
- * Hashes the value using the given algorithm
- */
- public static Expression hashExpression(final String expression, final String algorithm) {
- return new ExpressionAdapter() {
- private Expression exp;
-
- @Override
- public void init(CamelContext context) {
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- InputStream is = exp.evaluate(exchange, InputStream.class);
- if (is != null) {
- try {
- // calculate the hash in chunks in case the payload is big
- MessageDigest digest = MessageDigest.getInstance(algorithm);
- DigestInputStream dis = new DigestInputStream(is, digest);
- IOHelper.copy(dis, new OutputStream() {
- @Override
- public void write(int b) throws IOException {
- // ignore
- }
- });
- return StringHelper.bytesToHex(digest.digest());
- } catch (Exception e) {
- throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
- } finally {
- // reset cached streams so they can be read again
- MessageHelper.resetStreamCache(exchange.getMessage());
- }
- }
- return null;
- }
-
- @Override
- public String toString() {
- return "hash(" + expression + "," + algorithm + ")";
- }
- };
- }
-
- /**
- * Returns a random number between min and max (exclusive)
- */
- public static Expression randomExpression(final String min, final String max) {
- return new ExpressionAdapter() {
- private Expression exp1;
- private Expression exp2;
-
- @Override
- public Object evaluate(Exchange exchange) {
- int num1 = exp1.evaluate(exchange, Integer.class);
- int num2 = exp2.evaluate(exchange, Integer.class);
- Random random = new Random(); // NOSONAR
- return random.nextInt(num2 - num1) + num1;
- }
-
- @Override
- public void init(CamelContext context) {
- exp1 = ExpressionBuilder.simpleExpression(min);
- exp1.init(context);
- exp2 = ExpressionBuilder.simpleExpression(max);
- exp2.init(context);
- }
-
- @Override
- public String toString() {
- return "random(" + min + "," + max + ")";
- }
- };
- }
-
- /**
- * Returns a range of number between min and max (exclusive)
- */
- public static Expression rangeExpression(final String min, final String max) {
- return new ExpressionAdapter() {
- private Expression exp1;
- private Expression exp2;
-
- @Override
- public Object evaluate(Exchange exchange) {
- int num1 = exp1.evaluate(exchange, Integer.class);
- int num2 = exp2.evaluate(exchange, Integer.class);
- if (num1 >= 0 && num1 <= num2 && num1 != num2) {
- List<Integer> answer = new ArrayList<>();
- for (int i = num1; i < num2; i++) {
- answer.add(i);
- }
- return answer;
- }
- return null;
- }
-
- @Override
- public void init(CamelContext context) {
- exp1 = ExpressionBuilder.simpleExpression(min);
- exp1.init(context);
- exp2 = ExpressionBuilder.simpleExpression(max);
- exp2.init(context);
- }
-
- @Override
- public String toString() {
- return "range(" + min + "," + max + ")";
- }
- };
- }
-
- /**
- * Returns a random number between 0 and max (exclusive)
- */
- public static Expression randomExpression(final int max) {
- return randomExpression(0, max);
- }
-
- /**
- * Returns a random number between min and max (exclusive)
- */
- public static Expression randomExpression(final int min, final int max) {
- return new ExpressionAdapter() {
- @Override
- public Object evaluate(Exchange exchange) {
- Random random = new Random(); // NOSONAR
- return random.nextInt(max - min) + min;
- }
-
- @Override
- public String toString() {
- return "random(" + min + "," + max + ")";
- }
- };
- }
-
- /**
- * Returns a new empty object of the given type
- */
- public static Expression newEmptyExpression(final String type) {
-
- return new ExpressionAdapter() {
- @Override
- public Object evaluate(Exchange exchange) {
- if ("map".equalsIgnoreCase(type)) {
- return new LinkedHashMap<>();
- } else if ("string".equalsIgnoreCase(type)) {
- return "";
- } else if ("list".equalsIgnoreCase(type)) {
- return new ArrayList<>();
- } else if ("set".equalsIgnoreCase(type)) {
- return new LinkedHashSet<>();
- }
- throw new IllegalArgumentException("function empty(%s) has unknown type".formatted(type));
- }
-
- @Override
- public String toString() {
- return "empty(%s)".formatted(type);
- }
- };
- }
-
- /**
- * Returns an uuid string based on the given generator (default, classic, short, simple)
- */
- public static Expression uuidExpression(final String generator) {
- return new ExpressionAdapter() {
-
- UuidGenerator uuid;
-
- @Override
- public Object evaluate(Exchange exchange) {
- return uuid.generateUuid();
- }
-
- @Override
- public void init(CamelContext context) {
- if ("classic".equalsIgnoreCase(generator)) {
- uuid = new ClassicUuidGenerator();
- } else if ("short".equals(generator)) {
- uuid = new ShortUuidGenerator();
- } else if ("simple".equals(generator)) {
- uuid = new SimpleUuidGenerator();
- } else if ("random".equals(generator)) {
- uuid = new RandomUuidGenerator();
- } else if (generator == null || "default".equals(generator)) {
- uuid = new DefaultUuidGenerator();
- } else {
- // lookup custom generator
- uuid = CamelContextHelper.mandatoryLookup(context, generator, UuidGenerator.class);
- }
- }
-
- @Override
- public String toString() {
- if (generator != null) {
- return "uuid(" + generator + ")";
- } else {
- return "uuid";
- }
- }
- };
- }
-
- public static Expression fileNameExpression() {
- return new ExpressionAdapter() {
- @Override
- public Object evaluate(Exchange exchange) {
- return exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
- }
-
- @Override
- public String toString() {
- return "file:name";
- }
- };
- }
-
- public static Expression fileOnlyNameExpression() {
- return new ExpressionAdapter() {
- @Override
- public Object evaluate(Exchange exchange) {
- String answer = exchange.getIn().getHeader(Exchange.FILE_NAME_ONLY, String.class);
- if (answer == null) {
- answer = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
- answer = FileUtil.stripPath(answer);
- }
- return answer;
- }
-
- @Override
- public String toString() {
- return "file:onlyname";
- }
- };
- }
-
- public static Expression fileNameNoExtensionExpression() {
- return new ExpressionAdapter() {
- @Override
- public Object evaluate(Exchange exchange) {
- String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
- return FileUtil.stripExt(name);
- }
-
- @Override
- public String toString() {
- return "file:name.noext";
- }
- };
- }
-
- public static Expression fileNameNoExtensionSingleExpression() {
- return new ExpressionAdapter() {
- @Override
- public Object evaluate(Exchange exchange) {
- String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
- return FileUtil.stripExt(name, true);
- }
-
- @Override
- public String toString() {
- return "file:name.noext.single";
- }
- };
- }
-
- public static Expression fileOnlyNameNoExtensionExpression() {
- return new ExpressionAdapter() {
- @Override
- public Object evaluate(Exchange exchange) {
- String name = fileOnlyNameExpression().evaluate(exchange, String.class);
- return FileUtil.stripExt(name);
- }
-
- @Override
- public String toString() {
- return "file:onlyname.noext";
- }
- };
- }
-
- public static Expression fileOnlyNameNoExtensionSingleExpression() {
- return new ExpressionAdapter() {
- @Override
- public Object evaluate(Exchange exchange) {
- String name = fileOnlyNameExpression().evaluate(exchange, String.class);
- return FileUtil.stripExt(name, true);
- }
-
- @Override
- public String toString() {
- return "file:onlyname.noext.single";
- }
- };
- }
-
- public static Expression fileExtensionExpression() {
- return new ExpressionAdapter() {
- @Override
- public Object evaluate(Exchange exchange) {
- String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
- return FileUtil.onlyExt(name);
- }
-
- @Override
- public String toString() {
- return "file:ext";
- }
- };
- }
-
- public static Expression fileExtensionSingleExpression() {
- return new ExpressionAdapter() {
- @Override
- public Object evaluate(Exchange exchange) {
- String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
- return FileUtil.onlyExt(name, true);
- }
-
- @Override
- public String toString() {
- return "file:ext.single";
- }
- };
- }
-
- public static Expression fileParentExpression() {
- return new ExpressionAdapter() {
- @Override
- public Object evaluate(Exchange exchange) {
- return exchange.getIn().getHeader("CamelFileParent", String.class);
- }
-
- @Override
- public String toString() {
- return "file:parent";
- }
- };
- }
-
- public static Expression filePathExpression() {
- return new ExpressionAdapter() {
- @Override
- public Object evaluate(Exchange exchange) {
- return exchange.getIn().getHeader("CamelFilePath", String.class);
- }
-
- @Override
- public String toString() {
- return "file:path";
- }
- };
- }
-
- public static Expression fileAbsolutePathExpression() {
- return new ExpressionAdapter() {
- @Override
- public Object evaluate(Exchange exchange) {
- return exchange.getIn().getHeader("CamelFileAbsolutePath", String.class);
- }
-
- @Override
- public String toString() {
- return "file:absolute.path";
- }
- };
- }
-
- public static Expression fileAbsoluteExpression() {
- return new ExpressionAdapter() {
- @Override
- public Object evaluate(Exchange exchange) {
- return exchange.getIn().getHeader("CamelFileAbsolute", Boolean.class);
- }
-
- @Override
- public String toString() {
- return "file:absolute";
- }
- };
- }
-
- public static Expression fileSizeExpression() {
- return new ExpressionAdapter() {
- @Override
- public Object evaluate(Exchange exchange) {
- return exchange.getIn().getHeader(Exchange.FILE_LENGTH, Long.class);
- }
-
- @Override
- public String toString() {
- return "file:length";
- }
- };
- }
-
- public static Expression fileLastModifiedExpression() {
- return new ExpressionAdapter() {
- @Override
- public Object evaluate(Exchange exchange) {
- return exchange.getIn().getHeader(Exchange.FILE_LAST_MODIFIED, Long.class);
- }
-
- @Override
- public String toString() {
- return "file:modified";
- }
- };
- }
-
- public static Expression dateExpression(final String command) {
- return dateExpression(command, null, null);
- }
-
- public static Expression dateExpression(final String command, final String pattern) {
- return dateExpression(command, null, pattern);
- }
-
- public static Expression dateExpression(
- final String commandWithOffsets, final String timezone,
- final String pattern) {
- final String command = commandWithOffsets.split("[+-]", 2)[0].trim();
- final List<Long> offsets = LanguageHelper.captureOffsets(commandWithOffsets, OFFSET_PATTERN);
-
- return new ExpressionAdapter() {
- @Override
- public Object evaluate(Exchange exchange) {
- Date date = evalDate(exchange, command);
- Object answer = LanguageHelper.applyDateOffsets(date, offsets, pattern, timezone);
- if ("millis".equals(command) && answer instanceof Date d) {
- answer = d.getTime();
- }
- return answer;
- }
-
- @Override
- public String toString() {
- if (timezone != null && pattern != null) {
- return "date(" + commandWithOffsets + ":" + timezone + ":" + pattern + ")";
- } else if (pattern != null) {
- return "date(" + commandWithOffsets + ":" + pattern + ")";
- } else {
- return "date(" + commandWithOffsets + ")";
- }
- }
- };
- }
-
- private static Date evalDate(Exchange exchange, String command) {
- Date date;
- if ("now".equals(command) || "millis".equals(command)) {
- date = new Date();
- } else if ("exchangeCreated".equals(command)) {
- date = LanguageHelper.dateFromExchangeCreated(exchange);
- } else if (command.startsWith("header.")) {
- date = LanguageHelper.dateFromHeader(exchange, command, (e, o) -> tryConvertingAsDate(e, o, command));
- } else if (command.startsWith("variable.")) {
- date = LanguageHelper.dateFromVariable(exchange, command, (e, o) -> tryConvertingAsDate(e, o, command));
- } else if (command.startsWith("exchangeProperty.")) {
- date = LanguageHelper.dateFromExchangeProperty(exchange, command, (e, o) -> tryConvertingAsDate(e, o, command));
- } else if ("file".equals(command)) {
- date = LanguageHelper.dateFromFileLastModified(exchange, command);
- } else {
- throw new IllegalArgumentException("Command not supported for dateExpression: " + command);
- }
- return date;
- }
-
- private static Date tryConvertingAsDate(Exchange exchange, Object obj, String command) {
- final Date date = exchange.getContext().getTypeConverter().tryConvertTo(Date.class, exchange, obj);
- if (date == null) {
- throw new IllegalArgumentException("Cannot find Date/long object at command: " + command);
- }
-
- return date;
- }
-
- public static Expression skipIteratorExpression(final Expression expression, final int skip) {
- return new ExpressionAdapter() {
- @Override
- public Object evaluate(Exchange exchange) {
- // evaluate expression as iterator
- Iterator<?> it = expression.evaluate(exchange, Iterator.class);
- ObjectHelper.notNull(it,
- "expression: " + expression + " evaluated on " + exchange + " must return an java.util.Iterator");
- return new SkipIterator(it, skip);
- }
-
- @Override
- public String toString() {
- return "skip " + expression + " " + skip + " times";
- }
- };
- }
-
- /**
- * Returns the expression for the {@code null} value
- */
- public static Expression nullExpression() {
- return new ExpressionAdapter() {
- @Override
- public Object evaluate(Exchange exchange) {
- return null;
- }
-
- @Override
- public String toString() {
- return "null";
- }
- };
- }
-
- /**
- * Returns the expression for the exchanges inbound message body converted to the given type and invoking methods on
- * the converted body defined in a simple OGNL notation
- */
- public static Expression mandatoryBodyOgnlExpression(final String name, final String ognl) {
- return new ExpressionAdapter() {
- private ClassResolver classResolver;
- private Expression exp;
- private Language bean;
-
- @Override
- public Object evaluate(Exchange exchange) {
- String text = exp.evaluate(exchange, String.class);
- Class<?> type;
- try {
- type = classResolver.resolveMandatoryClass(text);
- } catch (ClassNotFoundException e) {
- throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
- }
- Object body;
- try {
- body = exchange.getIn().getMandatoryBody(type);
- } catch (InvalidPayloadException e) {
- throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
- }
- Expression ognlExp = bean.createExpression(null, new Object[] { null, body, ognl });
- ognlExp.init(exchange.getContext());
- return ognlExp.evaluate(exchange, Object.class);
- }
-
- @Override
- public void init(CamelContext context) {
- classResolver = context.getClassResolver();
- exp = ExpressionBuilder.simpleExpression(name);
- exp.init(context);
- bean = context.resolveLanguage("bean");
- }
-
- @Override
- public String toString() {
- return "mandatoryBodyAs[" + name + "](" + ognl + ")";
- }
- };
- }
-
- /**
- * Returns the expression for the exchanges inbound message body converted to the given type
- */
- public static Expression mandatoryBodyExpression(final String name) {
- return new ExpressionAdapter() {
- private ClassResolver classResolver;
- private Expression exp;
-
- @Override
- public Object evaluate(Exchange exchange) {
- String text = exp.evaluate(exchange, String.class);
- Class<?> type;
- try {
- type = classResolver.resolveMandatoryClass(text);
- } catch (ClassNotFoundException e) {
- throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
- }
- try {
- return exchange.getIn().getMandatoryBody(type);
- } catch (InvalidPayloadException e) {
- throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
- }
- }
-
- @Override
- public void init(CamelContext context) {
- classResolver = context.getClassResolver();
- exp = ExpressionBuilder.simpleExpression(name);
- exp.init(context);
- }
-
- @Override
- public String toString() {
- return "mandatoryBodyAs[" + name + "]";
- }
- };
- }
-
- /**
- * Returns the expression for the message converted to the given type and invoking methods on the converted message
- * defined in a simple OGNL notation
- */
- public static Expression messageOgnlExpression(final String name, final String ognl) {
- return new ExpressionAdapter() {
- private ClassResolver classResolver;
- private Expression exp;
- private Language bean;
-
- @Override
- public Object evaluate(Exchange exchange) {
- String text = exp.evaluate(exchange, String.class);
- Class<?> type;
- try {
- type = classResolver.resolveMandatoryClass(text);
- } catch (ClassNotFoundException e) {
- throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
- }
- Object msg = exchange.getMessage(type);
- if (msg != null) {
- // ognl is able to evaluate method name if it contains nested functions
- // so we should not eager evaluate ognl as a string
- Expression ognlExp = bean.createExpression(null, new Object[] { null, msg, ognl });
- ognlExp.init(exchange.getContext());
- return ognlExp.evaluate(exchange, Object.class);
- } else {
- return null;
- }
- }
-
- @Override
- public void init(CamelContext context) {
- classResolver = context.getClassResolver();
- exp = ExpressionBuilder.simpleExpression(name);
- exp.init(context);
- bean = context.resolveLanguage("bean");
- }
-
- @Override
- public String toString() {
- return "messageOgnlAs[" + name + "](" + ognl + ")";
- }
- };
- }
-
- /**
- * Returns the expression for the exchanges inbound message body converted to the given type and invoking methods on
- * the converted body defined in a simple OGNL notation
- */
- public static Expression bodyOgnlExpression(final String name, final String ognl) {
- return new ExpressionAdapter() {
- private ClassResolver classResolver;
- private Expression exp;
- private Language bean;
-
- @Override
- public Object evaluate(Exchange exchange) {
- String text = exp.evaluate(exchange, String.class);
- Class<?> type;
- try {
- type = classResolver.resolveMandatoryClass(text);
- } catch (ClassNotFoundException e) {
- throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
- }
- Object body = exchange.getIn().getBody(type);
- if (body != null) {
- // ognl is able to evaluate method name if it contains nested functions
- // so we should not eager evaluate ognl as a string
- Expression ognlExp = bean.createExpression(null, new Object[] { null, body, ognl });
- ognlExp.init(exchange.getContext());
- return ognlExp.evaluate(exchange, Object.class);
- } else {
- return null;
- }
- }
-
- @Override
- public void init(CamelContext context) {
- classResolver = context.getClassResolver();
- exp = ExpressionBuilder.simpleExpression(name);
- exp.init(context);
- bean = context.resolveLanguage("bean");
- }
-
- @Override
- public String toString() {
- return "bodyOgnlAs[" + name + "](" + ognl + ")";
- }
- };
- }
-
- /**
- * Returns the expression for the exchange invoking methods defined in a simple OGNL notation
- *
- * @param ognl methods to invoke on the exchange in a simple OGNL syntax
- */
- public static Expression exchangeOgnlExpression(final String ognl) {
- return new ExpressionAdapter() {
- private Language bean;
-
- @Override
- public Object evaluate(Exchange exchange) {
- // ognl is able to evaluate method name if it contains nested functions
- // so we should not eager evaluate ognl as a string
- Expression ognlExp = bean.createExpression(null, new Object[] { null, exchange, ognl });
- ognlExp.init(exchange.getContext());
- return ognlExp.evaluate(exchange, Object.class);
- }
-
- @Override
- public void init(CamelContext context) {
- bean = context.resolveLanguage("bean");
- }
-
- @Override
- public String toString() {
- return "exchangeOgnl(" + ognl + ")";
- }
- };
- }
-
- /**
- * Returns the expression for the exchanges camelContext invoking methods defined in a simple OGNL notation
- *
- * @param ognl methods to invoke on the context in a simple OGNL syntax
- */
- public static Expression camelContextOgnlExpression(final String ognl) {
- return new ExpressionAdapter() {
- private Expression exp;
-
- @Override
- public void init(CamelContext context) {
- exp = ExpressionBuilder.beanExpression(context, ognl);
- exp.init(context);
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- // ognl is able to evaluate method name if it contains nested functions
- // so we should not eager evaluate ognl as a string
- return exp.evaluate(exchange, Object.class);
- }
-
- @Override
- public String toString() {
- return "camelContextOgnl(" + ognl + ")";
- }
- };
- }
-
- /**
- * Returns the expression for the exchanges inbound message body invoking methods defined in a simple OGNL notation
- *
- * @param ognl methods to invoke on the body in a simple OGNL syntax
- */
- public static Expression bodyOgnlExpression(final String ognl) {
- return new ExpressionAdapter() {
- private Language bean;
-
- @Override
- public Object evaluate(Exchange exchange) {
- Object body = exchange.getIn().getBody();
- if (body == null) {
- return null;
- }
- Expression ognlExp = bean.createExpression(null, new Object[] { null, body, ognl });
- ognlExp.init(exchange.getContext());
- return ognlExp.evaluate(exchange, Object.class);
- }
-
- @Override
- public void init(CamelContext context) {
- bean = context.resolveLanguage("bean");
- }
-
- @Override
- public String toString() {
- return "bodyOgnl(" + ognl + ")";
- }
- };
- }
-
- /**
- * Returns an expression that caches the evaluation of another expression and returns the cached value, to avoid
- * re-evaluating the expression.
- *
- * @param expression the target expression to cache
- * @return the cached value
- */
- public static Expression cacheExpression(final Expression expression) {
- return new ExpressionAdapter() {
- private final AtomicReference<Object> cache = new AtomicReference<>();
-
- @Override
- public Object evaluate(Exchange exchange) {
- Object answer = cache.get();
- if (answer == null) {
- answer = expression.evaluate(exchange, Object.class);
- cache.set(answer);
- }
- return answer;
- }
-
- @Override
- public String toString() {
- return expression.toString();
- }
- };
- }
-
- /**
- * Loads the given resource from classpath
- */
- public static Expression loadExpression(final String expression) {
- return new ExpressionAdapter() {
-
- private Expression exp;
-
- @Override
- public void init(CamelContext context) {
- exp = context.resolveLanguage("simple").createExpression(expression);
- exp.init(context);
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- String name = exp.evaluate(exchange, String.class);
- if (name != null) {
- name = name.trim();
- String part = StringHelper.after(name, ":", name);
- boolean optional = part.endsWith("?optional=true");
- if (optional) {
- part = part.substring(0, part.length() - 14);
- }
- if (part.endsWith("?optional=false")) {
- part = part.substring(0, part.length() - 15);
- }
- try {
- InputStream is;
- if (!optional) {
- is = ResourceHelper.resolveMandatoryResourceAsInputStream(exchange.getContext(), part);
- } else {
- is = ResourceHelper.resolveResourceAsInputStream(exchange.getContext(), part);
- }
- if (is == null) {
- return null;
- }
- return IOHelper.loadText(is, false);
- } catch (Exception e) {
- throw RuntimeCamelException.wrapRuntimeException(e);
- }
- }
- return null;
- }
-
- @Override
- public String toString() {
- return "load(" + expression + ")";
- }
- };
- }
-
- /**
- * Returns an expression for a type value
- *
- * @param name the type name
- * @return an expression object which will return the type value
- */
- public static Expression typeExpression(final String name) {
- return new ExpressionAdapter() {
- private ClassResolver classResolver;
- private Expression exp;
-
- @Override
- public void init(CamelContext context) {
- classResolver = context.getClassResolver();
- exp = ExpressionBuilder.simpleExpression(name);
- exp.init(context);
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- // it may refer to a class type
- String text = exp.evaluate(exchange, String.class);
- Class<?> type = classResolver.resolveClass(text);
- if (type != null) {
- return type;
- }
-
- int pos = text.lastIndexOf('.');
- if (pos > 0) {
- String before = text.substring(0, pos);
- String after = text.substring(pos + 1);
- type = classResolver.resolveClass(before);
- if (type != null) {
- // special for enum constants
- if (type.isEnum()) {
- Class<Enum<?>> enumClass = (Class<Enum<?>>) type;
- for (Enum<?> enumValue : enumClass.getEnumConstants()) {
- if (enumValue.name().equalsIgnoreCase(after)) {
- return type.cast(enumValue);
- }
- }
- throw CamelExecutionException.wrapCamelExecutionException(exchange,
- new ClassNotFoundException("Cannot find enum: " + after + " on type: " + type));
- } else {
- // we assume it is a field constant
- Object answer = ObjectHelper.lookupConstantFieldValue(type, after);
- if (answer != null) {
- return answer;
- }
- }
- }
- }
-
- throw CamelExecutionException.wrapCamelExecutionException(exchange,
- new ClassNotFoundException("Cannot find type: " + text));
- }
-
- @Override
- public String toString() {
- return "type:" + name;
- }
- };
- }
-
- /**
- * Returns an expression for the property value of exchange with the given name invoking methods defined in a simple
- * OGNL notation
- *
- * @param ognl methods to invoke on the property in a simple OGNL syntax
- */
- public static Expression propertyOgnlExpression(final String ognl) {
- return new KeyedOgnlExpressionAdapter(
- ognl, "propertyOgnl(" + ognl + ")",
- (exchange, exp) -> {
- String text = exp.evaluate(exchange, String.class);
- return exchange.getProperty(text);
- });
- }
-
- /**
- * Returns the expression for the exchange's exception invoking methods defined in a simple OGNL notation
- *
- * @param ognl methods to invoke on the body in a simple OGNL syntax
- */
- public static Expression exchangeExceptionOgnlExpression(final String ognl) {
- return new ExpressionAdapter() {
- private Language bean;
-
- @Override
- public Object evaluate(Exchange exchange) {
- Object exception = exchange.getException();
- if (exception == null) {
- exception = exchange.getProperty(ExchangePropertyKey.EXCEPTION_CAUGHT, Exception.class);
- }
-
- if (exception == null) {
- return null;
- }
-
- // ognl is able to evaluate method name if it contains nested functions
- // so we should not eager evaluate ognl as a string
- Expression ognlExp = bean.createExpression(null, new Object[] { null, exception, ognl });
- ognlExp.init(exchange.getContext());
- return ognlExp.evaluate(exchange, Object.class);
- }
-
- @Override
- public void init(CamelContext context) {
- bean = context.resolveLanguage("bean");
- }
-
- @Override
- public String toString() {
- return "exchangeExceptionOgnl(" + ognl + ")";
- }
- };
- }
-
- /**
- * Executes a custom simple function
- */
- public static Expression customFunction(final String name, final String parameter) {
- return new ExpressionAdapter() {
- private Expression func;
- private Expression exp;
-
- @Override
- public void init(CamelContext context) {
- super.init(context);
- SimpleFunctionRegistry registry
- = context.getCamelContextExtension().getContextPlugin(SimpleFunctionRegistry.class);
- func = registry.getFunction(name);
- if (func == null) {
- throw new IllegalArgumentException("No custom simple function with name: " + name);
- }
- exp = ExpressionBuilder.simpleExpression(parameter);
- exp.init(context);
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- final Object originalBody = exchange.getMessage().getBody();
- try {
- Object input = exp.evaluate(exchange, Object.class);
- exchange.getMessage().setBody(input);
- return func.evaluate(exchange, Object.class);
- } finally {
- exchange.getMessage().setBody(originalBody);
- }
- }
-
- public String toString() {
- return "function(" + name + ")";
- }
- };
- }
-
- /**
- * Evaluates the simple jsonpath with the source input
- */
- public static Expression simpleJsonPathExpression(String source, String path) {
- return new ExpressionAdapter() {
- private Expression input;
-
- @Override
- public Object evaluate(Exchange exchange) {
- Jsonable j = input.evaluate(exchange, Jsonable.class);
- if (j instanceof JsonObject jo) {
- return jo.path(path);
- } else if (j instanceof JsonArray ja) {
- // wrap array in pseudo root to leverage json-path here
- JsonObject jo = new JsonObject();
- jo.put("_root_", ja);
- return jo.path("_root_." + path);
- }
- return null;
- }
-
- @Override
- public void init(CamelContext context) {
- super.init(context);
- input = ExpressionBuilder.singleInputExpression(source);
- input.init(context);
- }
-
- @Override
- public String toString() {
- return "simpleJsonpath[" + path + "]";
- }
- };
- }
-
- /**
- * Expression adapter for OGNL expression from Message Header or Exchange property
- */
- public static class KeyedOgnlExpressionAdapter extends ExpressionAdapter {
- private final String ognl;
- private final String toStringValue;
- private final KeyedEntityRetrievalStrategy keyedEntityRetrievalStrategy;
- private String key;
- private final String method;
- private Expression keyExpression;
- private Expression ognlExpression;
- private Language beanLanguage;
-
- public KeyedOgnlExpressionAdapter(String ognl, String toStringValue,
- KeyedEntityRetrievalStrategy keyedEntityRetrievalStrategy) {
- this.ognl = ognl;
- this.toStringValue = toStringValue;
- this.keyedEntityRetrievalStrategy = keyedEntityRetrievalStrategy;
-
- // Split ognl except when this is not a Map, Array
- // and we would like to keep the dots within the key name
- List<String> methods = OgnlHelper.splitOgnl(ognl);
-
- key = methods.get(0);
- String keySuffix = "";
- // if ognl starts with a key inside brackets (eg: [foo.bar])
- // remove starting and ending brackets from key
- if (key.startsWith("[") && key.endsWith("]")) {
- key = StringHelper.removeLeadingAndEndingQuotes(key.substring(1, key.length() - 1));
- keySuffix = StringHelper.after(methods.get(0), key);
- }
- // remove any OGNL operators so we got the pure key name
- key = OgnlHelper.removeOperators(key);
- // and this may be the last remainder method to try as OGNL if there are no exchange properties with those key names
- method = StringHelper.after(ognl, key + keySuffix);
- }
-
- @Override
- public void init(CamelContext context) {
- beanLanguage = context.resolveLanguage("bean");
- ognlExpression = ExpressionBuilder.simpleExpression(ognl);
- ognlExpression.init(context);
- // key must be lazy eval as it only used in special situations
- }
-
- @Override
- public Object evaluate(Exchange exchange) {
- // try with full name first
- Object property = keyedEntityRetrievalStrategy.getKeyedEntity(exchange, ognlExpression);
- if (property != null) {
- return property;
- }
-
- // key must be lazy eval as it only used in special situations
- if (keyExpression == null) {
- keyExpression = ExpressionBuilder.simpleExpression(key);
- keyExpression.init(exchange.getContext());
- }
-
- property = keyedEntityRetrievalStrategy.getKeyedEntity(exchange, keyExpression);
- if (property == null) {
- return null;
- }
- if (method != null) {
- Expression exp = beanLanguage.createExpression(null, new Object[] { null, property, method });
- exp.init(exchange.getContext());
- return exp.evaluate(exchange, Object.class);
- } else {
- return property;
- }
- }
-
- @Override
- public String toString() {
- return toStringValue;
- }
-
- /**
- * Strategy to retrieve the value based on the key
- */
- public interface KeyedEntityRetrievalStrategy {
- Object getKeyedEntity(Exchange exchange, Expression key);
- }
- }
-}
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/StringExpressionBuilder.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/StringExpressionBuilder.java
new file mode 100644
index 000000000000..f2b29d8a7f88
--- /dev/null
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/StringExpressionBuilder.java
@@ -0,0 +1,804 @@
+/*
+ * 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;
+
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+import org.apache.camel.Expression;
+import org.apache.camel.StreamCache;
+import org.apache.camel.support.ExpressionAdapter;
+import org.apache.camel.support.builder.ExpressionBuilder;
+import org.apache.camel.support.builder.PredicateBuilder;
+import org.apache.camel.util.IOHelper;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.StringHelper;
+import org.apache.camel.util.StringQuoteHelper;
+
+/**
+ * Expression builder for string-manipulation functions used by the simple language.
+ */
+public final class StringExpressionBuilder {
+
+ private StringExpressionBuilder() {
+ }
+
+ /**
+ * Safe quotes the given expressions (uses message body if expression is null) if necessary.
+ */
+ public static Expression safeQuoteExpression(final String expression) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+
+ @Override
+ public void init(CamelContext context) {
+ if (expression != null) {
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ }
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ Object value;
+ if (exp != null) {
+ value = exp.evaluate(exchange, Object.class);
+ } else {
+ value = exchange.getMessage().getBody(Object.class);
+ }
+ if (value != null) {
+ String type = kindOfType(value);
+ if ("string".equals(type) || "array".equals(type) || "object".equals(type)) {
+ String body = exchange.getContext().getTypeConverter().tryConvertTo(String.class, exchange, value);
+ body = StringHelper.removeLeadingAndEndingQuotes(body);
+ value = StringQuoteHelper.doubleQuote(body);
+ }
+ }
+ return value;
+ }
+
+ private String kindOfType(Object value) {
+ Class<?> type = value.getClass();
+ if (ObjectHelper.isNumericType(type)) {
+ return "number";
+ } else if (boolean.class == type || Boolean.class == type) {
+ return "boolean";
+ } else if (value instanceof CharSequence) {
+ return "string";
+ } else if (ObjectHelper.isPrimitiveArrayType(type) || value instanceof Collection
+ || value instanceof Map<?, ?>) {
+ return "array";
+ } else {
+ return "object";
+ }
+ }
+
+ @Override
+ public String toString() {
+ if (expression != null) {
+ return "safeQuote(" + expression + ")";
+ } else {
+ return "safeQuote()";
+ }
+ }
+ };
+ }
+
+ /**
+ * Double quotes the given expressions (uses message body if expression is null)
+ */
+ public static Expression quoteExpression(final String expression) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+
+ @Override
+ public void init(CamelContext context) {
+ if (expression != null) {
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ }
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ String value;
+ if (exp != null) {
+ value = exp.evaluate(exchange, String.class);
+ } else {
+ value = exchange.getMessage().getBody(String.class);
+ }
+ if (value != null && !StringHelper.isDoubleQuoted(value)) {
+ value = StringHelper.removeLeadingAndEndingQuotes(value);
+ value = StringQuoteHelper.doubleQuote(value);
+ }
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ if (expression != null) {
+ return "quote(" + expression + ")";
+ } else {
+ return "quote()";
+ }
+ }
+ };
+ }
+
+ /**
+ * Un quotes the given expressions (uses message body if expression is null)
+ */
+ public static Expression unquoteExpression(final String expression) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+
+ @Override
+ public void init(CamelContext context) {
+ if (expression != null) {
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ }
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ String value;
+ if (exp != null) {
+ value = exp.evaluate(exchange, String.class);
+ } else {
+ value = exchange.getMessage().getBody(String.class);
+ }
+ if (value != null) {
+ value = StringHelper.removeLeadingAndEndingQuotes(value);
+ }
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ if (expression != null) {
+ return "unquote(" + expression + ")";
+ } else {
+ return "unquote()";
+ }
+ }
+ };
+ }
+
+ /**
+ * Trims the given expressions (uses message body if expression is null)
+ */
+ public static Expression trimExpression(final String expression) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+
+ @Override
+ public void init(CamelContext context) {
+ if (expression != null) {
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ }
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ String value;
+ if (exp != null) {
+ value = exp.evaluate(exchange, String.class);
+ } else {
+ value = exchange.getMessage().getBody(String.class);
+ }
+ if (value != null) {
+ value = value.trim();
+ }
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ if (expression != null) {
+ return "trim(" + expression + ")";
+ } else {
+ return "trim()";
+ }
+ }
+ };
+ }
+
+ /**
+ * Capitalizes the given expressions (uses message body if expression is null)
+ */
+ public static Expression capitalizeExpression(final String expression) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+
+ @Override
+ public void init(CamelContext context) {
+ if (expression != null) {
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ }
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ String value;
+ if (exp != null) {
+ value = exp.evaluate(exchange, String.class);
+ } else {
+ value = exchange.getMessage().getBody(String.class);
+ }
+ if (value != null) {
+ value = StringHelper.capitalizeAll(value);
+ }
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ if (expression != null) {
+ return "capitalize(" + expression + ")";
+ } else {
+ return "capitalize()";
+ }
+ }
+ };
+ }
+
+ /**
+ * Pad the expression
+ */
+ public static Expression padExpression(final String expression, final String length, final String separator) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+ private Expression len;
+
+ @Override
+ public void init(CamelContext context) {
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ len = context.resolveLanguage("simple").createExpression(length);
+ len.init(context);
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ String answer = exp.evaluate(exchange, String.class);
+ if (answer == null) {
+ return null;
+ }
+ Integer width = len.evaluate(exchange, Integer.class);
+ if (width == null) {
+ throw new IllegalArgumentException("pad length expression evaluated to null: " + length);
+ }
+ String sep = separator;
+ if (sep == null || sep.isEmpty()) {
+ sep = " ";
+ }
+
+ int max = Math.abs(width);
+ while (max > answer.length()) {
+ if (width > 0) {
+ answer = answer + sep;
+ } else {
+ answer = sep + answer;
+ }
+ }
+ return answer;
+ }
+
+ @Override
+ public String toString() {
+ return "pad(" + exp + "," + length + ")";
+ }
+ };
+ }
+
+ /**
+ * String concats the two expressions.
+ */
+ public static Expression concatExpression(final String right, final String left, String separator) {
+ return new ExpressionAdapter() {
+ private Expression exp1;
+ private Expression exp2;
+
+ @Override
+ public void init(CamelContext context) {
+ exp1 = context.resolveLanguage("simple").createExpression(right);
+ exp1.init(context);
+ exp2 = context.resolveLanguage("simple").createExpression(left);
+ exp2.init(context);
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ String value1 = exp1.evaluate(exchange, String.class);
+ String value2 = exp2.evaluate(exchange, String.class);
+ if (value1 != null && value2 != null) {
+ return value1 + (separator != null ? separator : "") + value2;
+ } else {
+ return value1 != null ? value1 : value2;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "concat(" + right + "," + left + ")";
+ }
+ };
+ }
+
+ /**
+ * Uppercases the given expressions (uses message body if expression is null)
+ */
+ public static Expression uppercaseExpression(final String expression) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+
+ @Override
+ public void init(CamelContext context) {
+ if (expression != null) {
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ }
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ String value;
+ if (exp != null) {
+ value = exp.evaluate(exchange, String.class);
+ } else {
+ value = exchange.getMessage().getBody(String.class);
+ }
+ if (value != null) {
+ value = value.toUpperCase(Locale.ENGLISH);
+ }
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ if (expression != null) {
+ return "uppercase(" + expression + ")";
+ } else {
+ return "uppercase()";
+ }
+ }
+ };
+ }
+
+ /**
+ * Lowercases the given expressions (uses message body if expression is null)
+ */
+ public static Expression lowercaseExpression(final String expression) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+
+ @Override
+ public void init(CamelContext context) {
+ if (expression != null) {
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ }
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ String value;
+ if (exp != null) {
+ value = exp.evaluate(exchange, String.class);
+ } else {
+ value = exchange.getMessage().getBody(String.class);
+ }
+ if (value != null) {
+ value = value.toLowerCase(Locale.ENGLISH);
+ }
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ if (expression != null) {
+ return "lowercase(" + expression + ")";
+ } else {
+ return "lowercase()";
+ }
+ }
+ };
+ }
+
+ /**
+ * Returns the size of the expression (number of elements in collection/map; otherwise 1)
+ */
+ public static Expression sizeExpression(final String expression) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+
+ @Override
+ public void init(CamelContext context) {
+ if (expression != null) {
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ }
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ Object body;
+ if (exp != null) {
+ body = exp.evaluate(exchange, Object.class);
+ } else {
+ body = exchange.getMessage().getBody();
+ }
+ if (body != null) {
+ if (body instanceof byte[] arr) {
+ return arr.length;
+ } else if (body instanceof char[] arr) {
+ return arr.length;
+ } else if (body instanceof int[] arr) {
+ return arr.length;
+ } else if (body instanceof long[] arr) {
+ return arr.length;
+ } else if (body instanceof double[] arr) {
+ return arr.length;
+ } else if (body instanceof String[] arr) {
+ return arr.length;
+ } else if (body instanceof Collection<?> c) {
+ return c.size();
+ } else if (body instanceof Map<?, ?> m) {
+ return m.size();
+ } else {
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ if (expression != null) {
+ return "size(" + expression + ")";
+ } else {
+ return "size()";
+ }
+ }
+ };
+ }
+
+ /**
+ * Returns the length of the expression (length of payload in bytes)
+ */
+ public static Expression lengthExpression(final String expression) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+
+ @Override
+ public void init(CamelContext context) {
+ if (expression != null) {
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ }
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ Object body;
+ if (exp != null) {
+ body = exp.evaluate(exchange, Object.class);
+ } else {
+ body = exchange.getMessage().getBody();
+ }
+ try {
+ if (body instanceof byte[] arr) {
+ return arr.length;
+ } else if (body instanceof char[] arr) {
+ return arr.length;
+ } else if (body instanceof int[] arr) {
+ return arr.length;
+ } else if (body instanceof long[] arr) {
+ return arr.length;
+ } else if (body instanceof double[] arr) {
+ return arr.length;
+ } else if (body instanceof String[] arr) {
+ return arr.length;
+ } else if (body instanceof StreamCache sc) {
+ return (int) sc.length();
+ } else {
+ // first read as stream
+ InputStream is = null;
+ try {
+ is = exchange.getContext().getTypeConverter().tryConvertTo(InputStream.class, exchange, body);
+ int len = 0;
+ while (is.read() != -1) {
+ len++;
+ }
+ return len;
+ } catch (Exception e) {
+ // ignore
+ } finally {
+ IOHelper.close(is);
+ }
+ // fallback to use string based
+ String data = exchange.getContext().getTypeConverter().tryConvertTo(String.class, exchange, body);
+ if (data != null) {
+ return data.length();
+ }
+ }
+ } finally {
+ if (body instanceof StreamCache streamCache) {
+ streamCache.reset();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ if (expression != null) {
+ return "length(" + expression + ")";
+ } else {
+ return "length()";
+ }
+ }
+ };
+ }
+
+ /**
+ * Normalizes the whitespaces in the given expressions (uses message body if expression is null)
+ */
+ public static Expression normalizeWhitespaceExpression(final String expression) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+
+ @Override
+ public void init(CamelContext context) {
+ if (expression != null) {
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ }
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ String value;
+ if (exp != null) {
+ value = exp.evaluate(exchange, String.class);
+ } else {
+ value = exchange.getMessage().getBody(String.class);
+ }
+ if (value != null) {
+ value = StringHelper.normalizeWhitespace(value);
+ }
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ if (expression != null) {
+ return "normalizeWhitespace(" + expression + ")";
+ } else {
+ return "normalizeWhitespace()";
+ }
+ }
+ };
+ }
+
+ /**
+ * Replaces string values from the expression
+ */
+ public static Expression replaceExpression(final String expression, final String from, final String to) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+
+ @Override
+ public void init(CamelContext context) {
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ exp = ExpressionBuilder.replaceAll(exp, from, to);
+ exp.init(context);
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ return exp.evaluate(exchange, Object.class);
+ }
+
+ @Override
+ public String toString() {
+ return "replace(" + expression + "," + from + "," + to + ")";
+ }
+ };
+ }
+
+ /**
+ * Substring string values from the expression
+ */
+ public static Expression substringExpression(final String expression, final String head, final String tail) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+ private Expression exp1;
+ private Expression exp2;
+
+ @Override
+ public void init(CamelContext context) {
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ exp1 = ExpressionBuilder.simpleExpression(head);
+ exp1.init(context);
+ exp2 = ExpressionBuilder.simpleExpression(tail);
+ exp2.init(context);
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ int num1 = exp1.evaluate(exchange, Integer.class);
+ int num2 = exp2.evaluate(exchange, Integer.class);
+ if (num1 < 0 && num2 == 0) {
+ // if there is only one value and its negative then we want to clip from tail
+ num2 = num1;
+ num1 = 0;
+ }
+ num1 = Math.abs(num1);
+ num2 = Math.abs(num2);
+ return ExpressionBuilder.substring(exp, num1, num2).evaluate(exchange, Object.class);
+ }
+
+ @Override
+ public String toString() {
+ return "substring(" + expression + "," + head + "," + tail + ")";
+ }
+ };
+ }
+
+ /**
+ * Returns the substring from the given expression that comes before
+ */
+ public static Expression substringBeforeExpression(final String expression, final String before) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+ private Expression expBefore;
+
+ @Override
+ public void init(CamelContext context) {
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ expBefore = ExpressionBuilder.simpleExpression(before);
+ expBefore.init(context);
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ String body = exp.evaluate(exchange, String.class);
+ if (body == null) {
+ return null;
+ }
+ String bef = expBefore.evaluate(exchange, String.class);
+ return StringHelper.before(body, bef);
+ }
+
+ @Override
+ public String toString() {
+ return "substringBefore(" + expression + "," + before + ")";
+ }
+ };
+ }
+
+ /**
+ * Returns the substring from the given expression that comes after
+ */
+ public static Expression substringAfterExpression(final String expression, final String after) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+ private Expression expAfter;
+
+ @Override
+ public void init(CamelContext context) {
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ expAfter = ExpressionBuilder.simpleExpression(after);
+ expAfter.init(context);
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ String body = exp.evaluate(exchange, String.class);
+ if (body == null) {
+ return null;
+ }
+ String aft = expAfter.evaluate(exchange, String.class);
+ return StringHelper.after(body, aft);
+ }
+
+ @Override
+ public String toString() {
+ return "substringAfter(" + expression + "," + after + ")";
+ }
+ };
+ }
+
+ /**
+ * Returns the substring from the given expression that are between after and before
+ */
+ public static Expression substringBetweenExpression(
+ final String expression, final String after,
+ final String before) {
+ return new ExpressionAdapter() {
+ private Expression exp;
+ private Expression expAfter;
+ private Expression expBefore;
+
+ @Override
+ public void init(CamelContext context) {
+ exp = context.resolveLanguage("simple").createExpression(expression);
+ exp.init(context);
+ expAfter = ExpressionBuilder.simpleExpression(after);
+ expAfter.init(context);
+ expBefore = ExpressionBuilder.simpleExpression(before);
+ expBefore.init(context);
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ String body = exp.evaluate(exchange, String.class);
+ if (body == null) {
+ return null;
+ }
+ String aft = expAfter.evaluate(exchange, String.class);
+ String bef = expBefore.evaluate(exchange, String.class);
+ return StringHelper.between(body, aft, bef);
+ }
+
+ @Override
+ public String toString() {
+ return "substringBetween(" + expression + "," + after + "," + before + ")";
+ }
+ };
+ }
+
+ /**
+ * Whether the expression matches the pattern
+ */
+ public static Expression containsExpression(final String expression, final String pattern) {
+ return new ExpressionAdapter() {
+ private Expression right;
+ private Expression left;
+
+ @Override
+ public void init(CamelContext context) {
+ left = context.resolveLanguage("simple").createExpression(expression);
+ left.init(context);
+ right = context.resolveLanguage("simple").createExpression(pattern);
+ right.init(context);
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ return PredicateBuilder.containsIgnoreCase(left, right).matches(exchange);
+ }
+
+ @Override
+ public String toString() {
+ return "contains(" + expression + "," + pattern + ")";
+ }
+ };
+ }
+}
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 5ee6064093e7..d7bcd6a14313 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
@@ -21,7 +21,7 @@ import java.util.Map;
import org.apache.camel.CamelContext;
import org.apache.camel.Expression;
import org.apache.camel.language.simple.BaseSimpleParser;
-import org.apache.camel.language.simple.SimpleExpressionBuilder;
+import org.apache.camel.language.simple.FileExpressionBuilder;
import org.apache.camel.language.simple.SimpleFunctionDispatcher;
import org.apache.camel.language.simple.SimpleFunctionHelper;
import org.apache.camel.language.simple.SimplePredicateParser;
@@ -157,33 +157,33 @@ public class SimpleFunctionExpression extends LiteralExpression {
private Expression createSimpleFileExpression(String remainder, boolean strict) {
if (ObjectHelper.equal(remainder, "name")) {
- return SimpleExpressionBuilder.fileNameExpression();
+ return FileExpressionBuilder.fileNameExpression();
} else if (ObjectHelper.equal(remainder, "name.noext")) {
- return SimpleExpressionBuilder.fileNameNoExtensionExpression();
+ return FileExpressionBuilder.fileNameNoExtensionExpression();
} else if (ObjectHelper.equal(remainder, "name.noext.single")) {
- return SimpleExpressionBuilder.fileNameNoExtensionSingleExpression();
+ return FileExpressionBuilder.fileNameNoExtensionSingleExpression();
} else if (ObjectHelper.equal(remainder, "name.ext") || ObjectHelper.equal(remainder, "ext")) {
- return SimpleExpressionBuilder.fileExtensionExpression();
+ return FileExpressionBuilder.fileExtensionExpression();
} else if (ObjectHelper.equal(remainder, "name.ext.single")) {
- return SimpleExpressionBuilder.fileExtensionSingleExpression();
+ return FileExpressionBuilder.fileExtensionSingleExpression();
} else if (ObjectHelper.equal(remainder, "onlyname")) {
- return SimpleExpressionBuilder.fileOnlyNameExpression();
+ return FileExpressionBuilder.fileOnlyNameExpression();
} else if (ObjectHelper.equal(remainder, "onlyname.noext")) {
- return SimpleExpressionBuilder.fileOnlyNameNoExtensionExpression();
+ return FileExpressionBuilder.fileOnlyNameNoExtensionExpression();
} else if (ObjectHelper.equal(remainder, "onlyname.noext.single")) {
- return SimpleExpressionBuilder.fileOnlyNameNoExtensionSingleExpression();
+ return FileExpressionBuilder.fileOnlyNameNoExtensionSingleExpression();
} else if (ObjectHelper.equal(remainder, "parent")) {
- return SimpleExpressionBuilder.fileParentExpression();
+ return FileExpressionBuilder.fileParentExpression();
} else if (ObjectHelper.equal(remainder, "path")) {
- return SimpleExpressionBuilder.filePathExpression();
+ return FileExpressionBuilder.filePathExpression();
} else if (ObjectHelper.equal(remainder, "absolute")) {
- return SimpleExpressionBuilder.fileAbsoluteExpression();
+ return FileExpressionBuilder.fileAbsoluteExpression();
} else if (ObjectHelper.equal(remainder, "absolute.path")) {
- return SimpleExpressionBuilder.fileAbsolutePathExpression();
+ return FileExpressionBuilder.fileAbsolutePathExpression();
} else if (ObjectHelper.equal(remainder, "length") || ObjectHelper.equal(remainder, "size")) {
- return SimpleExpressionBuilder.fileSizeExpression();
+ return FileExpressionBuilder.fileSizeExpression();
} else if (ObjectHelper.equal(remainder, "modified")) {
- return SimpleExpressionBuilder.fileLastModifiedExpression();
+ return FileExpressionBuilder.fileLastModifiedExpression();
}
if (strict) {
throw new SimpleParserException("Unknown file language syntax: " + remainder, token.getIndex());
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
index 76b59ee26d20..4c0b14bde767 100644
--- 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
@@ -20,7 +20,7 @@ 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.OgnlExpressionBuilder;
import org.apache.camel.language.simple.types.SimpleParserException;
import org.apache.camel.spi.SimpleLanguageFunctionFactory;
import org.apache.camel.support.builder.ExpressionBuilder;
@@ -71,7 +71,7 @@ public final class BodyFunctionFactory implements SimpleLanguageFunctionFactory
if (invalid) {
throw new SimpleParserException("Valid syntax: ${bodyAs(type).OGNL} was: " + function, index);
}
- return SimpleExpressionBuilder.bodyOgnlExpression(type, remainder);
+ return OgnlExpressionBuilder.bodyOgnlExpression(type, remainder);
} else {
return ExpressionBuilder.bodyExpression(type);
}
@@ -92,9 +92,9 @@ public final class BodyFunctionFactory implements SimpleLanguageFunctionFactory
throw new SimpleParserException(
"Valid syntax: ${mandatoryBodyAs(type).OGNL} was: " + function, index);
}
- return SimpleExpressionBuilder.mandatoryBodyOgnlExpression(type, remainder);
+ return OgnlExpressionBuilder.mandatoryBodyOgnlExpression(type, remainder);
} else {
- return SimpleExpressionBuilder.mandatoryBodyExpression(type);
+ return OgnlExpressionBuilder.mandatoryBodyExpression(type);
}
}
@@ -109,7 +109,7 @@ public final class BodyFunctionFactory implements SimpleLanguageFunctionFactory
if (invalid) {
throw new SimpleParserException("Valid syntax: ${body.OGNL} was: " + function, index);
}
- return SimpleExpressionBuilder.bodyOgnlExpression(remainder);
+ return OgnlExpressionBuilder.bodyOgnlExpression(remainder);
}
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 e469426bbe61..7600703539c5 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
@@ -18,7 +18,7 @@ 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.MiscExpressionBuilder;
import org.apache.camel.language.simple.types.SimpleParserException;
import org.apache.camel.spi.SimpleLanguageFunctionFactory;
import org.apache.camel.util.ObjectHelper;
@@ -41,7 +41,7 @@ public final class CollateFunctionFactory implements SimpleLanguageFunctionFacto
if (values == null || ObjectHelper.isEmpty(values)) {
throw new SimpleParserException("Valid syntax: ${collate(group)} was: " + function, index);
}
- return SimpleExpressionBuilder.collateExpression("${body}", values.trim());
+ return MiscExpressionBuilder.collateExpression("${body}", values.trim());
}
@Override
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/CollectionFunctionFactory.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/CollectionFunctionFactory.java
index f6a381413784..0f979e86e86b 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/CollectionFunctionFactory.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/CollectionFunctionFactory.java
@@ -22,7 +22,7 @@ import java.util.stream.Collectors;
import org.apache.camel.CamelContext;
import org.apache.camel.Expression;
-import org.apache.camel.language.simple.SimpleExpressionBuilder;
+import org.apache.camel.language.simple.CollectionExpressionBuilder;
import org.apache.camel.language.simple.types.SimpleParserException;
import org.apache.camel.spi.SimpleLanguageFunctionFactory;
import org.apache.camel.util.ObjectHelper;
@@ -67,7 +67,7 @@ public final class CollectionFunctionFactory implements SimpleLanguageFunctionFa
exp = tokens[1];
}
type = StringHelper.removeQuotes(type);
- return SimpleExpressionBuilder.setHeaderExpression(name, type, exp);
+ return CollectionExpressionBuilder.setHeaderExpression(name, type, exp);
}
remainder = ifStartsWithReturnRemainder("setVariable(", function);
@@ -92,7 +92,7 @@ public final class CollectionFunctionFactory implements SimpleLanguageFunctionFa
exp = tokens[1];
}
type = StringHelper.removeQuotes(type);
- return SimpleExpressionBuilder.setVariableExpression(name, type, exp);
+ return CollectionExpressionBuilder.setVariableExpression(name, type, exp);
}
remainder = ifStartsWithReturnRemainder("range(", function);
@@ -108,9 +108,9 @@ public final class CollectionFunctionFactory implements SimpleLanguageFunctionFa
throw new SimpleParserException(
"Valid syntax: ${range(min,max)} or ${range(max)} was: " + function, index);
}
- return SimpleExpressionBuilder.rangeExpression(tokens[0].trim(), tokens[1].trim());
+ return CollectionExpressionBuilder.rangeExpression(tokens[0].trim(), tokens[1].trim());
} else {
- return SimpleExpressionBuilder.rangeExpression("1", values.trim());
+ return CollectionExpressionBuilder.rangeExpression("1", values.trim());
}
}
@@ -123,7 +123,7 @@ public final class CollectionFunctionFactory implements SimpleLanguageFunctionFa
} else {
tokens = new String[] { "${body}" };
}
- return SimpleExpressionBuilder.distinctExpression(tokens);
+ return CollectionExpressionBuilder.distinctExpression(tokens);
}
remainder = ifStartsWithReturnRemainder("reverse(", function);
@@ -135,7 +135,7 @@ public final class CollectionFunctionFactory implements SimpleLanguageFunctionFa
} else {
tokens = new String[] { "${body}" };
}
- return SimpleExpressionBuilder.reverseExpression(tokens);
+ return CollectionExpressionBuilder.reverseExpression(tokens);
}
remainder = ifStartsWithReturnRemainder("shuffle(", function);
@@ -147,7 +147,7 @@ public final class CollectionFunctionFactory implements SimpleLanguageFunctionFa
} else {
tokens = new String[] { "${body}" };
}
- return SimpleExpressionBuilder.shuffleExpression(tokens);
+ return CollectionExpressionBuilder.shuffleExpression(tokens);
}
remainder = ifStartsWithReturnRemainder("split(", function);
@@ -170,7 +170,7 @@ public final class CollectionFunctionFactory implements SimpleLanguageFunctionFa
} else if ("\n".equals(values)) {
separator = values;
}
- return SimpleExpressionBuilder.splitStringExpression(exp, separator);
+ return CollectionExpressionBuilder.splitStringExpression(exp, separator);
}
remainder = ifStartsWithReturnRemainder("sort(", function);
@@ -191,7 +191,7 @@ public final class CollectionFunctionFactory implements SimpleLanguageFunctionFa
reverse = Boolean.parseBoolean(tokens[0]);
}
}
- return SimpleExpressionBuilder.sortExpression(exp, reverse);
+ return CollectionExpressionBuilder.sortExpression(exp, reverse);
}
remainder = ifStartsWithReturnRemainder("forEach(", function);
@@ -208,7 +208,7 @@ public final class CollectionFunctionFactory implements SimpleLanguageFunctionFa
}
String exp1 = tokens[0];
String exp2 = Arrays.stream(tokens).skip(1).collect(Collectors.joining(","));
- return SimpleExpressionBuilder.forEachExpression(exp1, exp2);
+ return CollectionExpressionBuilder.forEachExpression(exp1, exp2);
}
remainder = ifStartsWithReturnRemainder("filter(", function);
@@ -225,7 +225,7 @@ public final class CollectionFunctionFactory implements SimpleLanguageFunctionFa
}
String exp1 = tokens[0];
String exp2 = Arrays.stream(tokens).skip(1).collect(Collectors.joining(","));
- return SimpleExpressionBuilder.filterExpression(exp1, exp2);
+ return CollectionExpressionBuilder.filterExpression(exp1, exp2);
}
remainder = ifStartsWithReturnRemainder("listAdd(", function);
@@ -243,7 +243,7 @@ public final class CollectionFunctionFactory implements SimpleLanguageFunctionFa
exp1 = tokens[0];
}
String exp2 = Arrays.stream(tokens).skip(skip).collect(Collectors.joining(","));
- return SimpleExpressionBuilder.listAddExpression(exp1, exp2);
+ return CollectionExpressionBuilder.listAddExpression(exp1, exp2);
}
remainder = ifStartsWithReturnRemainder("listRemove(", function);
@@ -261,7 +261,7 @@ public final class CollectionFunctionFactory implements SimpleLanguageFunctionFa
exp1 = tokens[0];
}
String exp2 = Arrays.stream(tokens).skip(skip).collect(Collectors.joining(","));
- return SimpleExpressionBuilder.listRemoveExpression(exp1, exp2);
+ return CollectionExpressionBuilder.listRemoveExpression(exp1, exp2);
}
remainder = ifStartsWithReturnRemainder("mapAdd(", function);
@@ -287,7 +287,7 @@ public final class CollectionFunctionFactory implements SimpleLanguageFunctionFa
"Valid syntax: ${mapAdd(key,exp)} or ${mapAdd(exp,key,exp)} was: " + function, index);
}
String exp2 = Arrays.stream(tokens).skip(skip).collect(Collectors.joining(","));
- return SimpleExpressionBuilder.mapAddExpression(exp1, key, exp2);
+ return CollectionExpressionBuilder.mapAddExpression(exp1, key, exp2);
}
remainder = ifStartsWithReturnRemainder("mapRemove(", function);
@@ -310,7 +310,7 @@ public final class CollectionFunctionFactory implements SimpleLanguageFunctionFa
} else {
key = tokens[0];
}
- return SimpleExpressionBuilder.mapRemoveExpression(exp, key);
+ return CollectionExpressionBuilder.mapRemoveExpression(exp, key);
}
remainder = ifStartsWithReturnRemainder("list(", function);
@@ -320,7 +320,7 @@ public final class CollectionFunctionFactory implements SimpleLanguageFunctionFa
if (ObjectHelper.isNotEmpty(values)) {
tokens = StringQuoteHelper.splitSafeQuote(values, ',', true, false);
}
- return SimpleExpressionBuilder.listExpression(tokens);
+ return CollectionExpressionBuilder.listExpression(tokens);
}
remainder = ifStartsWithReturnRemainder("map(", function);
@@ -334,7 +334,7 @@ public final class CollectionFunctionFactory implements SimpleLanguageFunctionFa
throw new SimpleParserException(
"Map function must have an even number of values, was: " + tokens.length + " values.", index);
}
- return SimpleExpressionBuilder.mapExpression(tokens);
+ return CollectionExpressionBuilder.mapExpression(tokens);
}
return null;
@@ -485,7 +485,7 @@ public final class CollectionFunctionFactory implements SimpleLanguageFunctionFa
}
return "rangeList(exchange, " + before + ", " + after + ")";
} else {
- return "rangeList(exchange, 0, " + values.trim() + ")";
+ return "rangeList(exchange, 1, " + values.trim() + ")";
}
}
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/CustomFunctionFactory.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/CustomFunctionFactory.java
index a98f278103a1..856d8a35f92e 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/CustomFunctionFactory.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/CustomFunctionFactory.java
@@ -18,7 +18,7 @@ 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.MiscExpressionBuilder;
import org.apache.camel.language.simple.types.SimpleParserException;
import org.apache.camel.spi.SimpleLanguageFunctionFactory;
import org.apache.camel.util.ObjectHelper;
@@ -61,7 +61,7 @@ public final class CustomFunctionFactory implements SimpleLanguageFunctionFactor
if (param == null) {
param = "${body}";
}
- return SimpleExpressionBuilder.customFunction(key, param);
+ return MiscExpressionBuilder.customFunction(key, param);
}
@Override
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/DateFunctionFactory.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/DateFunctionFactory.java
index 25a41f131a67..22157c15b21b 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/DateFunctionFactory.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/DateFunctionFactory.java
@@ -18,7 +18,7 @@ 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.DateExpressionBuilder;
import org.apache.camel.language.simple.types.SimpleParserException;
import org.apache.camel.spi.SimpleLanguageFunctionFactory;
@@ -39,16 +39,16 @@ public final class DateFunctionFactory implements SimpleLanguageFunctionFactory
throw new SimpleParserException(
"Valid syntax: ${date-with-timezone:command:timezone:pattern} was: " + function, index);
}
- return SimpleExpressionBuilder.dateExpression(parts[0], parts[1], parts[2]);
+ return DateExpressionBuilder.dateExpression(parts[0], parts[1], parts[2]);
}
remainder = ifStartsWithReturnRemainder("date:", function);
if (remainder != null) {
String[] parts = remainder.split(":", 2);
if (parts.length == 1) {
- return SimpleExpressionBuilder.dateExpression(parts[0]);
+ return DateExpressionBuilder.dateExpression(parts[0]);
} else {
- return SimpleExpressionBuilder.dateExpression(parts[0], parts[1]);
+ return DateExpressionBuilder.dateExpression(parts[0], parts[1]);
}
}
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/DirectFunctionFactory.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/DirectFunctionFactory.java
index 54dd9290de12..fc2f0c044162 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/DirectFunctionFactory.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/DirectFunctionFactory.java
@@ -18,7 +18,7 @@ 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.MiscExpressionBuilder;
import org.apache.camel.spi.SimpleLanguageFunctionFactory;
import org.apache.camel.support.builder.ExpressionBuilder;
import org.apache.camel.util.ObjectHelper;
@@ -72,7 +72,7 @@ public final class DirectFunctionFactory implements SimpleLanguageFunctionFactor
} else if (ObjectHelper.equal(function, "stepId")) {
return ExpressionBuilder.stepIdExpression();
} else if (ObjectHelper.equal(function, "null")) {
- return SimpleExpressionBuilder.nullExpression();
+ return MiscExpressionBuilder.nullExpression();
}
return null;
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/ExchangeFunctionFactory.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/ExchangeFunctionFactory.java
index e6d9f2f88f40..e12cc05433d5 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/ExchangeFunctionFactory.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/ExchangeFunctionFactory.java
@@ -18,7 +18,7 @@ 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.OgnlExpressionBuilder;
import org.apache.camel.language.simple.types.SimpleParserException;
import org.apache.camel.spi.SimpleLanguageFunctionFactory;
import org.apache.camel.support.builder.ExpressionBuilder;
@@ -47,7 +47,7 @@ public final class ExchangeFunctionFactory implements SimpleLanguageFunctionFact
if (invalid) {
throw new SimpleParserException("Valid syntax: ${camelContext.OGNL} was: " + function, index);
}
- return SimpleExpressionBuilder.camelContextOgnlExpression(remainder);
+ return OgnlExpressionBuilder.camelContextOgnlExpression(remainder);
}
// Exception OGNL — exchangeProperty/exchange checked separately, no prefix clash here
@@ -57,7 +57,7 @@ public final class ExchangeFunctionFactory implements SimpleLanguageFunctionFact
if (invalid) {
throw new SimpleParserException("Valid syntax: ${exception.OGNL} was: " + function, index);
}
- return SimpleExpressionBuilder.exchangeExceptionOgnlExpression(remainder);
+ return OgnlExpressionBuilder.exchangeExceptionOgnlExpression(remainder);
}
// exchangeProperty must be checked before exchange to avoid prefix clash
@@ -78,7 +78,7 @@ public final class ExchangeFunctionFactory implements SimpleLanguageFunctionFact
}
if (OgnlHelper.isValidOgnlExpression(remainder)) {
- return SimpleExpressionBuilder.propertyOgnlExpression(remainder);
+ return OgnlExpressionBuilder.propertyOgnlExpression(remainder);
} else {
return ExpressionBuilder.exchangePropertyExpression(remainder);
}
@@ -91,7 +91,7 @@ public final class ExchangeFunctionFactory implements SimpleLanguageFunctionFact
if (invalid) {
throw new SimpleParserException("Valid syntax: ${exchange.OGNL} was: " + function, index);
}
- return SimpleExpressionBuilder.exchangeOgnlExpression(remainder);
+ return OgnlExpressionBuilder.exchangeOgnlExpression(remainder);
}
return null;
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
index f1068ea009c1..661d97ca5719 100644
--- 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
@@ -20,7 +20,7 @@ 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.OgnlExpressionBuilder;
import org.apache.camel.language.simple.types.SimpleParserException;
import org.apache.camel.spi.SimpleLanguageFunctionFactory;
import org.apache.camel.support.builder.ExpressionBuilder;
@@ -85,7 +85,7 @@ public final class HeaderFunctionFactory implements SimpleLanguageFunctionFactor
}
if (OgnlHelper.isValidOgnlExpression(key)) {
- return SimpleExpressionBuilder.headersOgnlExpression(key);
+ return OgnlExpressionBuilder.headersOgnlExpression(key);
} else {
return ExpressionBuilder.headerExpression(key);
}
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 7032da44246d..865ad431f5f9 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
@@ -18,7 +18,7 @@ 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.CollectionExpressionBuilder;
import org.apache.camel.language.simple.SimpleFunctionHelper;
import org.apache.camel.language.simple.types.SimpleParserException;
import org.apache.camel.spi.SimpleLanguageFunctionFactory;
@@ -61,7 +61,7 @@ public final class JoinFunctionFactory implements SimpleLanguageFunctionFactory
separator = tokens[0];
}
}
- return SimpleExpressionBuilder.joinExpression(exp, separator, prefix);
+ return CollectionExpressionBuilder.joinExpression(exp, separator, prefix);
}
@Override
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/MathFunctionFactory.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/MathFunctionFactory.java
index bbd3e1e97f4f..0aa72c65ccf4 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/MathFunctionFactory.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/MathFunctionFactory.java
@@ -20,7 +20,7 @@ import java.util.StringJoiner;
import org.apache.camel.CamelContext;
import org.apache.camel.Expression;
-import org.apache.camel.language.simple.SimpleExpressionBuilder;
+import org.apache.camel.language.simple.MathExpressionBuilder;
import org.apache.camel.language.simple.types.SimpleParserException;
import org.apache.camel.spi.SimpleLanguageFunctionFactory;
import org.apache.camel.util.ObjectHelper;
@@ -43,36 +43,36 @@ public final class MathFunctionFactory implements SimpleLanguageFunctionFactory
remainder = ifStartsWithReturnRemainder("abs(", function);
if (remainder != null) {
String value = StringHelper.beforeLast(remainder, ")");
- return SimpleExpressionBuilder
- .absExpression(ObjectHelper.isNotEmpty(value) ? StringHelper.removeQuotes(value) : null);
+ return MathExpressionBuilder.absExpression(
+ ObjectHelper.isNotEmpty(value) ? StringHelper.removeQuotes(value) : null);
}
remainder = ifStartsWithReturnRemainder("floor(", function);
if (remainder != null) {
String value = StringHelper.beforeLast(remainder, ")");
- return SimpleExpressionBuilder.floorExpression(
+ return MathExpressionBuilder.floorExpression(
ObjectHelper.isNotEmpty(value) ? StringHelper.removeQuotes(value) : null);
}
remainder = ifStartsWithReturnRemainder("ceil(", function);
if (remainder != null) {
String value = StringHelper.beforeLast(remainder, ")");
- return SimpleExpressionBuilder.ceilExpression(
+ return MathExpressionBuilder.ceilExpression(
ObjectHelper.isNotEmpty(value) ? StringHelper.removeQuotes(value) : null);
}
remainder = ifStartsWithReturnRemainder("sum(", function);
if (remainder != null) {
- return SimpleExpressionBuilder.sumExpression(parseVariadicTokens(remainder));
+ return MathExpressionBuilder.sumExpression(parseVariadicTokens(remainder));
}
remainder = ifStartsWithReturnRemainder("max(", function);
if (remainder != null) {
- return SimpleExpressionBuilder.maxExpression(parseVariadicTokens(remainder));
+ return MathExpressionBuilder.maxExpression(parseVariadicTokens(remainder));
}
remainder = ifStartsWithReturnRemainder("min(", function);
if (remainder != null) {
- return SimpleExpressionBuilder.minExpression(parseVariadicTokens(remainder));
+ return MathExpressionBuilder.minExpression(parseVariadicTokens(remainder));
}
remainder = ifStartsWithReturnRemainder("average(", function);
if (remainder != null) {
- return SimpleExpressionBuilder.averageExpression(parseVariadicTokens(remainder));
+ return MathExpressionBuilder.averageExpression(parseVariadicTokens(remainder));
}
return null;
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/MessageFunctionFactory.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/MessageFunctionFactory.java
index 9d6c0ff6e7cd..1afa17c3088e 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/MessageFunctionFactory.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/MessageFunctionFactory.java
@@ -18,7 +18,7 @@ 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.OgnlExpressionBuilder;
import org.apache.camel.language.simple.types.SimpleParserException;
import org.apache.camel.spi.SimpleLanguageFunctionFactory;
import org.apache.camel.support.builder.ExpressionBuilder;
@@ -48,7 +48,7 @@ public final class MessageFunctionFactory implements SimpleLanguageFunctionFacto
if (invalid) {
throw new SimpleParserException("Valid syntax: ${messageAs(type).OGNL} was: " + function, index);
}
- return SimpleExpressionBuilder.messageOgnlExpression(type, remainder);
+ return OgnlExpressionBuilder.messageOgnlExpression(type, remainder);
} else {
return ExpressionBuilder.messageExpression(type);
}
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/MiscFunctionFactory.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/MiscFunctionFactory.java
index a173f147621e..ba72ad68ca3e 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/MiscFunctionFactory.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/MiscFunctionFactory.java
@@ -18,7 +18,7 @@ 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.MiscExpressionBuilder;
import org.apache.camel.language.simple.types.SimpleParserException;
import org.apache.camel.spi.SimpleLanguageFunctionFactory;
import org.apache.camel.util.ObjectHelper;
@@ -54,7 +54,7 @@ public final class MiscFunctionFactory implements SimpleLanguageFunctionFactory
if (ObjectHelper.isNotEmpty(value)) {
exp = StringHelper.removeQuotes(value);
}
- return SimpleExpressionBuilder.isEmptyExpression(exp);
+ return MiscExpressionBuilder.isEmptyExpression(exp);
}
remainder = ifStartsWithReturnRemainder("isAlpha(", function);
@@ -64,7 +64,7 @@ public final class MiscFunctionFactory implements SimpleLanguageFunctionFactory
if (ObjectHelper.isNotEmpty(value)) {
exp = StringHelper.removeQuotes(value);
}
- return SimpleExpressionBuilder.isAlphaExpression(exp);
+ return MiscExpressionBuilder.isAlphaExpression(exp);
}
remainder = ifStartsWithReturnRemainder("isAlphaNumeric(", function);
@@ -74,7 +74,7 @@ public final class MiscFunctionFactory implements SimpleLanguageFunctionFactory
if (ObjectHelper.isNotEmpty(value)) {
exp = StringHelper.removeQuotes(value);
}
- return SimpleExpressionBuilder.isAlphaNumericExpression(exp);
+ return MiscExpressionBuilder.isAlphaNumericExpression(exp);
}
remainder = ifStartsWithReturnRemainder("isNumeric(", function);
@@ -84,7 +84,7 @@ public final class MiscFunctionFactory implements SimpleLanguageFunctionFactory
if (ObjectHelper.isNotEmpty(value)) {
exp = StringHelper.removeQuotes(value);
}
- return SimpleExpressionBuilder.isNumericExpression(exp);
+ return MiscExpressionBuilder.isNumericExpression(exp);
}
remainder = ifStartsWithReturnRemainder("not(", function);
@@ -94,7 +94,7 @@ public final class MiscFunctionFactory implements SimpleLanguageFunctionFactory
if (ObjectHelper.isNotEmpty(value)) {
exp = value;
}
- return SimpleExpressionBuilder.isNotPredicate(exp);
+ return MiscExpressionBuilder.isNotPredicate(exp);
}
remainder = ifStartsWithReturnRemainder("kindOfType(", function);
@@ -104,7 +104,7 @@ public final class MiscFunctionFactory implements SimpleLanguageFunctionFactory
if (ObjectHelper.isNotEmpty(value)) {
exp = StringHelper.removeQuotes(value);
}
- return SimpleExpressionBuilder.kindOfTypeExpression(exp);
+ return MiscExpressionBuilder.kindOfTypeExpression(exp);
}
remainder = ifStartsWithReturnRemainder("throwException(", function);
@@ -127,7 +127,7 @@ public final class MiscFunctionFactory implements SimpleLanguageFunctionFactory
} else {
msg = StringHelper.removeQuotes(values.trim());
}
- return SimpleExpressionBuilder.throwExceptionExpression(msg, type);
+ return MiscExpressionBuilder.throwExceptionExpression(msg, type);
}
remainder = ifStartsWithReturnRemainder("assert(", function);
@@ -140,7 +140,7 @@ public final class MiscFunctionFactory implements SimpleLanguageFunctionFactory
if (tokens.length != 2) {
throw new SimpleParserException("Valid syntax: ${assert(exp,msg)} was: " + function, index);
}
- return SimpleExpressionBuilder.assertExpression(tokens[0], StringHelper.removeQuotes(tokens[1]));
+ return MiscExpressionBuilder.assertExpression(tokens[0], StringHelper.removeQuotes(tokens[1]));
}
remainder = ifStartsWithReturnRemainder("convertTo(", function);
@@ -170,9 +170,9 @@ public final class MiscFunctionFactory implements SimpleLanguageFunctionFactory
throw new SimpleParserException(
"Valid syntax: ${convertTo(type).OGNL} or ${convertTo(exp,type).OGNL} was: " + function, index);
}
- return SimpleExpressionBuilder.convertToOgnlExpression(exp, type, remainder);
+ return MiscExpressionBuilder.convertToOgnlExpression(exp, type, remainder);
} else {
- return SimpleExpressionBuilder.convertToExpression(exp, type);
+ return MiscExpressionBuilder.convertToExpression(exp, type);
}
}
@@ -185,17 +185,17 @@ public final class MiscFunctionFactory implements SimpleLanguageFunctionFactory
} else {
detailed = Boolean.parseBoolean(values);
}
- return SimpleExpressionBuilder.messageHistoryExpression(detailed);
+ return MiscExpressionBuilder.messageHistoryExpression(detailed);
} else if (ObjectHelper.equal(function, "messageHistory")) {
- return SimpleExpressionBuilder.messageHistoryExpression(true);
+ return MiscExpressionBuilder.messageHistoryExpression(true);
}
remainder = ifStartsWithReturnRemainder("uuid", function);
if (remainder != null) {
String values = StringHelper.between(remainder, "(", ")");
- return SimpleExpressionBuilder.uuidExpression(values);
+ return MiscExpressionBuilder.uuidExpression(values);
} else if (ObjectHelper.equal(function, "uuid")) {
- return SimpleExpressionBuilder.uuidExpression(null);
+ return MiscExpressionBuilder.uuidExpression(null);
}
remainder = ifStartsWithReturnRemainder("hash(", function);
@@ -211,9 +211,9 @@ public final class MiscFunctionFactory implements SimpleLanguageFunctionFactory
throw new SimpleParserException(
"Valid syntax: ${hash(value,algorithm)} or ${hash(value)} was: " + function, index);
}
- return SimpleExpressionBuilder.hashExpression(tokens[0].trim(), tokens[1].trim());
+ return MiscExpressionBuilder.hashExpression(tokens[0].trim(), tokens[1].trim());
} else {
- return SimpleExpressionBuilder.hashExpression(values.trim(), "SHA-256");
+ return MiscExpressionBuilder.hashExpression(values.trim(), "SHA-256");
}
}
@@ -223,7 +223,7 @@ public final class MiscFunctionFactory implements SimpleLanguageFunctionFactory
if (ObjectHelper.isEmpty(value)) {
throw new SimpleParserException("Valid syntax: ${empty(<type>)} but was: " + function, index);
}
- return SimpleExpressionBuilder.newEmptyExpression(value);
+ return MiscExpressionBuilder.newEmptyExpression(value);
}
remainder = ifStartsWithReturnRemainder("newEmpty(", function);
@@ -232,7 +232,7 @@ public final class MiscFunctionFactory implements SimpleLanguageFunctionFactory
if (ObjectHelper.isEmpty(value)) {
throw new SimpleParserException("Valid syntax: ${newEmpty(<type>)} but was: " + function, index);
}
- return SimpleExpressionBuilder.newEmptyExpression(value);
+ return MiscExpressionBuilder.newEmptyExpression(value);
}
remainder = ifStartsWithReturnRemainder("iif(", function);
@@ -247,7 +247,7 @@ public final class MiscFunctionFactory implements SimpleLanguageFunctionFactory
throw new SimpleParserException(
"Valid syntax: ${iif(predicate,trueExpression,falseExpression)} was: " + function, index);
}
- return SimpleExpressionBuilder.iifExpression(tokens[0].trim(), tokens[1].trim(), tokens[2].trim());
+ return MiscExpressionBuilder.iifExpression(tokens[0].trim(), tokens[1].trim(), tokens[2].trim());
}
remainder = ifStartsWithReturnRemainder("load(", function);
@@ -256,7 +256,7 @@ public final class MiscFunctionFactory implements SimpleLanguageFunctionFactory
if (ObjectHelper.isEmpty(value)) {
throw new SimpleParserException("Valid syntax: ${load(name)} but was: " + function, index);
}
- return SimpleExpressionBuilder.loadExpression(StringHelper.removeQuotes(value));
+ return MiscExpressionBuilder.loadExpression(StringHelper.removeQuotes(value));
}
return null;
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/QueryLanguageFunctionFactory.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/QueryLanguageFunctionFactory.java
index fe681c3a0c8c..9a6b4b67fc6e 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/QueryLanguageFunctionFactory.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/QueryLanguageFunctionFactory.java
@@ -18,7 +18,7 @@ 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.MiscExpressionBuilder;
import org.apache.camel.language.simple.types.SimpleParserException;
import org.apache.camel.spi.SimpleLanguageFunctionFactory;
import org.apache.camel.support.builder.ExpressionBuilder;
@@ -101,7 +101,7 @@ public final class QueryLanguageFunctionFactory implements SimpleLanguageFunctio
exp = exp.trim();
}
}
- return SimpleExpressionBuilder.simpleJsonPathExpression(input, exp);
+ return MiscExpressionBuilder.simpleJsonPathExpression(input, exp);
}
private static boolean hasInputSource(String exp) {
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 136d1d48eb5a..70b8ed47985e 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
@@ -18,7 +18,7 @@ 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.MiscExpressionBuilder;
import org.apache.camel.language.simple.types.SimpleParserException;
import org.apache.camel.spi.SimpleLanguageFunctionFactory;
import org.apache.camel.util.ObjectHelper;
@@ -48,9 +48,9 @@ public final class RandomFunctionFactory implements SimpleLanguageFunctionFactor
throw new SimpleParserException(
"Valid syntax: ${random(min,max)} or ${random(max)} was: " + function, index);
}
- return SimpleExpressionBuilder.randomExpression(tokens[0].trim(), tokens[1].trim());
+ return MiscExpressionBuilder.randomExpression(tokens[0].trim(), tokens[1].trim());
}
- return SimpleExpressionBuilder.randomExpression("0", values.trim());
+ return MiscExpressionBuilder.randomExpression("0", values.trim());
}
@Override
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 25d8aa2862d3..0f5fede0d819 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
@@ -18,7 +18,7 @@ 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.MiscExpressionBuilder;
import org.apache.camel.language.simple.types.SimpleParserException;
import org.apache.camel.spi.SimpleLanguageFunctionFactory;
import org.apache.camel.util.ObjectHelper;
@@ -41,7 +41,7 @@ public final class SkipFunctionFactory implements SimpleLanguageFunctionFactory
if (values == null || ObjectHelper.isEmpty(values)) {
throw new SimpleParserException("Valid syntax: ${skip(number)} was: " + function, index);
}
- return SimpleExpressionBuilder.skipExpression("${body}", values.trim());
+ return MiscExpressionBuilder.skipExpression("${body}", values.trim());
}
@Override
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/StringFunctionFactory.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/StringFunctionFactory.java
index 442c9be46086..7090b4b488a8 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/StringFunctionFactory.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/StringFunctionFactory.java
@@ -18,7 +18,7 @@ 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.StringExpressionBuilder;
import org.apache.camel.language.simple.types.SimpleParserException;
import org.apache.camel.spi.SimpleLanguageFunctionFactory;
import org.apache.camel.support.builder.ExpressionBuilder;
@@ -62,7 +62,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
if (tokens.length == 3) {
exp = tokens[2];
}
- return SimpleExpressionBuilder.replaceExpression(exp, from, to);
+ return StringExpressionBuilder.replaceExpression(exp, from, to);
}
remainder = ifStartsWithReturnRemainder("substring(", function);
@@ -88,7 +88,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
if (tokens.length == 3) {
exp = tokens[2];
}
- return SimpleExpressionBuilder.substringExpression(exp, num1, num2);
+ return StringExpressionBuilder.substringExpression(exp, num1, num2);
}
remainder = ifStartsWithReturnRemainder("substringBefore(", function);
@@ -111,7 +111,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
} else {
before = tokens[0];
}
- return SimpleExpressionBuilder.substringBeforeExpression(exp1, before);
+ return StringExpressionBuilder.substringBeforeExpression(exp1, before);
}
remainder = ifStartsWithReturnRemainder("substringAfter(", function);
@@ -134,7 +134,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
} else {
after = tokens[0];
}
- return SimpleExpressionBuilder.substringAfterExpression(exp1, after);
+ return StringExpressionBuilder.substringAfterExpression(exp1, after);
}
remainder = ifStartsWithReturnRemainder("substringBetween(", function);
@@ -164,7 +164,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
after = tokens[0];
before = tokens[1];
}
- return SimpleExpressionBuilder.substringBetweenExpression(exp1, after, before);
+ return StringExpressionBuilder.substringBetweenExpression(exp1, after, before);
}
remainder = ifStartsWithReturnRemainder("contains(", function);
@@ -187,7 +187,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
exp = tokens[0];
pattern = tokens[1];
}
- return SimpleExpressionBuilder.containsExpression(exp, pattern);
+ return StringExpressionBuilder.containsExpression(exp, pattern);
}
remainder = ifStartsWithReturnRemainder("trim(", function);
@@ -197,7 +197,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
if (ObjectHelper.isNotEmpty(value)) {
exp = StringHelper.removeQuotes(value);
}
- return SimpleExpressionBuilder.trimExpression(exp);
+ return StringExpressionBuilder.trimExpression(exp);
}
remainder = ifStartsWithReturnRemainder("val(", function);
@@ -217,7 +217,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
if (ObjectHelper.isNotEmpty(value)) {
exp = StringHelper.removeQuotes(value);
}
- return SimpleExpressionBuilder.capitalizeExpression(exp);
+ return StringExpressionBuilder.capitalizeExpression(exp);
}
remainder = ifStartsWithReturnRemainder("pad(", function);
@@ -241,7 +241,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
if (tokens.length == 3) {
separator = StringHelper.removeQuotes(tokens[2]);
}
- return SimpleExpressionBuilder.padExpression(exp, len, separator);
+ return StringExpressionBuilder.padExpression(exp, len, separator);
}
remainder = ifStartsWithReturnRemainder("concat(", function);
@@ -272,7 +272,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
} else {
exp2 = StringHelper.removeQuotes(values.trim());
}
- return SimpleExpressionBuilder.concatExpression(exp1, exp2, separator);
+ return StringExpressionBuilder.concatExpression(exp1, exp2, separator);
}
remainder = ifStartsWithReturnRemainder("quote(", function);
@@ -282,7 +282,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
if (ObjectHelper.isNotEmpty(value)) {
exp = StringHelper.removeQuotes(value);
}
- return SimpleExpressionBuilder.quoteExpression(exp);
+ return StringExpressionBuilder.quoteExpression(exp);
}
remainder = ifStartsWithReturnRemainder("safeQuote(", function);
@@ -292,7 +292,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
if (ObjectHelper.isNotEmpty(value)) {
exp = StringHelper.removeQuotes(value);
}
- return SimpleExpressionBuilder.safeQuoteExpression(exp);
+ return StringExpressionBuilder.safeQuoteExpression(exp);
}
remainder = ifStartsWithReturnRemainder("unquote(", function);
@@ -302,7 +302,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
if (ObjectHelper.isNotEmpty(value)) {
exp = StringHelper.removeQuotes(value);
}
- return SimpleExpressionBuilder.unquoteExpression(exp);
+ return StringExpressionBuilder.unquoteExpression(exp);
}
remainder = ifStartsWithReturnRemainder("uppercase(", function);
@@ -312,7 +312,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
if (ObjectHelper.isNotEmpty(value)) {
exp = StringHelper.removeQuotes(value);
}
- return SimpleExpressionBuilder.uppercaseExpression(exp);
+ return StringExpressionBuilder.uppercaseExpression(exp);
}
remainder = ifStartsWithReturnRemainder("lowercase(", function);
@@ -322,7 +322,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
if (ObjectHelper.isNotEmpty(value)) {
exp = StringHelper.removeQuotes(value);
}
- return SimpleExpressionBuilder.lowercaseExpression(exp);
+ return StringExpressionBuilder.lowercaseExpression(exp);
}
remainder = ifStartsWithReturnRemainder("length(", function);
@@ -332,7 +332,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
if (ObjectHelper.isNotEmpty(value)) {
exp = StringHelper.removeQuotes(value);
}
- return SimpleExpressionBuilder.lengthExpression(exp);
+ return StringExpressionBuilder.lengthExpression(exp);
}
remainder = ifStartsWithReturnRemainder("size(", function);
@@ -342,7 +342,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
if (ObjectHelper.isNotEmpty(value)) {
exp = StringHelper.removeQuotes(value);
}
- return SimpleExpressionBuilder.sizeExpression(exp);
+ return StringExpressionBuilder.sizeExpression(exp);
}
remainder = ifStartsWithReturnRemainder("normalizeWhitespace(", function);
@@ -352,7 +352,7 @@ public final class StringFunctionFactory implements SimpleLanguageFunctionFactor
if (ObjectHelper.isNotEmpty(value)) {
exp = StringHelper.removeQuotes(value);
}
- return SimpleExpressionBuilder.normalizeWhitespaceExpression(exp);
+ return StringExpressionBuilder.normalizeWhitespaceExpression(exp);
}
return null;
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/TypeFunctionFactory.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/TypeFunctionFactory.java
index 09151c9f63a1..20ba9c5ceb19 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/TypeFunctionFactory.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/TypeFunctionFactory.java
@@ -18,7 +18,7 @@ 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.MiscExpressionBuilder;
import org.apache.camel.spi.SimpleLanguageFunctionFactory;
import static org.apache.camel.language.simple.SimpleFunctionHelper.appendClass;
@@ -36,9 +36,9 @@ public final class TypeFunctionFactory implements SimpleLanguageFunctionFactory
public Expression createFunction(CamelContext camelContext, String function, int index) {
String remainder = ifStartsWithReturnRemainder("type:", function);
if (remainder != null) {
- Expression exp = SimpleExpressionBuilder.typeExpression(remainder);
+ Expression exp = MiscExpressionBuilder.typeExpression(remainder);
exp.init(camelContext);
- return SimpleExpressionBuilder.cacheExpression(exp);
+ return MiscExpressionBuilder.cacheExpression(exp);
}
return null;
}
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
index 368368a55885..f3cc90574dcf 100644
--- 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
@@ -18,7 +18,7 @@ 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.OgnlExpressionBuilder;
import org.apache.camel.language.simple.types.SimpleParserException;
import org.apache.camel.spi.SimpleLanguageFunctionFactory;
import org.apache.camel.support.builder.ExpressionBuilder;
@@ -82,7 +82,7 @@ public final class VariableFunctionFactory implements SimpleLanguageFunctionFact
}
if (OgnlHelper.isValidOgnlExpression(key)) {
- return SimpleExpressionBuilder.variablesOgnlExpression(key);
+ return OgnlExpressionBuilder.variablesOgnlExpression(key);
} else {
return ExpressionBuilder.variableExpression(key);
}
diff --git a/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleTest.java b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleTest.java
index 5f55e5515849..980f182d453d 100644
--- a/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleTest.java
@@ -3680,15 +3680,20 @@ public class SimpleTest extends LanguageTestSupport {
@Test
public void testThrowException() {
+ // RuntimeException subclasses must propagate unwrapped so that typed onException handlers match.
+ // Pre-fix: the catch(Exception) block re-wrapped the just-thrown RuntimeException in a second
+ // RuntimeException, so e.getCause() held the intended exception instead of e itself.
try {
Expression expression = context.resolveLanguage("simple").createExpression("${throwException('Forced error')}");
expression.evaluate(exchange, Object.class);
fail();
} catch (Exception e) {
- assertIsInstanceOf(IllegalArgumentException.class, e.getCause());
- assertEquals("Forced error", e.getCause().getMessage());
+ assertIsInstanceOf(IllegalArgumentException.class, e);
+ assertEquals("Forced error", e.getMessage());
}
+ // Checked exceptions are wrapped in RuntimeCamelException (one layer, not two).
+ // Pre-fix: they were wrapped twice: RuntimeException(RuntimeCamelException(IOException)).
try {
Expression expression
= context.resolveLanguage("simple")
@@ -3696,8 +3701,8 @@ public class SimpleTest extends LanguageTestSupport {
expression.evaluate(exchange, Object.class);
fail();
} catch (Exception e) {
- assertIsInstanceOf(IOException.class, e.getCause().getCause());
- assertEquals("Some IO error", e.getCause().getCause().getMessage());
+ assertIsInstanceOf(IOException.class, e.getCause());
+ assertEquals("Some IO error", e.getCause().getMessage());
}
}
diff --git a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/CollateFunctionFactoryTest.java b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/CollateFunctionFactoryTest.java
index 65d959725568..67f5cc0e80ad 100644
--- a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/CollateFunctionFactoryTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/CollateFunctionFactoryTest.java
@@ -120,6 +120,32 @@ public class CollateFunctionFactoryTest extends AbstractSimpleFunctionFactoryTes
assertEquals("G", chunk3.get(0));
}
+ @Test
+ public void testCollateDynamicEvaluatedTwiceProducesCorrectChunks() {
+ // pre-fix: collateExpression(String, String) overwrote the 'exp' field inside evaluate(),
+ // so a second call wrapped an already-grouped iterator instead of the raw body expression,
+ // producing iterators-of-iterators instead of list chunks.
+ List<Object> data = new ArrayList<>();
+ data.add("A");
+ data.add("B");
+ data.add("C");
+ data.add("D");
+ exchange.getIn().setBody(data);
+ exchange.getIn().setHeader("num", 2);
+
+ Iterator<?> first = (Iterator<?>) evaluate("collate(${header.num})");
+ assertEquals(List.of("A", "B"), first.next());
+ assertEquals(List.of("C", "D"), first.next());
+ assertFalse(first.hasNext());
+
+ // second evaluation on the same route exchange must produce the same shape
+ exchange.getIn().setBody(data);
+ Iterator<?> second = (Iterator<?>) evaluate("collate(${header.num})");
+ Object chunk = second.next();
+ assertFalse(chunk instanceof Iterator, "chunk must be a List of elements, not a nested iterator");
+ assertEquals(List.of("A", "B"), chunk);
+ }
+
@Test
public void testCreateCode() {
assertEquals("collate(exchange, 10)", createCode("collate(10)"));
diff --git a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/CollectionFunctionFactoryTest.java b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/CollectionFunctionFactoryTest.java
index 87158d5fd1ca..b7d92467a6d6 100644
--- a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/CollectionFunctionFactoryTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/CollectionFunctionFactoryTest.java
@@ -25,6 +25,7 @@ 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.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -93,7 +94,40 @@ public class CollectionFunctionFactoryTest extends AbstractSimpleFunctionFactory
@Test
public void testCreateCodeRangeMax() {
- assertEquals("rangeList(exchange, 0, 3)", createCode("range(3)"));
+ // pre-fix: csimple generated rangeList(exchange, 0, N) while simple used min=1,
+ // causing ${range(N)} to produce different results in the two execution modes.
+ assertEquals("rangeList(exchange, 1, 3)", createCode("range(3)"));
+ }
+
+ // --- shuffle toString ---
+
+ @Test
+ public void testShuffleToStringReportsShuffleNotReverse() {
+ // pre-fix: shuffleExpression.toString() returned "reverse(...)" due to a copy-paste
+ // error; any error message or log showing the expression would be completely misleading.
+ org.apache.camel.Expression expr
+ = context.resolveLanguage("simple").createExpression("${shuffle(${body})}");
+ expr.init(context);
+ String repr = expr.toString();
+ assertTrue(repr.contains("shuffle"), "toString should contain 'shuffle', got: " + repr);
+ assertFalse(repr.startsWith("reverse"), "toString must not start with 'reverse', got: " + repr);
+ }
+
+ // --- map toString ---
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testMapToStringShowsSourcePairsNotCompiledExpressions() {
+ // pre-fix: mapExpression.toString() printed the compiled Expression[] array (JVM object refs)
+ // instead of the original String[] pairs, making diagnostic output useless.
+ org.apache.camel.Expression expr
+ = context.resolveLanguage("simple").createExpression("${map('k1','v1')}");
+ expr.init(context);
+ String repr = expr.toString();
+ assertTrue(repr.contains("k1"), "toString should contain source key 'k1', got: " + repr);
+ assertTrue(repr.contains("v1"), "toString should contain source value 'v1', got: " + repr);
+ assertFalse(repr.contains("ExpressionAdapter"),
+ "toString must not expose compiled objects, got: " + repr);
}
// --- distinct ---
diff --git a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/MathFunctionFactoryTest.java b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/MathFunctionFactoryTest.java
index f14dfc7a0d93..ad510eecdb13 100644
--- a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/MathFunctionFactoryTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/MathFunctionFactoryTest.java
@@ -45,12 +45,26 @@ public class MathFunctionFactoryTest extends AbstractSimpleFunctionFactoryTestSu
assertEquals(3L, evaluate("floor(${body})", Long.class));
}
+ @Test
+ public void testFloorLargeValueNoOverflow() {
+ // pre-fix: (int) Math.floor(3_000_000_000.9) silently overflowed to a negative int
+ exchange.getIn().setBody(3_000_000_000.9);
+ assertEquals(3_000_000_000L, evaluate("floor(${body})", Long.class));
+ }
+
@Test
public void testCeil() {
exchange.getIn().setBody(3.2);
assertEquals(4L, evaluate("ceil(${body})", Long.class));
}
+ @Test
+ public void testCeilLargeValueNoOverflow() {
+ // pre-fix: (int) Math.ceil(2_147_483_648.0) silently overflowed to Integer.MIN_VALUE
+ exchange.getIn().setBody(2_147_483_648.0);
+ assertEquals(2_147_483_648L, evaluate("ceil(${body})", Long.class));
+ }
+
@Test
public void testSum() {
assertEquals(6.0, evaluate("sum(1, 2, 3)", Double.class));
diff --git a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/MiscFunctionFactoryTest.java b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/MiscFunctionFactoryTest.java
index a5be49e28d1b..fd54f3d5e68b 100644
--- a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/MiscFunctionFactoryTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/MiscFunctionFactoryTest.java
@@ -27,6 +27,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class MiscFunctionFactoryTest extends AbstractSimpleFunctionFactoryTestSupport {
@@ -153,6 +154,50 @@ public class MiscFunctionFactoryTest extends AbstractSimpleFunctionFactoryTestSu
assertTrue(((Map<?, ?>) result).isEmpty());
}
+ // --- throwException ---
+
+ @Test
+ public void testThrowExceptionDefaultTypeIsIllegalArgumentException() {
+ // pre-fix: catch(Exception e) around the entire body caught the RuntimeException that was
+ // just thrown and re-wrapped it in new RuntimeException(e), producing
+ // RuntimeException(IllegalArgumentException) instead of a bare IllegalArgumentException.
+ // onException(IllegalArgumentException.class) handlers would never match.
+ IllegalArgumentException ex = assertThrows(
+ IllegalArgumentException.class,
+ () -> evaluate("throwException('boom')"));
+ assertEquals("boom", ex.getMessage());
+ }
+
+ @Test
+ public void testThrowExceptionCustomRuntimeTypeIsNotDoubleWrapped() {
+ // A custom RuntimeException subclass must arrive unwrapped so that typed onException
+ // handlers can catch it. Pre-fix: it arrived wrapped in RuntimeException(CustomEx).
+ exchange.setProperty("type", "java.lang.IllegalStateException");
+ IllegalStateException ex = assertThrows(
+ IllegalStateException.class,
+ () -> evaluate("throwException('state failure',java.lang.IllegalStateException)"));
+ assertEquals("state failure", ex.getMessage());
+ }
+
+ // --- pad ---
+
+ @Test
+ public void testPadNullBodyReturnsNull() {
+ // pre-fix: answer.length() threw NullPointerException when the body was null
+ exchange.getIn().setBody(null);
+ assertNull(evaluate("pad(${body},5)"));
+ }
+
+ @Test
+ public void testPadNullWidthThrowsIllegalArgument() {
+ // pre-fix: int width = null unboxed to NullPointerException with no useful message
+ exchange.getIn().setBody("hi");
+ // header absent -> width expression evaluates to null
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> evaluate("pad(${body},${header.missingWidth})"));
+ }
+
// --- iif ---
@Test
diff --git a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/SkipFunctionFactoryTest.java b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/SkipFunctionFactoryTest.java
index 59e5a8a51d83..fa384bd304ed 100644
--- a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/SkipFunctionFactoryTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/SkipFunctionFactoryTest.java
@@ -70,6 +70,20 @@ public class SkipFunctionFactoryTest extends AbstractSimpleFunctionFactoryTestSu
assertFalse(it.hasNext());
}
+ @Test
+ public void testSkipDynamicNullNumberThrowsIllegalArgument() {
+ // pre-fix: int n = num.evaluate(…, Integer.class) unboxed null and threw NullPointerException
+ // with no useful message when the header was absent.
+ List<Object> data = new ArrayList<>();
+ data.add("A");
+ data.add("B");
+ exchange.getIn().setBody(data);
+ // header intentionally absent so the expression resolves to null
+ org.junit.jupiter.api.Assertions.assertThrows(
+ IllegalArgumentException.class,
+ () -> evaluate("skip(${header.missingNum})"));
+ }
+
@Test
public void testCreateCode() {
assertEquals("skip(exchange, 10)", createCode("skip(10)"));
diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc
index a75f995c5473..702d78ff39a8 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc
@@ -20,6 +20,40 @@ It implies small differences listed https://github.com/whatap/java-grok#what-is-
=== camel-core
+==== Simple language: internal builder classes reorganized
+
+The internal class `org.apache.camel.language.simple.SimpleExpressionBuilder`
+(in `camel-core-languages`) has been removed. Its methods have been split across
+seven domain-aligned builder classes:
+
+- `CollectionExpressionBuilder`
+- `DateExpressionBuilder`
+- `FileExpressionBuilder`
+- `MathExpressionBuilder`
+- `MiscExpressionBuilder`
+- `OgnlExpressionBuilder`
+- `StringExpressionBuilder`
+
+These classes are all in the `org.apache.camel.language.simple` package and are
+internal API (not part of `camel-api`). Applications and custom DSLs that import
+`SimpleExpressionBuilder` directly must update their imports to the appropriate
+replacement class.
+
+==== Simple language: `floor`/`ceil` return `long` instead of `int`
+
+The `floor` and `ceil` functions in the Simple language previously silently
+overflowed for input values larger than `Integer.MAX_VALUE` because the result
+was cast to `int`. They now return `long`. Routes that pass the result of
+`floor`/`ceil` to a method that expects `int` may require an explicit cast or
+a converter step.
+
+==== Simple language: `range(N)` starts at 1, not 0
+
+In csimple, `range(N)` previously generated a range starting at 0 (i.e., 0..N-1).
+It now starts at 1 (i.e., 1..N), consistent with the `simple` language behaviour.
+Routes using csimple with `range(N)` that relied on the range starting at 0 must
+be updated.
+
The `camel-api` module now has an optional dependency on `org.jspecify:jspecify` for null safety annotations
(`@NullMarked`, `@Nullable`). The dependency is `<optional>true</optional>`, so it is not pulled transitively
into your application. The annotations are used to document nullability contracts across the Camel API surface