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

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

commit ccd0272ce0882b9e176ebd12df5277f9602caed0
Merge: 493f25117b3c 8e13c665d8e6
Author: Claus Ibsen <[email protected]>
AuthorDate: Wed Sep 23 11:45:07 2026 +0200

    Merge fix/CAMEL-24920 into fix/CAMEL-24921

 .../main/java/org/apache/camel/language/simple/SimpleSyntaxHints.java | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --cc 
core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleSyntaxHints.java
index 389ae91d176c,f56601ce9c2d..171a3258aa09
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleSyntaxHints.java
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleSyntaxHints.java
@@@ -149,95 -149,6 +149,93 @@@ public final class SimpleSyntaxHints 
                 + VALUE_FORMS + (isKnownFunction(functionName(word)) ? ", e.g. 
${" + word + "}" : ", e.g. '" + word + "'");
      }
  
 +    /** The comparison operators, with the spaces they must be surrounded by. 
*/
 +    private static final String[] SPACED_OPERATORS = {
 +            " >= ", " <= ", " > ", " < ", " == ", " != ", " =~ ", " !=~ ",
 +            " contains ", " !contains ", " ~~ ", " !~~ ", " regex ", " !regex 
",
 +            " in ", " !in ", " is ", " !is ", " range ", " !range ",
 +            " startsWith ", " !startsWith ", " endsWith ", " !endsWith " };
 +
 +    /**
 +     * Wraps the function references of a predicate written inside {@code ${ 
}} so that it can be parsed as one:
 +     * {@code body != null && body.size() > 0} becomes {@code ${body} != null 
&& ${body.size()} > 0}.
 +     * <p/>
 +     * Each comparison is wrapped, not only the first, so that a compound 
condition reads the way it looks (CAMEL-24920,
 +     * CAMEL-24921). An operator counts only when whitespace surrounds it 
outside quotes, which is what keeps
 +     * {@code ${header.Content-Length}} and {@code ${date:now:yyyy-MM-dd}} a 
plain function.
 +     */
 +    public static String wrapFunctions(String text) {
 +        StringBuilder answer = new StringBuilder();
 +        int from = 0;
 +        for (int at = logicalOperator(text, 0); at >= 0; at = 
logicalOperator(text, from)) {
++            // the operator matched with its trailing space, so the next 
space is at most two characters away
 +            int end = text.indexOf(' ', at + 1);
-             if (end < 0) {
-                 break; // the operator has nothing after it: leave it to the 
parser to say so
-             }
 +            answer.append(wrapComparison(text.substring(from, at).trim()));
 +            answer.append(' ').append(text, at, end).append(' ');
 +            from = end + 1;
 +        }
 +        answer.append(wrapComparison(text.substring(from).trim()));
 +        return answer.toString();
 +    }
 +
 +    /**
 +     * The index of the next logical operator ({@code &&} or {@code ||}) 
outside quotes, or -1. Simple has no word
 +     * forms: {@code and} and {@code or} are refused by the parser with a 
message that says so.
 +     */
 +    private static int logicalOperator(String text, int from) {
 +        boolean single = false;
 +        boolean dubble = false;
 +        for (int i = from; i < text.length(); i++) {
 +            char c = text.charAt(i);
 +            if (c == '\'' && !dubble) {
 +                single = !single;
 +            } else if (c == '"' && !single) {
 +                dubble = !dubble;
 +            } else if (!single && !dubble && c == ' ') {
 +                for (String op : new String[] { "&& ", "|| " }) {
 +                    if (text.startsWith(op, i + 1)) {
 +                        return i + 1;
 +                    }
 +                }
 +            }
 +        }
 +        return -1;
 +    }
 +
 +    /** Wraps the left hand side of one comparison with {@code ${ }} when it 
is a function reference. */
 +    private static String wrapComparison(String text) {
 +        for (String op : SPACED_OPERATORS) {
 +            int at = text.indexOf(op);
 +            if (at < 0 && text.endsWith(op.stripTrailing())) {
 +                // the operator ends the text: wrap what is there, so the 
parser says what is missing after it
 +                at = text.length() - op.stripTrailing().length();
 +            }
 +            if (at > 0) {
 +                String left = text.substring(0, at).trim();
 +                String right = at + op.length() <= text.length() ? 
text.substring(at + op.length()).trim() : "";
 +                if (!left.startsWith("${") && !left.startsWith("'") && 
!left.startsWith("\"")
 +                        && !isNumeric(left) && !"true".equalsIgnoreCase(left)
 +                        && !"false".equalsIgnoreCase(left) && 
!"null".equalsIgnoreCase(left)) {
 +                    left = "${" + left + "}";
 +                }
 +                return left + op + right;
 +            }
 +        }
 +        return text;
 +    }
 +
 +    private static boolean isNumeric(String text) {
 +        if (text == null || text.isEmpty()) {
 +            return false;
 +        }
 +        try {
 +            Double.parseDouble(text);
 +            return true;
 +        } catch (NumberFormatException e) {
 +            return false;
 +        }
 +    }
 +
      /**
       * When an operator is written inside the function (${body == 'x'}), the 
rewrite with the operator outside, else
       * null.

Reply via email to