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 c10d7d6314a4 CAMEL-26384: camel-jbang - AI tools: a resource: in both 
values of a Simple ternary goes before the function, and the list item column 
message quotes the lines (#28634)
c10d7d6314a4 is described below

commit c10d7d6314a436cb1a21716fba2f04b9115444c6
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Oct 29 07:10:27 2026 +0100

    CAMEL-26384: camel-jbang - AI tools: a resource: in both values of a Simple 
ternary goes before the function, and the list item column message quotes the 
lines (#28634)
    
    - A top-level ternary whose two values start with the same resource: and 
scheme is rewritten with that prefix before the function.
    - The list item column message quotes the item's line and the list's first 
line as written (cut at 120 characters); the lenient repair reads the quoted 
form.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../dsl/jbang/core/commands/ai/LenientRepairs.java |  5 ++--
 .../dsl/jbang/core/commands/ai/SimpleChecks.java   | 23 ++++++++++++--
 .../commands/ai/SourceValidatorSimpleTest.java     | 13 ++++++++
 .../camel/dsl/yaml/validator/YamlValidator.java    | 21 +++++++++++--
 .../validator/YamlValidatorPropertyHintTest.java   | 35 +++++++++++++++++-----
 5 files changed, 81 insertions(+), 16 deletions(-)

diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/LenientRepairs.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/LenientRepairs.java
index 6d640fb0869a..631b162a175f 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/LenientRepairs.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/LenientRepairs.java
@@ -77,9 +77,10 @@ final class LenientRepairs {
             "^line (\\d+): .* without quotes is read as a YAML map .*write the 
line as (.+)$");
     private static final Pattern BRACES = Pattern.compile(
             "^Line (\\d+): .*\\((\\{[^}]*\\}) without quotes is a YAML map");
+    // the validator quotes each line after its number: line 11 ("    - 
log:"): ...
     private static final Pattern ITEM_COLUMN = Pattern.compile(
-            "^line (\\d+): this list item starts in column (\\d+), but the 
list that starts at line \\d+ has its items in"
-                                                               + " column 
(\\d+)");
+            "^line (\\d+)(?: \\(\".*?\"\\))?: this list item starts in column 
(\\d+), but the list that starts at line"
+                                                               + " \\d+(?: 
\\(\".*?\"\\))? has its items in column (\\d+)");
     private static final Pattern KEY_COLUMN = Pattern.compile(
             "^line (\\d+): (\\S+) starts in column (\\d+), but the keys of the 
mapping that starts at line \\d+ are in"
                                                               + " column 
(\\d+)");
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/SimpleChecks.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/SimpleChecks.java
index 0d40463c873c..57cd4a9fa52c 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/SimpleChecks.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/SimpleChecks.java
@@ -210,7 +210,9 @@ final class SimpleChecks {
      * function the condition starts with is opened before the condition and 
closed after the second value, and a value
      * written as bare text is quoted. 
<tt>resource:file:${exchangeProperty.CamelTimerCounter} == 1 ? order.json :
      * order-bad-email.json</tt> is 
<tt>resource:file:${exchangeProperty.CamelTimerCounter == 1 ? 'order.json' :
-     * 'order-bad-email.json'}</tt>.
+     * 'order-bad-email.json'}</tt>, and so is
+     * <tt>${exchangeProperty.CamelTimerCounter} == 1 ? 
resource:file:order.json :
+     * resource:file:order-bad-email.json</tt>.
      */
     static String ternaryInsideOneFunction(String text, int question, int 
colon) {
         // the functions at the top level before the ?, as {start, end} with 
end the index of the closing brace
@@ -238,10 +240,25 @@ final class SimpleChecks {
         int start = functions.get(index)[0];
         int end = functions.get(index)[1];
         String condition = text.substring(start + 2, end) + text.substring(end 
+ 1, question);
-        return text.substring(0, start) + "${" + condition + " ? " + 
quoted(text.substring(question + 3, colon).trim())
-               + " : " + quoted(text.substring(colon + 3).trim()) + "}";
+        String prefix = text.substring(0, start);
+        String first = text.substring(question + 3, colon).trim();
+        String second = text.substring(colon + 3).trim();
+        // resource: is read only at the start of the expression: inside the 
function, 'resource:file:order.json' is the
+        // text the body becomes, so a resource: both values start with goes 
before the function (the local-model
+        // benchmark: the body was the text resource:file:order.json, and the 
next step failed to parse it as JSON)
+        Matcher scheme = RESOURCE_SCHEME.matcher(first);
+        if (prefix.isEmpty() && scheme.lookingAt() && 
second.startsWith(scheme.group())
+                && first.length() > scheme.end() && second.length() > 
scheme.end()) {
+            prefix = scheme.group();
+            first = first.substring(scheme.end());
+            second = second.substring(scheme.end());
+        }
+        return prefix + "${" + condition + " ? " + quoted(first) + " : " + 
quoted(second) + "}";
     }
 
+    /** The resource: and its scheme a value starts with, such as 
<tt>resource:file:</tt>. */
+    private static final Pattern RESOURCE_SCHEME = 
Pattern.compile("resource:[a-z]+:");
+
     /** The index of the brace that closes the function opened at the index, 
or -1. */
     private static int closingBrace(String text, int open) {
         char quote = 0;
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidatorSimpleTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidatorSimpleTest.java
index 593d5058f278..290b5f6cae01 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidatorSimpleTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidatorSimpleTest.java
@@ -501,6 +501,19 @@ class SourceValidatorSimpleTest {
                 .endsWith("resource:file:${exchangeProperty.CamelTimerCounter 
== 1 ? 'order.json' : 'order-bad-email.json'}");
     }
 
+    @Test
+    void aResourceInBothValuesOfATopLevelTernaryGoesBeforeTheFunction() {
+        // inside the function, 'resource:file:order.json' is only text: the 
body would be that text, not the file
+        assertThat(rewritten(
+                "${exchangeProperty.CamelTimerCounter} == 1 ? 
resource:file:order.json : resource:file:order-bad-email.json"))
+                .isEqualTo("resource:file:${exchangeProperty.CamelTimerCounter 
== 1 ? 'order.json' : 'order-bad-email.json'}");
+        // a value that is not a resource of the same scheme stays as it is
+        assertThat(rewritten("${header.a} == 1 ? resource:file:order.json : 
resource:classpath:order.json"))
+                .isEqualTo("${header.a == 1 ? 'resource:file:order.json' : 
'resource:classpath:order.json'}");
+        assertThat(rewritten("${header.a} == 1 ? resource:file:order.json : 
none"))
+                .isEqualTo("${header.a == 1 ? 'resource:file:order.json' : 
'none'}");
+    }
+
     @Test
     void aResourceLocationWithoutATopLevelTernaryIsAccepted() {
         List<String> msgs = SourceValidator.validateYamlSimple(
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/YamlValidator.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/YamlValidator.java
index 06455137980b..cd1d75848cc6 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/YamlValidator.java
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/YamlValidator.java
@@ -485,10 +485,25 @@ public class YamlValidator {
             }
         }
         Mark list = deeper != null ? deeper : shallower;
+        // the two lines as written: with the columns alone, a local model 
sent the same indentation again up to 18 times
         String belongs = list == null
-                ? "" : ", but the list that starts at line " + list.line() + " 
has its items in column " + list.column();
-        return hint("line " + problem.line() + ": this list item starts in 
column " + problem.column() + belongs
-                    + "; every item of a list must start in the same column");
+                ? ""
+                : ", but the list that starts at line " + list.line() + 
quotedLine(lines, list.line())
+                  + " has its items in column " + list.column();
+        return hint("line " + problem.line() + quotedLine(lines, 
problem.line()) + ": this list item starts in column "
+                    + problem.column() + belongs + "; every item of a list 
must start in the same column");
+    }
+
+    /**
+     * The line as written, quoted after its number, or nothing when the line 
is not in the text. A long line (a JSON
+     * body, a long expression) is cut at 120 characters, as the indentation 
the hint is about is at its start.
+     */
+    private static String quotedLine(String[] lines, int line) {
+        if (line < 1 || line > lines.length || lines[line - 1].isBlank()) {
+            return "";
+        }
+        String text = lines[line - 1].stripTrailing();
+        return " (\"" + (text.length() > 120 ? text.substring(0, 120) + "..." 
: text) + "\")";
     }
 
     /**
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlValidatorPropertyHintTest.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlValidatorPropertyHintTest.java
index 68807cc68354..64540c5c9e82 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlValidatorPropertyHintTest.java
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlValidatorPropertyHintTest.java
@@ -1142,12 +1142,31 @@ public class YamlValidatorPropertyHintTest {
                 """);
         assertThat(errors).hasSize(1);
         assertThat(errors.get(0).getMessage())
-                .startsWith("line 11: this list item starts in column 13")
-                .contains("the list that starts at line 7 has its items in 
column 15")
+                .startsWith("line 11 (\"            - simple: \"${body} > 
2\"\"): this list item starts in column 13")
+                .contains(
+                        "the list that starts at line 7 (\"              - 
simple: \"${body} > 1\"\") has its items in column 15")
                 .contains("every item of a list must start in the same column")
                 .doesNotContain("block end");
     }
 
+    /** CAMEL-26384: a long line is quoted only up to 120 characters, so the 
hint does not grow with the value. */
+    @Test
+    void aLongListItemLineIsCutInTheHint() throws Exception {
+        List<Error> errors = new YamlValidator().validate("""
+                - route:
+                    from:
+                      uri: timer:tick
+                      steps:
+                        - log:
+                            message: a
+                          - log: "%s"
+                """.formatted("x".repeat(200)));
+        assertThat(errors).hasSize(1);
+        assertThat(errors.get(0).getMessage())
+                .startsWith("line 7 (\"          - log: 
\"xxx").contains("...\"): this list item starts in column 11")
+                .doesNotContain("x".repeat(120));
+    }
+
     /** CAMEL-24837: the item is over-indented, so the list it belongs to is 
the shallower one above it. */
     @Test
     void anOverIndentedListItemNamesTheListAboveIt() throws Exception {
@@ -1163,8 +1182,8 @@ public class YamlValidatorPropertyHintTest {
                 """);
         assertThat(errors).hasSize(1);
         assertThat(errors.get(0).getMessage())
-                .startsWith("line 7: this list item starts in column 11")
-                .contains("the list that starts at line 5 has its items in 
column 9");
+                .startsWith("line 7 (\"").contains("\"): this list item starts 
in column 11")
+                .contains("the list that starts at line 5 (\"").contains("\") 
has its items in column 9");
     }
 
     /**
@@ -1293,8 +1312,8 @@ public class YamlValidatorPropertyHintTest {
                 """);
         assertThat(errors).hasSize(1);
         assertThat(errors.get(0).getMessage())
-                .startsWith("line 12: this list item starts in column 13")
-                .contains("the list that starts at line 7 has its items in 
column 15");
+                .startsWith("line 12 (\"").contains("\"): this list item 
starts in column 13")
+                .contains("the list that starts at line 7 (\"").contains("\") 
has its items in column 15");
     }
 
     /**
@@ -1316,8 +1335,8 @@ public class YamlValidatorPropertyHintTest {
                 """);
         assertThat(errors).hasSize(1);
         assertThat(errors.get(0).getMessage())
-                .startsWith("line 8: this list item starts in column 10")
-                .contains("the list that starts at line 5 has its items in 
column 9")
+                .startsWith("line 8 (\"").contains("\"): this list item starts 
in column 10")
+                .contains("the list that starts at line 5 (\"").contains("\") 
has its items in column 9")
                 .doesNotContain("block sequence start");
     }
 

Reply via email to