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 3c2fbfe07beb CAMEL-26420: camel-core - Simple: the white space around
the prefix and the expression of join is not part of them (#29044)
3c2fbfe07beb is described below
commit 3c2fbfe07beb275fb643c0df485448d36f94d42d
Author: allthingssecurity <[email protected]>
AuthorDate: Tue Nov 3 13:26:06 2026 +0530
CAMEL-26420: camel-core - Simple: the white space around the prefix and the
expression of join is not part of them (#29044)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
.../camel/catalog/docs/simple-functions.adoc | 4 +-
.../modules/languages/pages/simple-functions.adoc | 4 +-
.../language/simple/SimpleFunctionHelper.java | 22 ++++++----
.../simple/functions/JoinFunctionFactory.java | 25 +++++++-----
.../simple/functions/JoinFunctionFactoryTest.java | 47 ++++++++++++++++++++++
.../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc | 9 +++++
6 files changed, 92 insertions(+), 19 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/simple-functions.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/simple-functions.adoc
index bcf1371e8f6b..af676991a731 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/simple-functions.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/simple-functions.adoc
@@ -563,7 +563,9 @@ In the example we had to add a space after `"Hello "`, but
you can also use the
The `join` function is a more complex function to join together a set of
values, separated by comma (by default) as a single combined String.
For example if the message body contains a list of [A, B, C] then `${join()}`
would return `A,B,C`.
To use semicolon as separator you would use `${join(;)}`. The `prefix`
argument can be used to prefix each value, so for example
-`${join(&,id=)}` would return `id=A&id=B&id=C`.
+`${join(&,id=)}` would return `id=A&id=B&id=C`. The white space around the
prefix and the expression is not part of
+them, so `${join(&, id=)}` returns the same. An unquoted separator is kept as
written, so `${join(; )}` returns
+`A; B; C`; a quoted one is the text inside its quotes.
The `escape` function escapes special characters so a value can be safely
embedded in another format,
instead of chaining multiple `replace` calls or using a custom bean. The first
argument is the kind of escaping:
diff --git
a/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-functions.adoc
b/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-functions.adoc
index bcf1371e8f6b..af676991a731 100644
---
a/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-functions.adoc
+++
b/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-functions.adoc
@@ -563,7 +563,9 @@ In the example we had to add a space after `"Hello "`, but
you can also use the
The `join` function is a more complex function to join together a set of
values, separated by comma (by default) as a single combined String.
For example if the message body contains a list of [A, B, C] then `${join()}`
would return `A,B,C`.
To use semicolon as separator you would use `${join(;)}`. The `prefix`
argument can be used to prefix each value, so for example
-`${join(&,id=)}` would return `id=A&id=B&id=C`.
+`${join(&,id=)}` would return `id=A&id=B&id=C`. The white space around the
prefix and the expression is not part of
+them, so `${join(&, id=)}` returns the same. An unquoted separator is kept as
written, so `${join(; )}` returns
+`A; B; C`; a quoted one is the text inside its quotes.
The `escape` function escapes special characters so a value can be safely
embedded in another format,
instead of chaining multiple `replace` calls or using a custom bean. The first
argument is the kind of escaping:
diff --git
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleFunctionHelper.java
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleFunctionHelper.java
index e56fdb14b3f7..a93e0835beb3 100644
---
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleFunctionHelper.java
+++
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleFunctionHelper.java
@@ -109,14 +109,22 @@ public final class SimpleFunctionHelper {
public static String[] splitArguments(String values) {
String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', false,
true);
for (int i = 0; i < tokens.length; i++) {
- String token = tokens[i];
- String trimmed = token.trim();
- if (!trimmed.isEmpty()) {
- // a whitespace only argument such as a new line is kept as-is
- token = trimmed;
- }
- tokens[i] = StringHelper.removeLeadingAndEndingQuotes(token);
+ tokens[i] = trimArgument(tokens[i]);
}
return tokens;
}
+
+ /**
+ * An argument as {@link #splitArguments(String)} gives it, from the
argument as written (split with
+ * {@code StringQuoteHelper.splitSafeQuote(values, ',', false, true)}):
without the white space and the quotes
+ * around it, unless it is only white space.
+ */
+ public static String trimArgument(String token) {
+ String trimmed = token.trim();
+ if (!trimmed.isEmpty()) {
+ // a whitespace only argument such as a new line is kept as-is
+ token = trimmed;
+ }
+ return StringHelper.removeLeadingAndEndingQuotes(token);
+ }
}
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 f9ff0803eaf0..7a9d34337d8d 100644
---
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/JoinFunctionFactory.java
+++
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/JoinFunctionFactory.java
@@ -26,10 +26,15 @@ import org.apache.camel.util.StringHelper;
import org.apache.camel.util.StringQuoteHelper;
import static
org.apache.camel.language.simple.SimpleFunctionHelper.ifStartsWithReturnRemainder;
+import static
org.apache.camel.language.simple.SimpleFunctionHelper.trimArgument;
/**
* Built-in Simple function: {@code ${join()}} / {@code ${join(separator)}} /
{@code ${join(separator,prefix)}} /
* {@code ${join(separator,prefix,expression)}}.
+ * <p>
+ * The white space around the prefix and the expression is not part of them,
as for the arguments of the other
+ * functions. An unquoted separator is kept as written, as it can be white
space on purpose: {@code ${join(; )}} joins
+ * with {@code "; "}; a quoted one is the text inside its quotes.
*/
public final class JoinFunctionFactory implements
SimpleLanguageFunctionFactory {
@@ -44,20 +49,20 @@ public final class JoinFunctionFactory implements
SimpleLanguageFunctionFactory
String prefix = null;
String exp = "${body}";
if (ObjectHelper.isNotEmpty(values)) {
- String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',',
false);
+ // the arguments as written (with their quotes), split at the
commas outside quotes as splitArguments does,
+ // so the separator can be kept as written
+ String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',',
false, true);
if (tokens.length > 3) {
throw new SimpleParserException(
"Valid syntax: ${join(separator,prefix,expression)}
was: " + function, index);
}
- if (tokens.length == 3) {
- separator = tokens[0];
- prefix = tokens[1];
- exp = tokens[2];
- } else if (tokens.length == 2) {
- separator = tokens[0];
- prefix = tokens[1];
- } else {
- separator = tokens[0];
+ separator = StringHelper.removeLeadingAndEndingQuotes(tokens[0]);
+ if (tokens.length > 1) {
+ // the prefix and the expression without the white space and
the quotes around them
+ prefix = trimArgument(tokens[1]);
+ if (tokens.length == 3) {
+ exp = trimArgument(tokens[2]);
+ }
}
}
return CollectionExpressionBuilder.joinExpression(exp, separator,
prefix);
diff --git
a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/JoinFunctionFactoryTest.java
b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/JoinFunctionFactoryTest.java
index b7e090d6e79a..71a46a75e97c 100644
---
a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/JoinFunctionFactoryTest.java
+++
b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/JoinFunctionFactoryTest.java
@@ -56,4 +56,51 @@ public class JoinFunctionFactoryTest extends
AbstractSimpleFunctionFactoryTestSu
assertEquals("id=A&id=B&id=C",
evaluate("join('&','id=','${header.id}')", String.class));
}
+
+ @Test
+ public void testJoinSpaceAroundPrefixAndExpression() {
+ List<Object> data = new ArrayList<>();
+ data.add("A");
+ data.add("B");
+ data.add("C");
+ exchange.getIn().setBody(data);
+ exchange.getIn().setHeader("id", List.of("X", "Y"));
+
+ // the white space around the prefix and the expression is not part of
them
+ assertEquals("id=A&id=B&id=C", evaluate("join(&, id=)", String.class));
+ assertEquals("id=A&id=B&id=C", evaluate("join(&, id= )",
String.class));
+ assertEquals("id=X&id=Y", evaluate("join(&, id=, ${header.id})",
String.class));
+ assertEquals("id=X&id=Y", evaluate("join('&', 'id=', '${header.id}')",
String.class));
+ // a space inside quotes is kept
+ assertEquals(" id=A& id=B& id=C", evaluate("join(&,' id=')",
String.class));
+ }
+
+ @Test
+ public void testJoinSeparatorKeptAsWritten() {
+ List<Object> data = new ArrayList<>();
+ data.add("A");
+ data.add("B");
+ data.add("C");
+ exchange.getIn().setBody(data);
+
+ // the separator can be white space on purpose, so it is kept as
written
+ assertEquals("A; B; C", evaluate("join(; )", String.class));
+ assertEquals("id=A; id=B; id=C", evaluate("join(; ,id=)",
String.class));
+ assertEquals("id=A; id=B; id=C", evaluate("join('; ', id=)",
String.class));
+ }
+
+ @Test
+ public void testJoinQuotesInsideAnArgumentAreKept() {
+ List<Object> data = new ArrayList<>();
+ data.add("A");
+ data.add("B");
+ exchange.getIn().setBody(data);
+ exchange.getIn().setHeader("x", "Hello,World");
+
+ // only the quotes around a whole argument are removed, as for the
other functions
+ assertEquals("a'b'cA&a'b'cB", evaluate("join(&,a'b'c)", String.class));
+ assertEquals("id=Hello&id=World",
evaluate("join(&,id=,${header.x.split(',')})", String.class));
+ // a quoted separator is the text inside its quotes
+ assertEquals("id=A&id=B", evaluate("join('&' , id=)", String.class));
+ }
}
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
index c843baa34182..66d2601aecdf 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
@@ -355,6 +355,15 @@ space next to the comma, such as `+${replace(a,b )}+` for
the text `++b ++`, quo
Only the quotes around a whole argument are removed: a quote inside an
argument, as in `+a'b'c+`, is now kept (it was
removed).
+The prefix and the expression of the `join` function no longer include the
white space around them either:
+for the body `[A, B, C]`, `+${join(&, id=)}+` gives `id=A&id=B&id=C` (it gave
`++ id=A& id=B& id=C++`). An unquoted
+separator is kept as written, as it can be white space on purpose: `+${join(;
)}+` still joins with `++; ++`; a quoted
+separator is the text inside its quotes (`+${join('&' ,id=)}+` joins with `&`,
it joined with `++& ++`; put the space
+inside the quotes to keep it: `+${join('& ',id=)}+`). A prefix with a space at
its start or end is quoted instead:
+`+${join(&,' id=')}+`. As for the other functions, only the quotes around a
whole argument of
+`join` are removed: a quote inside an argument, as in `+${join(&,a'b'c)}+`, is
now kept (it was removed), so an
+expression such as `+${join(&,id=,${header.x.split(',')})}+` now works.
+
=== camel-core - a route called from a doTry uses the error handler of its EIPs
A route that a `doTry` calls from its steps, or from its `doCatch` or
`doFinally` blocks (with `direct`, `seda`,