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

davsclaus pushed a commit to branch fix/CAMEL-24692-tui
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 4916178aa310a96d1ff67f54ed0f1b133a010233
Author: Claus Ibsen <[email protected]>
AuthorDate: Sat Sep 12 10:04:57 2026 +0200

    CAMEL-24692: camel-jbang-plugin-tui - narrow the simple validation 
placeholder workaround
    
    The save-time validation skipped any simple expression that started with {{
    and ended with }}, to work around the catalog rejecting a property
    placeholder used as an operand. The catalog handles that now, so the
    workaround hid real errors in expressions such as "{{a}} =!= {{b}}".
    
    One case remains that the catalog cannot validate: a placeholder used as an
    operand of a logical operator, as in "{{enabled}} && ${body} > 1". A
    placeholder can expand to an entire predicate, and the catalog substitutes a
    dummy value, which a logical operator does not accept on either side. 
Replace
    the old workaround with a check for exactly that shape.
    
    This is narrower than before, so expressions that are merely placeholder
    valued are now validated, and it also covers the case the old workaround
    missed, where the placeholder is not at the start of the expression.
    
    Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../jbang/core/commands/tui/SourceEditAssist.java  | 53 +++++++++++++++++++++-
 .../commands/tui/SourceEditAssistValidateTest.java | 44 ++++++++++++++++++
 2 files changed, 95 insertions(+), 2 deletions(-)

diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceEditAssist.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceEditAssist.java
index a767e0f0bfe0..ccd16eca885d 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceEditAssist.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceEditAssist.java
@@ -1617,8 +1617,8 @@ final class SourceEditAssist {
             if (simpleText == null || simpleText.isEmpty()) {
                 continue;
             }
-            // Skip placeholder-only expressions
-            if (simpleText.startsWith("{{") && simpleText.endsWith("}}")) {
+            // Skip what the catalog cannot validate because a placeholder is 
unresolved
+            if (hasPlaceholderAsLogicalOperand(simpleText)) {
                 continue;
             }
 
@@ -1646,6 +1646,55 @@ final class SourceEditAssist {
         return errors;
     }
 
+    /**
+     * Whether the text uses a property placeholder as an operand of a logical 
operator, such as
+     * <tt>{{enabled}} && ${body} > 10</tt>.
+     *
+     * A placeholder can expand to an entire predicate, which the catalog 
cannot know as it validates without a running
+     * Camel application. The catalog substitutes a placeholder with a dummy 
value, which is what an operand of a binary
+     * operator needs, but a logical operator needs a predicate on either 
side. Validating those would report an error
+     * for a route that is perfectly valid at runtime, so they are skipped.
+     */
+    static boolean hasPlaceholderAsLogicalOperand(String text) {
+        if (text == null || !text.contains("{{")) {
+            return false;
+        }
+
+        // split into the operands of the logical operators, ignoring any 
quoted literal
+        List<String> operands = new ArrayList<>();
+        char quote = 0;
+        int start = 0;
+        for (int i = 0; i < text.length(); i++) {
+            char ch = text.charAt(i);
+            if (quote == 0 && (ch == '\'' || ch == '"')) {
+                quote = ch;
+            } else if (quote == ch) {
+                quote = 0;
+            } else if (quote == 0 && i < text.length() - 1) {
+                char next = text.charAt(i + 1);
+                if (ch == '&' && next == '&' || ch == '|' && next == '|') {
+                    operands.add(text.substring(start, i));
+                    i++;
+                    start = i + 1;
+                }
+            }
+        }
+        if (operands.isEmpty()) {
+            // no logical operator so the placeholders are all used as a value 
which the catalog can validate
+            return false;
+        }
+        operands.add(text.substring(start));
+
+        for (String operand : operands) {
+            String s = operand.trim();
+            // is the operand nothing but a single placeholder
+            if (s.startsWith("{{") && s.endsWith("}}") && s.indexOf("}}") == 
s.length() - 2) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     static String findParentEip(String[] lines, int lineIdx, int lineIndent) {
         for (int j = lineIdx - 1; j >= 0; j--) {
             String prev = lines[j];
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceEditAssistValidateTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceEditAssistValidateTest.java
index 78f1a847ae31..0875a793a538 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceEditAssistValidateTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceEditAssistValidateTest.java
@@ -80,4 +80,48 @@ class SourceEditAssistValidateTest {
         assertTrue(SourceEditAssist.isValidatableFile("routes.YAML"));
         assertFalse(SourceEditAssist.isValidatableFile("Foo.java"));
     }
+
+    @Test
+    void placeholderAsOperandIsValidated() {
+        // CAMEL-24692: the catalog can validate these now, so they must no 
longer be skipped
+        
assertFalse(SourceEditAssist.hasPlaceholderAsLogicalOperand("{{hot.threshold}}"));
+        assertFalse(SourceEditAssist.hasPlaceholderAsLogicalOperand("${body} 
>= {{hot.threshold}}"));
+        assertFalse(SourceEditAssist.hasPlaceholderAsLogicalOperand("{{a}} == 
{{b}}"));
+        assertFalse(SourceEditAssist.hasPlaceholderAsLogicalOperand("${body} 
>= {{t}} && ${body} < {{u}}"));
+        assertFalse(SourceEditAssist.hasPlaceholderAsLogicalOperand("${body} 
== 'abc'"));
+        assertFalse(SourceEditAssist.hasPlaceholderAsLogicalOperand(null));
+    }
+
+    @Test
+    void placeholderAsLogicalOperandIsSkipped() {
+        // a placeholder can expand to an entire predicate which the catalog 
cannot know
+        assertTrue(SourceEditAssist.hasPlaceholderAsLogicalOperand("{{a}} && 
{{b}}"));
+        assertTrue(SourceEditAssist.hasPlaceholderAsLogicalOperand("${body} > 
1 && {{flag}}"));
+        assertTrue(SourceEditAssist.hasPlaceholderAsLogicalOperand("{{flag}} 
|| ${body} > 1"));
+        // a logical operator inside a quoted literal is not an operator
+        assertFalse(SourceEditAssist.hasPlaceholderAsLogicalOperand("${body} 
== '{{a}} && {{b}}'"));
+    }
+
+    @Test
+    void placeholderPredicateIsNotReportedAsError() {
+        List<String> errors = 
assist().validateSource("placeholder.camel.yaml", """
+                - route:
+                    id: placeholder
+                    from:
+                      uri: timer:tick
+                      steps:
+                        - choice:
+                            when:
+                              - simple: "${body} >= {{hot.threshold}}"
+                                steps:
+                                  - log:
+                                      message: "hot"
+                              - simple: "{{enabled}} && ${body} > 1"
+                                steps:
+                                  - log:
+                                      message: "on"
+                """);
+
+        assertTrue(errors.stream().noneMatch(e -> e.contains("Simple syntax 
error")), String.valueOf(errors));
+    }
 }

Reply via email to