This is an automated email from the ASF dual-hosted git repository.

Croway 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 b6355fb3d5d6 perf: camel-joor - default-value patterns must not span 
two calls on a line
b6355fb3d5d6 is described below

commit b6355fb3d5d679c9e3ddc3e739b62f03d7152d1c
Author: croway <[email protected]>
AuthorDate: Thu Sep 10 21:46:41 2026 +0200

    perf: camel-joor - default-value patterns must not span two calls on a line
    
    The headerAs/exchangePropertyAs default-value regexes used a greedy (.+)
    for the default value, so with two calls on one line the match ran from
    the first call's name to the last call's type. For example
    
      'order-' + headerAs('amount', Integer) + '-' + headerAs('vip', Boolean)
    
    was rewritten to headerAs(message, "amount",  Integer) + "-" +
    headerAs(message, "vip", Boolean.class) and failed to compile with
    "cannot find symbol: variable Integer". The patterns also left the dot of
    ".class" unescaped and accepted a literal | as a quote via ['|"].
    
    The default value is now matched lazily and may not contain parentheses
    except one balanced level (new Date(), '(none)'), so it can never run
    into a following call; the type suffix is \.class and quotes are ['"].
    The pattern pieces are shared constants so the eight patterns stay in
    sync. New tests cover two two-argument calls, two three-argument calls,
    mixed forms, .class and no-.class forms, and defaults with parentheses,
    for both headerAs and exchangePropertyAs; all three fail on the old
    patterns.
    
    Co-Authored-By: Claude Fable 5.1 <[email protected]>
    (cherry picked from commit ae61f2b5d3f86857132742227350d711a49ede16)
---
 .../apache/camel/language/joor/JoorCompiler.java   | 26 ++++++++++-----
 .../camel/language/joor/JoorLanguageTest.java      | 38 ++++++++++++++++++++++
 2 files changed, 55 insertions(+), 9 deletions(-)

diff --git 
a/components/camel-joor/src/main/java/org/apache/camel/language/joor/JoorCompiler.java
 
b/components/camel-joor/src/main/java/org/apache/camel/language/joor/JoorCompiler.java
index dbdf37e4aa79..4a9513bb0193 100644
--- 
a/components/camel-joor/src/main/java/org/apache/camel/language/joor/JoorCompiler.java
+++ 
b/components/camel-joor/src/main/java/org/apache/camel/language/joor/JoorCompiler.java
@@ -38,29 +38,37 @@ import org.slf4j.LoggerFactory;
 
 public class JoorCompiler extends ServiceSupport implements StaticService {
 
+    // quoted name: 'foo' or "foo"
+    private static final String QUOTED_NAME = "(['\"][A-Za-z0-9.$]*['\"])";
+    // default value: anything but parentheses, or a single level of balanced 
parentheses (eg new Date(), '(none)'),
+    // so a default value can never span from one headerAs/exchangePropertyAs 
call into the next one on the same line
+    private static final String DEFAULT_VALUE = "((?:[^()]|\\([^()]*\\))+?)";
+    private static final String TYPE_CLASS = "([A-Za-z0-9.$]*\\.class)";
+    private static final String TYPE_NO_CLASS = "([A-Za-z0-9.$]*)";
+
     private static final Pattern BEAN_INJECTION_PATTERN = 
Pattern.compile("(#bean:)([A-Za-z0-9-_]*)");
-    private static final Pattern BODY_AS_PATTERN = 
Pattern.compile("(optionalBodyAs|bodyAs)\\(([A-Za-z0-9.$]*)(.class)\\)");
+    private static final Pattern BODY_AS_PATTERN = 
Pattern.compile("(optionalBodyAs|bodyAs)\\(([A-Za-z0-9.$]*)(\\.class)\\)");
     private static final Pattern BODY_AS_PATTERN_NO_CLASS = 
Pattern.compile("(optionalBodyAs|bodyAs)\\(([A-Za-z0-9.$]*)\\)");
     private static final Pattern HEADER_AS_PATTERN
-            = 
Pattern.compile("(optionalHeaderAs|headerAs)\\((['|\"][A-Za-z0-9.$]*['|\"]\\s*),\\s*([A-Za-z0-9.$]*.class)\\)");
+            = Pattern.compile("(optionalHeaderAs|headerAs)\\(" + QUOTED_NAME + 
"\\s*,\\s*" + TYPE_CLASS + "\\)");
     private static final Pattern HEADER_AS_PATTERN_NO_CLASS
-            = 
Pattern.compile("(optionalHeaderAs|headerAs)\\((['|\"][A-Za-z0-9.$]*['|\"])\\s*,\\s*([A-Za-z0-9.$]*)\\)");
+            = Pattern.compile("(optionalHeaderAs|headerAs)\\(" + QUOTED_NAME + 
"\\s*,\\s*" + TYPE_NO_CLASS + "\\)");
     private static final Pattern HEADER_AS_DEFAULT_VALUE_PATTERN
-            = 
Pattern.compile("(headerAs)\\((['|\"][A-Za-z0-9.$]*['|\"])\\s*,(.+),\\s*([A-Za-z0-9.$]*.class)\\)");
+            = Pattern.compile("(headerAs)\\(" + QUOTED_NAME + "\\s*," + 
DEFAULT_VALUE + ",\\s*" + TYPE_CLASS + "\\)");
     private static final Pattern HEADER_AS_DEFAULT_VALUE_PATTERN_NO_CLASS
-            = 
Pattern.compile("(headerAs)\\((['|\"][A-Za-z0-9.$]*['|\"])\\s*,(.+),\\s*([A-Za-z0-9.$]*)\\)");
+            = Pattern.compile("(headerAs)\\(" + QUOTED_NAME + "\\s*," + 
DEFAULT_VALUE + ",\\s*" + TYPE_NO_CLASS + "\\)");
     private static final Pattern EXCHANGE_PROPERTY_AS_PATTERN
             = Pattern.compile(
-                    
"(optionalExchangePropertyAs|exchangePropertyAs)\\((['|\"][A-Za-z0-9.$]*['|\"])\\s*,\\s*([A-Za-z0-9.$]*.class)\\)");
+                    "(optionalExchangePropertyAs|exchangePropertyAs)\\(" + 
QUOTED_NAME + "\\s*,\\s*" + TYPE_CLASS + "\\)");
     private static final Pattern EXCHANGE_PROPERTY_AS_PATTERN_NO_CLASS
             = Pattern.compile(
-                    
"(optionalExchangePropertyAs|exchangePropertyAs)\\((['|\"][A-Za-z0-9.$]*['|\"])\\s*,\\s*([A-Za-z0-9.$]*)\\)");
+                    "(optionalExchangePropertyAs|exchangePropertyAs)\\(" + 
QUOTED_NAME + "\\s*,\\s*" + TYPE_NO_CLASS + "\\)");
     private static final Pattern EXCHANGE_PROPERTY_AS_DEFAULT_VALUE_PATTERN
             = Pattern.compile(
-                    
"(exchangePropertyAs)\\((['|\"][A-Za-z0-9.$]*['|\"])\\s*,(.+),\\s*([A-Za-z0-9.$]*.class)\\)");
+                    "(exchangePropertyAs)\\(" + QUOTED_NAME + "\\s*," + 
DEFAULT_VALUE + ",\\s*" + TYPE_CLASS + "\\)");
     private static final Pattern 
EXCHANGE_PROPERTY_AS_DEFAULT_VALUE_PATTERN_NO_CLASS
             = Pattern.compile(
-                    
"(exchangePropertyAs)\\((['|\"][A-Za-z0-9.$]*['|\"])\\s*,(.+),\\s*([A-Za-z0-9.$]*)\\)");
+                    "(exchangePropertyAs)\\(" + QUOTED_NAME + "\\s*," + 
DEFAULT_VALUE + ",\\s*" + TYPE_NO_CLASS + "\\)");
 
     private static final Logger LOG = 
LoggerFactory.getLogger(JoorCompiler.class);
     private static final AtomicInteger UUID = new AtomicInteger();
diff --git 
a/components/camel-joor/src/test/java/org/apache/camel/language/joor/JoorLanguageTest.java
 
b/components/camel-joor/src/test/java/org/apache/camel/language/joor/JoorLanguageTest.java
index c1807a79339e..cbf08d8c6b74 100644
--- 
a/components/camel-joor/src/test/java/org/apache/camel/language/joor/JoorLanguageTest.java
+++ 
b/components/camel-joor/src/test/java/org/apache/camel/language/joor/JoorLanguageTest.java
@@ -158,6 +158,34 @@ public class JoorLanguageTest extends LanguageTestSupport {
         assertExpression("'Hello ' + headerAs('dog', 'World', String)", "Hello 
World");
     }
 
+    @Test
+    public void testExchangeHeaderAsTwoCallsInOneExpression() {
+        exchange.getIn().setHeader("amount", 42);
+        exchange.getIn().setHeader("vip", true);
+
+        assertExpression("'order-' + headerAs('amount', Integer) + '-' + 
headerAs('vip', Boolean)", "order-42-true");
+        assertExpression("'order-' + headerAs('amount', Integer.class) + '-' + 
headerAs('vip', Boolean.class)",
+                "order-42-true");
+        assertExpression("'order-' + headerAs('amount', int) + '-' + 
headerAs('vip', Boolean.class)", "order-42-true");
+        assertExpression("headerAs('amount', Integer) + headerAs('amount', 
Integer)", "84");
+    }
+
+    @Test
+    public void testExchangeHeaderAsDefaultValueTwoCallsInOneExpression() {
+        exchange.getIn().setHeader("a", 40);
+
+        assertExpression("headerAs('a', 1, Integer) + headerAs('b', 2, 
Integer)", "42");
+        assertExpression("headerAs('a', 1, Integer.class) + headerAs('b', 2, 
Integer.class)", "42");
+        assertExpression("headerAs('a', 1, Integer) + headerAs('b', 2, 
Integer.class)", "42");
+        assertExpression("headerAs('a', 1, Integer.class) + headerAs('b', 2, 
Integer)", "42");
+        // two-argument and three-argument forms mixed on the same line
+        assertExpression("headerAs('a', Integer) + headerAs('b', 2, Integer)", 
"42");
+        assertExpression("headerAs('b', 2, Integer) + headerAs('a', Integer)", 
"42");
+        // default values containing parentheses and quotes
+        assertExpression("headerAs('x', '(none)', String) + headerAs('y', 
\"(n/a)\", String)", "(none)(n/a)");
+        assertExpression("headerAs('b', Integer.valueOf(2), Integer) + 
headerAs('a', Integer)", "42");
+    }
+
     @Test
     public void testExchangeOptionalHeaderAs() {
         exchange.getIn().setHeader("foo", 22);
@@ -179,6 +207,16 @@ public class JoorLanguageTest extends LanguageTestSupport {
         assertExpression("var num = optionalHeaderAs(\"foo\", int).get(); 
return num * 4", "88");
     }
 
+    @Test
+    public void testExchangePropertyAsTwoCallsInOneExpression() {
+        exchange.setProperty("a", 40);
+
+        assertExpression("exchangePropertyAs('a', Integer) + 
exchangePropertyAs('a', Integer.class)", "80");
+        assertExpression("exchangePropertyAs('a', 1, Integer) + 
exchangePropertyAs('b', 2, Integer)", "42");
+        assertExpression("exchangePropertyAs('a', 1, Integer.class) + 
exchangePropertyAs('b', 2, Integer.class)", "42");
+        assertExpression("exchangePropertyAs('a', Integer) + 
exchangePropertyAs('b', 2, Integer)", "42");
+    }
+
     @Test
     public void testExchangePropertyAs() {
         exchange.setProperty("bar", 22);

Reply via email to