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 8efd0e972dbf CAMEL-26312: camel-core-languages - Simple: the white
space around an argument of replace, substring, substringBefore,
substringAfter, substringBetween and contains is not part of it (#28715)
8efd0e972dbf is described below
commit 8efd0e972dbfe5bc067cfc8cb0e504aff7eb537e
Author: allthingssecurity <[email protected]>
AuthorDate: Sat Oct 31 00:23:19 2026 +0530
CAMEL-26312: camel-core-languages - Simple: the white space around an
argument of replace, substring, substringBefore, substringAfter,
substringBetween and contains is not part of it (#28715)
replace, substring, substringBefore, substringAfter, substringBetween and
contains split their arguments keeping the white space around a value, so a
space after a comma became part of the next argument (${replace('o', '0')}
replaced with " 0", and the documented replace examples did not work). The
splitArguments helper of CAMEL-24969 moves from CollectionFunctionFactory to
SimpleFunctionHelper and is used for these six functions too. A quote inside an
argument (a'b'c) is now kept. Up [...]
_Claude Code on behalf of allthingssecurity_
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
.../language/simple/SimpleFunctionHelper.java | 23 ++++++++++++++++++++++
.../functions/CollectionFunctionFactory.java | 19 +-----------------
.../simple/functions/StringFunctionFactory.java | 13 ++++++------
.../apache/camel/language/simple/SimpleTest.java | 18 +++++++++++++++++
.../functions/StringFunctionFactoryTest.java | 21 ++++++++++++++++++++
.../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc | 11 +++++++++++
6 files changed, 81 insertions(+), 24 deletions(-)
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 f01b84edfa48..e56fdb14b3f7 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
@@ -16,6 +16,9 @@
*/
package org.apache.camel.language.simple;
+import org.apache.camel.util.StringHelper;
+import org.apache.camel.util.StringQuoteHelper;
+
public final class SimpleFunctionHelper {
private SimpleFunctionHelper() {
@@ -96,4 +99,24 @@ public final class SimpleFunctionHelper {
}
return -1;
}
+
+ /**
+ * Splits the arguments of a function at the commas outside quotes. The
white space around an unquoted argument is
+ * not part of it, so {@code ${sort(${body}, true)}} and {@code
${replace('o', '0')}} work, while a quoted argument
+ * such as {@code ' '} is kept as written (without its quotes). An
argument of only white space is kept as it is, so
+ * {@code ${replace( ,-)}} replaces a space.
+ */
+ 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);
+ }
+ return tokens;
+ }
}
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 4a7a98a78497..ec5508ef64e5 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
@@ -29,6 +29,7 @@ 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.splitArguments;
/**
* Built-in Simple collection functions: {@code ${setHeader}}, {@code
${setVariable}}, {@code ${range}},
@@ -339,22 +340,4 @@ public final class CollectionFunctionFactory implements
SimpleLanguageFunctionFa
return null;
}
-
- /**
- * Splits the function arguments by comma. Unquoted arguments are trimmed,
so ${sort(${body}, true)} works, while a
- * quoted argument such as ' ' is kept as written (without its quotes).
- */
- private 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);
- }
- return tokens;
- }
}
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 e7858d6b5969..ef44f6227e0f 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
@@ -28,6 +28,7 @@ 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.splitArguments;
/**
* Built-in Simple string functions: {@code ${replace}}, {@code ${substring}},
{@code ${substringBefore}},
@@ -49,7 +50,7 @@ public final class StringFunctionFactory implements
SimpleLanguageFunctionFactor
throw new SimpleParserException(
"Valid syntax: ${replace(from,to)} or
${replace(from,to,expression)} was: " + function, index);
}
- String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',',
false);
+ String[] tokens = splitArguments(values);
if (tokens.length < 2 || tokens.length > 3) {
throw new SimpleParserException(
"Valid syntax: ${replace(from,to)} or
${replace(from,to,expression)} was: " + function, index);
@@ -75,7 +76,7 @@ public final class StringFunctionFactory implements
SimpleLanguageFunctionFactor
+ function,
index);
}
- String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',',
false);
+ String[] tokens = splitArguments(values);
if (tokens.length > 3) {
throw new SimpleParserException(
"Valid syntax: ${substring(num,num,expression)} was: "
+ function, index);
@@ -99,7 +100,7 @@ public final class StringFunctionFactory implements
SimpleLanguageFunctionFactor
throw new SimpleParserException(
"Valid syntax: ${substringBefore(exp)} or
${substringBefore(exp,exp)} was: " + function, index);
}
- String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',',
false);
+ String[] tokens = splitArguments(values);
if (tokens.length > 2) {
throw new SimpleParserException(
"Valid syntax: ${substringBefore(exp)} or
${substringBefore(exp,exp)} was: " + function, index);
@@ -122,7 +123,7 @@ public final class StringFunctionFactory implements
SimpleLanguageFunctionFactor
throw new SimpleParserException(
"Valid syntax: ${substringAfter(exp)} or
${substringAfter(exp,exp)} was: " + function, index);
}
- String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',',
false);
+ String[] tokens = splitArguments(values);
if (tokens.length > 2) {
throw new SimpleParserException(
"Valid syntax: ${substringAfter(exp)} or
${substringAfter(exp,exp)} was: " + function, index);
@@ -147,7 +148,7 @@ public final class StringFunctionFactory implements
SimpleLanguageFunctionFactor
+ function,
index);
}
- String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',',
false);
+ String[] tokens = splitArguments(values);
if (tokens.length < 2 || tokens.length > 3) {
throw new SimpleParserException(
"Valid syntax: ${substringBetween(after,before)} or
${substringBetween(exp,after,before)} was: "
@@ -175,7 +176,7 @@ public final class StringFunctionFactory implements
SimpleLanguageFunctionFactor
throw new SimpleParserException(
"Valid syntax: ${contains(text)} or
${contains(exp,text)} was: " + function, index);
}
- String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',',
false);
+ String[] tokens = splitArguments(values);
if (tokens.length < 1 || tokens.length > 2) {
throw new SimpleParserException(
"Valid syntax: ${contains(text)} or
${contains(exp,text)} was: " + function, index);
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 2a09973610de..c598f0158865 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
@@ -1877,6 +1877,24 @@ public class SimpleTest extends LanguageTestSupport {
assertExpression("${replace(",',${header.foo})}", "{'foo':
'cheese'}");
}
+ @Test
+ void testReplaceAllExpressionWithSpacesAroundTheComma() {
+ // the examples of the simple-advanced page ("Replacing double and
single quotes") write a space around the
+ // comma; the spaces were part of the arguments, so nothing or the
wrong text was replaced
+ exchange.getMessage().setBody("{\"foo\": \"cheese\"}");
+ assertExpression("${replace(" , ')}", "{'foo': 'cheese'}");
+ exchange.getMessage().setBody("{'foo': 'cheese'}");
+ assertExpression("${replace(' , ")}", "{\"foo\":
\"cheese\"}");
+ exchange.getMessage().setBody("{\"foo\": \"cheese\"}");
+ assertExpression("${replace(" , ∅)}", "{foo: cheese}");
+
+ exchange.getMessage().setBody("Hello World");
+ assertExpression("${replace('o', '0')}", "Hell0 W0rld");
+ assertExpression("${replace(' ', '_')}", "Hello_World");
+ exchange.getMessage().setHeader("foo", "Hello World");
+ assertExpression("${replace('o', '0', ${header.foo})}", "Hell0 W0rld");
+ }
+
@Test
public void testList() {
exchange.getMessage().setBody("4");
diff --git
a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/StringFunctionFactoryTest.java
b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/StringFunctionFactoryTest.java
index 82d93c38c5a0..fc0ea7ceba5d 100644
---
a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/StringFunctionFactoryTest.java
+++
b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/StringFunctionFactoryTest.java
@@ -48,6 +48,27 @@ public class StringFunctionFactoryTest extends
AbstractSimpleFunctionFactoryTest
assertEquals("foo-bar", evaluate("replace( ,-,${header.msg})",
String.class));
}
+ @Test
+ void testArgumentsWithASpaceAfterTheComma() {
+ // a space after the comma is not part of the next argument, as in the
collection functions (CAMEL-24969)
+ exchange.getIn().setBody("Hello great big World");
+ assertEquals("Hell0 great big W0rld", evaluate("replace('o', '0')",
String.class));
+ assertEquals("ello great big Worl", evaluate("substring(1, -1)",
String.class));
+ assertEquals("Hello", evaluate("substringBefore(${body}, ' ')",
String.class));
+ assertEquals("great big World", evaluate("substringAfter(${body},
'Hello ')", String.class));
+ assertEquals(" big ", evaluate("substringBetween(${body}, 'great',
'World')", String.class));
+ assertEquals(true, evaluate("contains(${body}, 'hello')",
Boolean.class));
+ // the spaces inside the quotes are kept
+ assertEquals("Hello GREAT big World", evaluate("replace(' great', '
GREAT')", String.class));
+ }
+
+ @Test
+ void testQuoteInsideAnArgumentIsKept() {
+ // only the quotes around a whole argument are removed: a'b'c is the
text a'b'c (it was abc)
+ exchange.getIn().setBody("a'b'c and abc");
+ assertEquals("x and abc", evaluate("replace(a'b'c,x)", String.class));
+ }
+
// --- substring ---
@Test
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 d342cc41af88..936a5773a1be 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
@@ -185,6 +185,17 @@ since `val` was added in Camel 4.18.1. A route that
removed the quotes itself, o
adjusted. Values without quotes, functions such as `+${body}+`, and the quotes
inside the predicate of `iif` are not
changed.
+=== camel-core - Simple: a space after a comma is not part of a function
argument
+
+The arguments of the `replace`, `substring`, `substringBefore`,
`substringAfter`, `substringBetween` and `contains`
+functions no longer include the white space around them, as for the collection
functions since 4.23 (CAMEL-24969):
+`+${replace('o', '0')}+` replaces `o` with `0` (it replaced it with `++ 0++`),
and the documented
+`+${replace(" , ')}+` replaces double quotes with single quotes. The
spaces inside quotes are kept, and an
+argument of only white space is kept as it is, so `+${replace( ,-)}+` still
replaces a space. A route that relied on a
+space next to the comma, such as `+${replace(a,b )}+` for the text `++b ++`,
quotes it instead: `+${replace(a,'b ')}+`.
+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).
+
=== camel-util - FilterIterator.remove after hasNext throws
IllegalStateException
`FilterIterator.hasNext()` looks ahead in the iterator it wraps, so a
`remove()` after `next()` and then `hasNext()`