You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2026/09/24 06:22:50 UTC
(camel) 01/01: CAMEL-24983: simple - say what to write when ! is used to negate a function
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch fix/CAMEL-24983
in repository https://gitbox.apache.org/repos/asf/camel.git
commit defc344dc6fec7987b20e5752634d273bcdc5045
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Thu Sep 24 08:17:17 2026 +0200
CAMEL-24983: simple - say what to write when ! is used to negate a function
Simple has no boolean ! prefix, and a negated function failed with a
message that pointed at the operator instead of at the !:
${body != null && !${body.isEmpty()}}
-> Logical operator && needs a predicate on the right hand side,
e.g. ${header.foo} == 'bar'; was: !${body.isEmpty()}
The nesting is not the problem - ${body != null && ${body.isEmpty()}}
evaluates fine - and the plain ${body != null && !body.isEmpty()} fails
the same way. The message now names the ! and the comparison to write:
! does not negate a function: compare it instead, so !${body.isEmpty()}
is written as ${body.isEmpty()} == false, or negate the operator
(!=, !contains)
A negated operator is left alone, so ${body} !contains 'x' is unaffected.
Co-Authored-By: Claude Opus 5 (1M context) <no...@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bp3538HRBPMQkb5ta9xRaj
---
.../camel/language/simple/SimpleSyntaxHints.java | 22 +++++++++++++++++
.../language/simple/SimpleSyntaxHintsTest.java | 28 ++++++++++++++++++++++
2 files changed, 50 insertions(+)
diff --git 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
index 9b80f36a0e8b..ffd5f4710811 100644
--- 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
@@ -103,6 +103,23 @@ public final class SimpleSyntaxHints {
return expression.substring(start, end);
}
+ /**
+ * The comparison that a negated function is written as, or null when the text is not one: simple has no boolean
+ * {@code !} prefix, so {@code !${body.isEmpty()}} is {@code ${body.isEmpty()} == false}. Models and people write
+ * the Java form, and the parser otherwise reports it as a missing predicate next to the operator.
+ */
+ private static String negatedFunction(String word) {
+ if (word.length() < 2 || word.charAt(0) != '!') {
+ return null;
+ }
+ String rest = word.substring(1);
+ // !=, !contains and the other negated operators are words of their own, not a negated function
+ if (rest.isEmpty() || rest.charAt(0) == '=' || Character.isLetter(rest.charAt(0)) && !rest.contains("(")) {
+ return null;
+ }
+ return rest + " == false";
+ }
+
/** The message for a token the grammar does not know at the given index. */
public static String unexpectedToken(String expression, int index) {
String word = wordAt(expression, index);
@@ -145,6 +162,11 @@ public final class SimpleSyntaxHints {
/** The message when an operator has no usable value next to it. */
public static String unsupportedOperand(String kind, Object operator, String expression, int index) {
String word = wordAt(expression, index);
+ String compared = negatedFunction(word);
+ if (compared != null) {
+ return "! does not negate a function: compare it instead, so " + word + " is written as " + compared
+ + ", or negate the operator (!=, !contains)";
+ }
if ("Logical".equals(kind)) {
return kind + " operator " + operator + " needs a predicate on the right hand side, e.g. ${header.foo} == 'bar'"
+ (word.isEmpty() ? "" : "; was: " + word);
diff --git a/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleSyntaxHintsTest.java b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleSyntaxHintsTest.java
index 55de40810c70..ea81f6c87e83 100644
--- a/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleSyntaxHintsTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleSyntaxHintsTest.java
@@ -17,6 +17,7 @@
package org.apache.camel.language.simple;
import org.apache.camel.ExchangeTestSupport;
+import org.apache.camel.Expression;
import org.apache.camel.language.simple.types.SimpleIllegalSyntaxException;
import org.junit.jupiter.api.Test;
@@ -35,6 +36,18 @@ public class SimpleSyntaxHintsTest extends ExchangeTestSupport {
return assertThrows(SimpleIllegalSyntaxException.class, parser::parsePredicate).getMessage();
}
+ /**
+ * The message of a function that only fails when the node builds its expression, such as one with operators inside
+ * the {@code ${ }} (CAMEL-24921), which the parsers above accept as a single function token.
+ */
+ private String functionError(String text) {
+ return assertThrows(SimpleIllegalSyntaxException.class, () -> {
+ Expression exp = context.resolveLanguage("simple").createExpression(text);
+ exp.init(context);
+ exp.evaluate(exchange, Object.class);
+ }).getMessage();
+ }
+
private String expressionError(String text) {
SimpleExpressionParser parser = new SimpleExpressionParser(context, text, true, null);
return assertThrows(SimpleIllegalSyntaxException.class, parser::parseExpression).getMessage();
@@ -77,6 +90,21 @@ public class SimpleSyntaxHintsTest extends ExchangeTestSupport {
assertThat(predicateError("${body} == 'x' || ")).contains("needs a predicate on the right hand side");
}
+ @Test
+ public void testNegatedFunction() {
+ // simple has no boolean ! prefix; both the nested and the plain form fail the same way, and the message
+ // says the comparison to write instead (the shapes a local model wrote in the benchmark)
+ assertThat(functionError("${body != null && !${body.isEmpty()}}"))
+ .contains("! does not negate a function")
+ .contains("${body.isEmpty()} == false")
+ .doesNotContain("needs a predicate on the right hand side");
+ assertThat(functionError("${body != null && !body.isEmpty()}"))
+ .contains("! does not negate a function")
+ .contains("body.isEmpty() == false");
+ // a negated operator is not a negated function
+ assertThat(predicateError("${body} !contains")).doesNotContain("does not negate a function");
+ }
+
@Test
public void testQuotes() {
assertThat(predicateError("${body} == 'it''s'")).contains("double quotes");