Author: davsclaus Date: Tue Aug 24 18:18:41 2010 New Revision: 988647 URL: http://svn.apache.org/viewvc?rev=988647&view=rev Log: CAMEL-3075: Simple language now supports using unlimited and,or operators.
Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/language/simple/SimpleLanguageSupport.java camel/trunk/camel-core/src/test/java/org/apache/camel/component/file/FileConsumerPollStrategyTest.java camel/trunk/camel-core/src/test/java/org/apache/camel/language/SimpleOperatorTest.java camel/trunk/camel-core/src/test/resources/log4j.properties Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/language/simple/SimpleLanguageSupport.java URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/language/simple/SimpleLanguageSupport.java?rev=988647&r1=988646&r2=988647&view=diff ============================================================================== --- camel/trunk/camel-core/src/main/java/org/apache/camel/language/simple/SimpleLanguageSupport.java (original) +++ camel/trunk/camel-core/src/main/java/org/apache/camel/language/simple/SimpleLanguageSupport.java Tue Aug 24 18:18:41 2010 @@ -30,6 +30,7 @@ import org.apache.camel.builder.Expressi import org.apache.camel.builder.PredicateBuilder; import org.apache.camel.builder.ValueBuilder; import org.apache.camel.spi.Language; +import org.apache.camel.util.KeyValueHolder; import org.apache.camel.util.ObjectHelper; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -41,27 +42,49 @@ import static org.apache.camel.language. public abstract class SimpleLanguageSupport implements Language, IsSingleton { // this is a regex for a given group in a simple expression that uses operators - protected static final String GROUP_PATTERN = + protected static final String OPERATOR_REGEX = "\\$\\{(\\S+)\\}\\s+(==|>|>=|<|<=|!=|contains|not contains|regex|not regex|in|not in|is|not is|range|not range)\\s+('.*?'|\\S+)"; // this is the operator reg ex pattern used to match if a given expression is operator based or not - protected static final Pattern PATTERN = Pattern.compile("^(" + GROUP_PATTERN + ")(\\s+(and|or)\\s+(" + GROUP_PATTERN + "))?$"); + protected static final Pattern OPERATOR_PATTERN = Pattern.compile(OPERATOR_REGEX); + + // two specialized pattern for matching/finding multiple expressions combined using multiple and|or operators + protected static final Pattern ANDOR_PATTERN = Pattern.compile("\\s+(and|or)\\s+" + OPERATOR_REGEX); + protected static final Pattern START_ANDOR_PATTERN = Pattern.compile("^" + OPERATOR_REGEX + "\\s+(and|or)\\s+.*$"); // this is special for the range operator where you define the range as from..to (where from and to are numbers) protected static final Pattern RANGE_PATTERN = Pattern.compile("^(\\d+)(\\.\\.)(\\d+)$"); protected final Log log = LogFactory.getLog(getClass()); + /** + * A holder class to hold an operator and the expression. + * <p/> + * This is used for expression with multiple expressions grouped using and/or operators + */ + private final class ExpressionGroup extends KeyValueHolder<SimpleLanguageOperator, Expression> { + + public ExpressionGroup(SimpleLanguageOperator key, Expression value) { + super(key, value); + } + + @Override + public String toString() { + return getKey() + " " + getValue(); + } + } + public Predicate createPredicate(String expression) { return PredicateBuilder.toPredicate(createExpression(expression)); } public Expression createExpression(String expression) { - Matcher matcher = PATTERN.matcher(expression); - if (matcher.matches()) { + Matcher matcher = OPERATOR_PATTERN.matcher(expression); + Matcher startMatcher = START_ANDOR_PATTERN.matcher(expression); + if (matcher.matches() || startMatcher.matches()) { if (log.isDebugEnabled()) { - log.debug("Expression is evaluated as simple expression with operator: " + expression); + log.debug("Expression is evaluated as simple (with operator) expression: " + expression); } - return createOperatorExpression(matcher, expression); + return createOperatorExpression(matcher, startMatcher, expression); } else if (expression.indexOf("${") >= 0) { if (log.isDebugEnabled()) { log.debug("Expression is evaluated as simple (strict) expression: " + expression); @@ -75,54 +98,80 @@ public abstract class SimpleLanguageSupp } } - private Expression createOperatorExpression(final Matcher matcher, final String expression) { - int groupCount = matcher.groupCount(); + private Expression createOperatorExpression(Matcher matcher, Matcher startMatcher, String expression) { + Expression answer = null; - if (log.isTraceEnabled()) { - log.trace("Matcher expression: " + expression); - log.trace("Matcher group count: " + groupCount); - for (int i = 0; i < matcher.groupCount() + 1; i++) { - String group = matcher.group(i); - if (log.isTraceEnabled()) { - log.trace("Matcher group #" + i + ": " + group); - } - } + if (startMatcher.matches()) { + answer = doCreateOperatorExpression(expression, startMatcher.group(1), startMatcher.group(2), startMatcher.group(3)); + } else if (matcher.matches()) { + answer = doCreateOperatorExpression(expression, matcher.group(1), matcher.group(2), matcher.group(3)); } - // a simple expression with operator can either be a single group or a dual group - String operatorText = matcher.group(6); - if (operatorText == null) { - // single group - return doCreateOperatorExpression(expression, matcher.group(2), matcher.group(3), matcher.group(4)); - } else { - // dual group with an and/or operator between the two groups - final Expression first = doCreateOperatorExpression(expression, matcher.group(2), matcher.group(3), matcher.group(4)); - final SimpleLanguageOperator operator = asOperator(operatorText); - final Expression last = doCreateOperatorExpression(expression, matcher.group(8), matcher.group(9), matcher.group(10)); - - // create a compound predicate to combine the two groups with the operator - final Predicate compoundPredicate; - if (operator == AND) { - compoundPredicate = PredicateBuilder.and(PredicateBuilder.toPredicate(first), PredicateBuilder.toPredicate(last)); - } else if (operator == OR) { - compoundPredicate = PredicateBuilder.or(PredicateBuilder.toPredicate(first), PredicateBuilder.toPredicate(last)); - } else { + // append any additional operators + answer = appendAdditionalOperatorExpressions(answer, expression); + + return answer; + } + + private Expression appendAdditionalOperatorExpressions(final Expression answer, final String expression) { + Matcher matcher = ANDOR_PATTERN.matcher(expression); + + // now go through the and/or and append those sub expressions + final List<ExpressionGroup> expressions = new ArrayList<ExpressionGroup>(); + while (matcher.find()) { + dumpMatcher(matcher); + + // we only support AND/OR operator between expression groups + final SimpleLanguageOperator operator = asOperator(matcher.group(1)); + if (operator != AND && operator != OR) { throw new IllegalArgumentException("Syntax error in expression: " + expression + ". Expected operator as either and/or but was: " + operator); } + final Expression exp = doCreateOperatorExpression(expression, matcher.group(2), matcher.group(3), matcher.group(4)); + + // add this group + expressions.add(new ExpressionGroup(operator, exp)); + } - // return the expression that evaluates this expression - return new Expression() { - public <T> T evaluate(Exchange exchange, Class<T> type) { - boolean matches = compoundPredicate.matches(exchange); - return exchange.getContext().getTypeConverter().convertTo(type, matches); + // return the expression that evaluates the entire expression with multiple groups + return new Expression() { + public <T> T evaluate(Exchange exchange, Class<T> type) { + boolean matches = PredicateBuilder.toPredicate(answer).matches(exchange); + for (ExpressionGroup group : expressions) { + boolean result = PredicateBuilder.toPredicate(group.getValue()).matches(exchange); + if (group.getKey() == AND) { + matches &= result; + } else { + matches |= result; + } } + return exchange.getContext().getTypeConverter().convertTo(type, matches); + } - @Override - public String toString() { - return first + " " + operator + " " + last; + @Override + public String toString() { + StringBuilder msg = new StringBuilder(answer.toString()); + for (ExpressionGroup group : expressions) { + msg.append(" "); + msg.append(group.getKey()); + msg.append(" "); + msg.append(group.getValue()); } - }; + return msg.toString(); + } + }; + } + + private void dumpMatcher(Matcher matcher) { + if (log.isTraceEnabled()) { + log.trace("Matcher start: " + matcher.start()); + log.trace("Matcher end: " + matcher.end()); + log.trace("Matcher group: " + matcher.group()); + log.trace("Matcher group count: " + matcher.groupCount()); + for (int i = 0; i < matcher.groupCount() + 1; i++) { + String group = matcher.group(i); + log.trace("Matcher group #" + i + ": " + group); + } } } Modified: camel/trunk/camel-core/src/test/java/org/apache/camel/component/file/FileConsumerPollStrategyTest.java URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/component/file/FileConsumerPollStrategyTest.java?rev=988647&r1=988646&r2=988647&view=diff ============================================================================== --- camel/trunk/camel-core/src/test/java/org/apache/camel/component/file/FileConsumerPollStrategyTest.java (original) +++ camel/trunk/camel-core/src/test/java/org/apache/camel/component/file/FileConsumerPollStrategyTest.java Tue Aug 24 18:18:41 2010 @@ -56,7 +56,7 @@ public class FileConsumerPollStrategyTes assertMockEndpointsSatisfied(); // give file consumer a bit time - Thread.sleep(1000); + Thread.sleep(1500); assertTrue(event.startsWith("rollbackcommit")); } Modified: camel/trunk/camel-core/src/test/java/org/apache/camel/language/SimpleOperatorTest.java URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/language/SimpleOperatorTest.java?rev=988647&r1=988646&r2=988647&view=diff ============================================================================== --- camel/trunk/camel-core/src/test/java/org/apache/camel/language/SimpleOperatorTest.java (original) +++ camel/trunk/camel-core/src/test/java/org/apache/camel/language/SimpleOperatorTest.java Tue Aug 24 18:18:41 2010 @@ -47,6 +47,46 @@ public class SimpleOperatorTest extends assertExpression("${in.header.foo} == abc and ${in.header.bar} < 200", true); } + public void testTwoAnd() throws Exception { + exchange.getIn().setBody("Hello World"); + assertExpression("${in.header.foo} == abc and ${in.header.bar} == 123 and ${body} == 'Hello World'", true); + assertExpression("${in.header.foo} == 'abc' and ${in.header.bar} == 123 and ${body} == 'Hello World'", true); + assertExpression("${in.header.foo} == abc and ${in.header.bar} == 123 and ${body} == 'Bye World'", false); + assertExpression("${in.header.foo} == 'abc' and ${in.header.bar} == 123 and ${body} == 'Bye World'", false); + } + + public void testThreeAnd() throws Exception { + exchange.getIn().setBody("Hello World"); + assertExpression("${in.header.foo} == abc and ${in.header.bar} == 123 and ${body} == 'Hello World' and ${in.header.xx}} == null", true); + assertExpression("${in.header.foo} == 'abc' and ${in.header.bar} == 123 and ${body} == 'Hello World' and ${in.header.xx}} == null", true); + } + + public void testTwoOr() throws Exception { + exchange.getIn().setBody("Hello World"); + assertExpression("${in.header.foo} == abc or ${in.header.bar} == 44 or ${body} == 'Bye World'", true); + assertExpression("${in.header.foo} == 'abc' or ${in.header.bar} == 44 or ${body} == 'Bye World'", true); + assertExpression("${in.header.foo} == xxx or ${in.header.bar} == 44 or ${body} == 'Bye World'", false); + assertExpression("${in.header.foo} == 'xxx' or ${in.header.bar} == 44 or ${body} == 'Bye World'", false); + assertExpression("${in.header.foo} == xxx or ${in.header.bar} == 44 or ${body} == 'Hello World'", true); + assertExpression("${in.header.foo} == 'xxx' or ${in.header.bar} == 44 or ${body} == 'Hello World'", true); + assertExpression("${in.header.foo} == xxx or ${in.header.bar} == 123 or ${body} == 'Bye World'", true); + assertExpression("${in.header.foo} == 'xxx' or ${in.header.bar} == 123 or ${body} == 'Bye World'", true); + } + + public void testThreeOr() throws Exception { + exchange.getIn().setBody("Hello World"); + assertExpression("${in.header.foo} == xxx or ${in.header.bar} == 44 or ${body} == 'Bye Moon' or ${body} contains 'World'", true); + assertExpression("${in.header.foo} == 'xxx' or ${in.header.bar} == 44 or ${body} == 'Bye Moon' or ${body} contains 'World'", true); + assertExpression("${in.header.foo} == xxx or ${in.header.bar} == 44 or ${body} == 'Bye Moon' or ${body} contains 'Moon'", false); + assertExpression("${in.header.foo} == 'xxx' or ${in.header.bar} == 44 or ${body} == 'Bye Moon' or ${body} contains 'Moon'", false); + assertExpression("${in.header.foo} == abc or ${in.header.bar} == 44 or ${body} == 'Bye Moon' or ${body} contains 'Moon'", true); + assertExpression("${in.header.foo} == 'abc' or ${in.header.bar} == 44 or ${body} == 'Bye Moon' or ${body} contains 'Moon'", true); + assertExpression("${in.header.foo} == xxx or ${in.header.bar} == 123 or ${body} == 'Bye Moon' or ${body} contains 'Moon'", true); + assertExpression("${in.header.foo} == 'xxx' or ${in.header.bar} == 123 or ${body} == 'Bye Moon' or ${body} contains 'Moon'", true); + assertExpression("${in.header.foo} == xxx or ${in.header.bar} == 44 or ${body} == 'Hello World' or ${body} contains 'Moon'", true); + assertExpression("${in.header.foo} == 'xxx' or ${in.header.bar} == 44 or ${body} == 'Hello World' or ${body} contains 'Moon'", true); + } + public void testAndWithQuotation() throws Exception { assertExpression("${in.header.foo} == 'abc' and ${in.header.bar} == '123'", true); assertExpression("${in.header.foo} == 'abc' and ${in.header.bar} == '444'", false); Modified: camel/trunk/camel-core/src/test/resources/log4j.properties URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/resources/log4j.properties?rev=988647&r1=988646&r2=988647&view=diff ============================================================================== --- camel/trunk/camel-core/src/test/resources/log4j.properties (original) +++ camel/trunk/camel-core/src/test/resources/log4j.properties Tue Aug 24 18:18:41 2010 @@ -26,6 +26,7 @@ log4j.logger.org.apache.camel.impl.Defau #log4j.logger.org.apache.camel.impl.converter.DefaultTypeConverter=TRACE #log4j.logger.org.apache.camel=DEBUG +#log4j.logger.org.apache.camel.language.simple=TRACE #log4j.logger.org.apache.camel.component=TRACE #log4j.logger.org.apache.camel.component.seda=TRACE #log4j.logger.org.apache.camel.impl.DefaultUnitOfWork=TRACE