001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.camel.language.simple.ast; 018 019import java.util.ArrayList; 020import java.util.Iterator; 021import java.util.List; 022import java.util.regex.Matcher; 023import java.util.regex.Pattern; 024 025import org.apache.camel.Exchange; 026import org.apache.camel.Expression; 027import org.apache.camel.Predicate; 028import org.apache.camel.builder.ExpressionBuilder; 029import org.apache.camel.builder.PredicateBuilder; 030import org.apache.camel.builder.ValueBuilder; 031import org.apache.camel.language.simple.types.BinaryOperatorType; 032import org.apache.camel.language.simple.types.SimpleIllegalSyntaxException; 033import org.apache.camel.language.simple.types.SimpleParserException; 034import org.apache.camel.language.simple.types.SimpleToken; 035import org.apache.camel.util.ObjectHelper; 036 037/** 038 * Represents a binary expression in the AST. 039 */ 040public class BinaryExpression extends BaseSimpleNode { 041 042 // this is special for the range operator where you define the range as from..to (where from and to are numbers) 043 private static final Pattern RANGE_PATTERN = Pattern.compile("^(\\d+)(\\.\\.)(\\d+)$"); 044 045 private final BinaryOperatorType operator; 046 private SimpleNode left; 047 private SimpleNode right; 048 049 public BinaryExpression(SimpleToken token) { 050 super(token); 051 operator = BinaryOperatorType.asOperator(token.getText()); 052 } 053 054 @Override 055 public String toString() { 056 return left + " " + token.getText() + " " + right; 057 } 058 059 public boolean acceptLeftNode(SimpleNode lef) { 060 this.left = lef; 061 return true; 062 } 063 064 public boolean acceptRightNode(SimpleNode right) { 065 this.right = right; 066 return true; 067 } 068 069 public BinaryOperatorType getOperator() { 070 return operator; 071 } 072 073 @Override 074 public Expression createExpression(String expression) { 075 ObjectHelper.notNull(left, "left node", this); 076 ObjectHelper.notNull(right, "right node", this); 077 078 final Expression leftExp = left.createExpression(expression); 079 final Expression rightExp = right.createExpression(expression); 080 081 if (operator == BinaryOperatorType.EQ) { 082 return createExpression(leftExp, rightExp, PredicateBuilder.isEqualTo(leftExp, rightExp)); 083 } else if (operator == BinaryOperatorType.EQ_IGNORE) { 084 return createExpression(leftExp, rightExp, PredicateBuilder.isEqualToIgnoreCase(leftExp, rightExp)); 085 } else if (operator == BinaryOperatorType.GT) { 086 return createExpression(leftExp, rightExp, PredicateBuilder.isGreaterThan(leftExp, rightExp)); 087 } else if (operator == BinaryOperatorType.GTE) { 088 return createExpression(leftExp, rightExp, PredicateBuilder.isGreaterThanOrEqualTo(leftExp, rightExp)); 089 } else if (operator == BinaryOperatorType.LT) { 090 return createExpression(leftExp, rightExp, PredicateBuilder.isLessThan(leftExp, rightExp)); 091 } else if (operator == BinaryOperatorType.LTE) { 092 return createExpression(leftExp, rightExp, PredicateBuilder.isLessThanOrEqualTo(leftExp, rightExp)); 093 } else if (operator == BinaryOperatorType.NOT_EQ) { 094 return createExpression(leftExp, rightExp, PredicateBuilder.isNotEqualTo(leftExp, rightExp)); 095 } else if (operator == BinaryOperatorType.CONTAINS) { 096 return createExpression(leftExp, rightExp, PredicateBuilder.contains(leftExp, rightExp)); 097 } else if (operator == BinaryOperatorType.NOT_CONTAINS) { 098 return createExpression(leftExp, rightExp, PredicateBuilder.not(PredicateBuilder.contains(leftExp, rightExp))); 099 } else if (operator == BinaryOperatorType.CONTAINS_IGNORECASE) { 100 return createExpression(leftExp, rightExp, PredicateBuilder.containsIgnoreCase(leftExp, rightExp)); 101 } else if (operator == BinaryOperatorType.IS || operator == BinaryOperatorType.NOT_IS) { 102 return createIsExpression(expression, leftExp, rightExp); 103 } else if (operator == BinaryOperatorType.REGEX || operator == BinaryOperatorType.NOT_REGEX) { 104 return createRegexExpression(leftExp, rightExp); 105 } else if (operator == BinaryOperatorType.IN || operator == BinaryOperatorType.NOT_IN) { 106 return createInExpression(leftExp, rightExp); 107 } else if (operator == BinaryOperatorType.RANGE || operator == BinaryOperatorType.NOT_RANGE) { 108 return createRangeExpression(expression, leftExp, rightExp); 109 } else if (operator == BinaryOperatorType.STARTS_WITH) { 110 return createExpression(leftExp, rightExp, PredicateBuilder.startsWith(leftExp, rightExp)); 111 } else if (operator == BinaryOperatorType.ENDS_WITH) { 112 return createExpression(leftExp, rightExp, PredicateBuilder.endsWith(leftExp, rightExp)); 113 } 114 115 throw new SimpleParserException("Unknown binary operator " + operator, token.getIndex()); 116 } 117 118 private Expression createIsExpression(final String expression, final Expression leftExp, final Expression rightExp) { 119 return new Expression() { 120 @Override 121 public <T> T evaluate(Exchange exchange, Class<T> type) { 122 Predicate predicate; 123 String name = rightExp.evaluate(exchange, String.class); 124 if (name == null || "null".equals(name)) { 125 throw new SimpleIllegalSyntaxException(expression, right.getToken().getIndex(), operator + " operator cannot accept null. A class type must be provided."); 126 } 127 Class<?> rightType = exchange.getContext().getClassResolver().resolveClass(name); 128 if (rightType == null) { 129 throw new SimpleIllegalSyntaxException(expression, right.getToken().getIndex(), operator + " operator cannot find class with name: " + name); 130 } 131 132 predicate = PredicateBuilder.isInstanceOf(leftExp, rightType); 133 if (operator == BinaryOperatorType.NOT_IS) { 134 predicate = PredicateBuilder.not(predicate); 135 } 136 boolean answer = predicate.matches(exchange); 137 138 return exchange.getContext().getTypeConverter().convertTo(type, answer); 139 } 140 141 @Override 142 public String toString() { 143 return left + " " + token.getText() + " " + right; 144 } 145 }; 146 } 147 148 private Expression createRegexExpression(final Expression leftExp, final Expression rightExp) { 149 return new Expression() { 150 @Override 151 public <T> T evaluate(Exchange exchange, Class<T> type) { 152 // reg ex should use String pattern, so we evaluate the right hand side as a String 153 Predicate predicate = PredicateBuilder.regex(leftExp, rightExp.evaluate(exchange, String.class)); 154 if (operator == BinaryOperatorType.NOT_REGEX) { 155 predicate = PredicateBuilder.not(predicate); 156 } 157 boolean answer = predicate.matches(exchange); 158 return exchange.getContext().getTypeConverter().convertTo(type, answer); 159 } 160 161 @Override 162 public String toString() { 163 return left + " " + token.getText() + " " + right; 164 } 165 }; 166 } 167 168 private Expression createInExpression(final Expression leftExp, final Expression rightExp) { 169 return new Expression() { 170 @Override 171 public <T> T evaluate(Exchange exchange, Class<T> type) { 172 // okay the in operator is a bit more complex as we need to build a list of values 173 // from the right hand side expression. 174 // each element on the right hand side must be separated by comma (default for create iterator) 175 Iterator<Object> it = ObjectHelper.createIterator(rightExp.evaluate(exchange, Object.class)); 176 List<Object> values = new ArrayList<Object>(); 177 while (it.hasNext()) { 178 values.add(it.next()); 179 } 180 // then reuse value builder to create the in predicate with the list of values 181 ValueBuilder vb = new ValueBuilder(leftExp); 182 Predicate predicate = vb.in(values.toArray()); 183 if (operator == BinaryOperatorType.NOT_IN) { 184 predicate = PredicateBuilder.not(predicate); 185 } 186 boolean answer = predicate.matches(exchange); 187 return exchange.getContext().getTypeConverter().convertTo(type, answer); 188 } 189 190 @Override 191 public String toString() { 192 return left + " " + token.getText() + " " + right; 193 } 194 }; 195 } 196 197 private Expression createRangeExpression(final String expression, final Expression leftExp, final Expression rightExp) { 198 return new Expression() { 199 @Override 200 public <T> T evaluate(Exchange exchange, Class<T> type) { 201 Predicate predicate; 202 203 String range = rightExp.evaluate(exchange, String.class); 204 Matcher matcher = RANGE_PATTERN.matcher(range); 205 if (matcher.matches()) { 206 // wrap as constant expression for the from and to values 207 Expression from = ExpressionBuilder.constantExpression(matcher.group(1)); 208 Expression to = ExpressionBuilder.constantExpression(matcher.group(3)); 209 210 // build a compound predicate for the range 211 predicate = PredicateBuilder.isGreaterThanOrEqualTo(leftExp, from); 212 predicate = PredicateBuilder.and(predicate, PredicateBuilder.isLessThanOrEqualTo(leftExp, to)); 213 } else { 214 throw new SimpleIllegalSyntaxException(expression, right.getToken().getIndex(), operator + " operator is not valid. Valid syntax:'from..to' (where from and to are numbers)."); 215 } 216 if (operator == BinaryOperatorType.NOT_RANGE) { 217 predicate = PredicateBuilder.not(predicate); 218 } 219 220 boolean answer = predicate.matches(exchange); 221 return exchange.getContext().getTypeConverter().convertTo(type, answer); 222 } 223 224 @Override 225 public String toString() { 226 return left + " " + token.getText() + " " + right; 227 } 228 }; 229 } 230 231 private Expression createExpression(final Expression left, final Expression right, final Predicate predicate) { 232 return new Expression() { 233 @Override 234 public <T> T evaluate(Exchange exchange, Class<T> type) { 235 boolean answer = predicate.matches(exchange); 236 return exchange.getContext().getTypeConverter().convertTo(type, answer); 237 } 238 239 @Override 240 public String toString() { 241 return left + " " + token.getText() + " " + right; 242 } 243 }; 244 } 245 246}