Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1310,6 +1310,10 @@ public static Object setVariable(Exchange exchange, String name, Class<?> type,
return null;
}

public static boolean matchesValue(Exchange exchange, Object value) {
return ObjectHelper.evaluateValuePredicate(value);
}

public static boolean isNot(Exchange exchange, Object value) {
if (value == null) {
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,21 +48,32 @@ public String toString() {
}

public boolean acceptLeftNode(SimpleNode lef) {
if (!(lef instanceof BinaryExpression) && !(lef instanceof LogicalExpression)) {
if (!isValidPredicateOperand(lef)) {
return false;
}
this.left = lef;
return true;
}

public boolean acceptRightNode(SimpleNode right) {
if (!(right instanceof BinaryExpression) && !(right instanceof LogicalExpression)) {
if (!isValidPredicateOperand(right)) {
return false;
}
this.right = right;
return true;
}

/**
* Predicate operands for logical AND/OR include binary/logical expressions as well as boolean-zen shorthand
* (standalone functions such as {@code ${header.active}} or {@code ${exchangeProperty.flag}}) that are evaluated
* via {@link org.apache.camel.support.ExpressionToPredicateAdapter}.
*/
private static boolean isValidPredicateOperand(SimpleNode node) {
return node instanceof BinaryExpression
|| node instanceof LogicalExpression
|| node instanceof SimpleFunctionStart;
}

public LogicalOperatorType getOperator() {
return operator;
}
Expand Down Expand Up @@ -137,8 +148,8 @@ private String doCreateCode(CamelContext camelContext, String expression) throws
ObjectHelper.notNull(left, "left node", this);
ObjectHelper.notNull(right, "right node", this);

final String leftExp = left.createCode(camelContext, expression);
final String rightExp = right.createCode(camelContext, expression);
final String leftExp = predicateOperandCode(left, camelContext, expression);
final String rightExp = predicateOperandCode(right, camelContext, expression);

if (operator == LogicalOperatorType.AND) {
return leftExp + " && " + rightExp;
Expand All @@ -148,4 +159,15 @@ private String doCreateCode(CamelContext camelContext, String expression) throws

throw new SimpleParserException("Unknown logical operator " + operator, token.getIndex());
}

private static String predicateOperandCode(SimpleNode node, CamelContext camelContext, String expression)
throws SimpleParserException {
String code = node.createCode(camelContext, expression);
code = code.replace(BaseSimpleParser.CODE_START, "");
code = code.replace(BaseSimpleParser.CODE_END, "");
if (node instanceof SimpleFunctionStart) {
return "matchesValue(exchange, " + code + ")";
}
return code;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,13 @@ public void testParseExchangeProperty() {
"isNotEqualTo(exchange, exchangePropertyAs(exchange, \"foo\", com.foo.User.class).getName(), \"bar\")", code);
}

@Test
public void testParseBooleanZenLogicalOr() {
CSimplePredicateParser parser = new CSimplePredicateParser();
String code = parser.parsePredicate("${header.token} == null || ${exchangeProperty.forceNewSessionToken}");
Assertions.assertEquals(
"isEqualTo(exchange, header(message, \"token\"), null) || matchesValue(exchange, exchangeProperty(exchange, \"forceNewSessionToken\"))",
code);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,17 @@
import org.apache.camel.language.simple.types.SimpleIllegalSyntaxException;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
* Regression tests for prepareLogicalExpressions in SimplePredicateParser. Covers the bug where the right-hand token
* was reported as the left-hand token in the "does not support right hand side token" error message.
*/
public class SimplePredicateParserLogicalTest extends ExchangeTestSupport {
class SimplePredicateParserLogicalTest extends ExchangeTestSupport {

@Test
public void testAndWithFunctionRightHandSide() {
void testAndWithFunctionRightHandSide() {
exchange.getIn().setBody("hello");
exchange.getIn().setHeader("active", true);

Expand All @@ -41,23 +40,90 @@ public void testAndWithFunctionRightHandSide() {
Predicate predicate = parser.parsePredicate();
predicate.init(context);

assertTrue(predicate.matches(exchange));
assertThat(predicate.matches(exchange)).isTrue();
}

@Test
public void testAndWithLiteralRightHandSide() {
void testOrWithBooleanZenRightHandSide() {
// CAMEL-24376: boolean-zen shorthand must work as the right operand of ||
exchange.getIn().setHeader("token", null);
exchange.setProperty("forceNewSessionToken", true);

SimplePredicateParser parser = new SimplePredicateParser(
context, "${header.token} == null || ${exchangeProperty.forceNewSessionToken}", true, null);
Predicate predicate = parser.parsePredicate();
predicate.init(context);

assertThat(predicate.matches(exchange)).isTrue();
}

@Test
void testOrWithBooleanZenLeftHandSide() {
exchange.getIn().setHeader("active", true);
exchange.getIn().setBody("other");

SimplePredicateParser parser = new SimplePredicateParser(
context, "${header.active} || ${body} == 'hello'", true, null);
Predicate predicate = parser.parsePredicate();
predicate.init(context);

assertThat(predicate.matches(exchange)).isTrue();
}

@Test
void testAndWithBooleanZenOperands() {
exchange.getIn().setHeader("enabled", true);
exchange.setProperty("ready", true);

SimplePredicateParser parser = new SimplePredicateParser(
context, "${header.enabled} && ${exchangeProperty.ready}", true, null);
Predicate predicate = parser.parsePredicate();
predicate.init(context);

assertThat(predicate.matches(exchange)).isTrue();

exchange.setProperty("ready", false);
assertThat(predicate.matches(exchange)).isFalse();
}

@Test
void testOrWithBooleanZenFalseWhenBothOperandsFalse() {
exchange.getIn().setHeader("token", "abc");
exchange.setProperty("forceNewSessionToken", false);

SimplePredicateParser parser = new SimplePredicateParser(
context, "${header.token} == null || ${exchangeProperty.forceNewSessionToken}", true, null);
Predicate predicate = parser.parsePredicate();
predicate.init(context);

assertThat(predicate.matches(exchange)).isFalse();
}

@Test
void testStandaloneBooleanZenStillWorks() {
exchange.getIn().setHeader("foo", "yes");

SimplePredicateParser parser = new SimplePredicateParser(context, "${header.foo}", true, null);
Predicate predicate = parser.parsePredicate();
predicate.init(context);

assertThat(predicate.matches(exchange)).isTrue();
}

@Test
void testAndWithLiteralRightHandSide() {
exchange.getIn().setBody("foo");

SimplePredicateParser parser = new SimplePredicateParser(
context, "${body} == 'foo' && ${body} != 'bar'", true, null);
Predicate predicate = parser.parsePredicate();
predicate.init(context);

assertTrue(predicate.matches(exchange));
assertThat(predicate.matches(exchange)).isTrue();
}

@Test
public void testOrWithFunctionRightHandSide() {
void testOrWithFunctionRightHandSide() {
exchange.getIn().setBody("hello");
exchange.getIn().setHeader("score", 5);

Expand All @@ -66,11 +132,11 @@ public void testOrWithFunctionRightHandSide() {
Predicate predicate = parser.parsePredicate();
predicate.init(context);

assertTrue(predicate.matches(exchange));
assertThat(predicate.matches(exchange)).isTrue();
}

@Test
public void testOrAllFalse() {
void testOrAllFalse() {
exchange.getIn().setBody("hello");
exchange.getIn().setHeader("score", 1);

Expand All @@ -79,23 +145,23 @@ public void testOrAllFalse() {
Predicate predicate = parser.parsePredicate();
predicate.init(context);

assertFalse(predicate.matches(exchange));
assertThat(predicate.matches(exchange)).isFalse();
}

@Test
public void testAndWithNumericRightHandSide() {
void testAndWithNumericRightHandSide() {
exchange.getIn().setBody(42);

SimplePredicateParser parser = new SimplePredicateParser(
context, "${body} > 10 && ${body} < 100", true, null);
Predicate predicate = parser.parsePredicate();
predicate.init(context);

assertTrue(predicate.matches(exchange));
assertThat(predicate.matches(exchange)).isTrue();
}

@Test
public void testAndWithNullRightHandSide() {
void testAndWithNullRightHandSide() {
exchange.getIn().setBody("present");
exchange.getIn().setHeader("tag", "x");

Expand All @@ -104,11 +170,11 @@ public void testAndWithNullRightHandSide() {
Predicate predicate = parser.parsePredicate();
predicate.init(context);

assertTrue(predicate.matches(exchange));
assertThat(predicate.matches(exchange)).isTrue();
}

@Test
public void testChainedAndOrLogicalOperators() {
void testChainedAndOrLogicalOperators() {
exchange.getIn().setBody("alpha");
exchange.getIn().setHeader("flag", true);

Expand All @@ -117,20 +183,18 @@ public void testChainedAndOrLogicalOperators() {
Predicate predicate = parser.parsePredicate();
predicate.init(context);

assertTrue(predicate.matches(exchange));
assertThat(predicate.matches(exchange)).isTrue();
}

@Test
public void testInvalidRightHandSideReportsRightToken() {
void testInvalidRightHandSideReportsRightToken() {
// "&&" followed by a bare numeric (not a binary expression) is invalid syntax.
// The error message must say "right hand side token 42" (the actual offending token),
// not "right hand side token ==" (which would indicate the left-hand node was reported).
SimplePredicateParser parser = new SimplePredicateParser(
context, "${body} == 'foo' && 42", true, null);
SimpleIllegalSyntaxException ex = assertThrows(SimpleIllegalSyntaxException.class, parser::parsePredicate);
assertTrue(ex.getMessage().contains("right hand side token 42"),
"Error message should say 'right hand side token 42', but was: " + ex.getMessage());
assertFalse(ex.getMessage().contains("right hand side token =="),
"Error message must not say 'right hand side token ==', but was: " + ex.getMessage());
assertThat(ex.getMessage()).contains("right hand side token 42");
assertThat(ex.getMessage()).doesNotContain("right hand side token ==");
}
}