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 */
017 package org.apache.camel.language.simple.ast;
018
019 import java.util.ArrayList;
020 import java.util.Iterator;
021 import java.util.List;
022 import java.util.regex.Matcher;
023 import java.util.regex.Pattern;
024
025 import org.apache.camel.Exchange;
026 import org.apache.camel.Expression;
027 import org.apache.camel.Predicate;
028 import org.apache.camel.builder.ExpressionBuilder;
029 import org.apache.camel.builder.PredicateBuilder;
030 import org.apache.camel.builder.ValueBuilder;
031 import org.apache.camel.language.simple.types.BinaryOperatorType;
032 import org.apache.camel.language.simple.types.SimpleIllegalSyntaxException;
033 import org.apache.camel.language.simple.types.SimpleParserException;
034 import org.apache.camel.language.simple.types.SimpleToken;
035 import org.apache.camel.util.ObjectHelper;
036
037 /**
038 * Represents a binary expression in the AST.
039 */
040 public 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.GT) {
084 return createExpression(leftExp, rightExp, PredicateBuilder.isGreaterThan(leftExp, rightExp));
085 } else if (operator == BinaryOperatorType.GTE) {
086 return createExpression(leftExp, rightExp, PredicateBuilder.isGreaterThanOrEqualTo(leftExp, rightExp));
087 } else if (operator == BinaryOperatorType.LT) {
088 return createExpression(leftExp, rightExp, PredicateBuilder.isLessThan(leftExp, rightExp));
089 } else if (operator == BinaryOperatorType.LTE) {
090 return createExpression(leftExp, rightExp, PredicateBuilder.isLessThanOrEqualTo(leftExp, rightExp));
091 } else if (operator == BinaryOperatorType.NOT_EQ) {
092 return createExpression(leftExp, rightExp, PredicateBuilder.isNotEqualTo(leftExp, rightExp));
093 } else if (operator == BinaryOperatorType.CONTAINS) {
094 return createExpression(leftExp, rightExp, PredicateBuilder.contains(leftExp, rightExp));
095 } else if (operator == BinaryOperatorType.NOT_CONTAINS) {
096 return createExpression(leftExp, rightExp, PredicateBuilder.not(PredicateBuilder.contains(leftExp, rightExp)));
097 } else if (operator == BinaryOperatorType.IS || operator == BinaryOperatorType.NOT_IS) {
098 return createIsExpression(expression, leftExp, rightExp);
099 } else if (operator == BinaryOperatorType.REGEX || operator == BinaryOperatorType.NOT_REGEX) {
100 return createRegexExpression(leftExp, rightExp);
101 } else if (operator == BinaryOperatorType.IN || operator == BinaryOperatorType.NOT_IN) {
102 return createInExpression(leftExp, rightExp);
103 } else if (operator == BinaryOperatorType.RANGE || operator == BinaryOperatorType.NOT_RANGE) {
104 return createRangeExpression(expression, leftExp, rightExp);
105 }
106
107 throw new SimpleParserException("Unknown binary operator " + operator, token.getIndex());
108 }
109
110 private Expression createIsExpression(final String expression, final Expression leftExp, final Expression rightExp) {
111 return new Expression() {
112 @Override
113 public <T> T evaluate(Exchange exchange, Class<T> type) {
114 Predicate predicate;
115 String name = rightExp.evaluate(exchange, String.class);
116 if (name == null || "null".equals(name)) {
117 throw new SimpleIllegalSyntaxException(expression, right.getToken().getIndex(), operator + " operator cannot accept null. A class type must be provided.");
118 }
119 Class<?> rightType = exchange.getContext().getClassResolver().resolveClass(name);
120 if (rightType == null) {
121 throw new SimpleIllegalSyntaxException(expression, right.getToken().getIndex(), operator + " operator cannot find class with name: " + name);
122 }
123
124 predicate = PredicateBuilder.isInstanceOf(leftExp, rightType);
125 if (operator == BinaryOperatorType.NOT_IS) {
126 predicate = PredicateBuilder.not(predicate);
127 }
128 boolean answer = predicate.matches(exchange);
129
130 return exchange.getContext().getTypeConverter().convertTo(type, answer);
131 }
132
133 @Override
134 public String toString() {
135 return left + " " + token.getText() + " " + right;
136 }
137 };
138 }
139
140 private Expression createRegexExpression(final Expression leftExp, final Expression rightExp) {
141 return new Expression() {
142 @Override
143 public <T> T evaluate(Exchange exchange, Class<T> type) {
144 // reg ex should use String pattern, so we evaluate the right hand side as a String
145 Predicate predicate = PredicateBuilder.regex(leftExp, rightExp.evaluate(exchange, String.class));
146 if (operator == BinaryOperatorType.NOT_REGEX) {
147 predicate = PredicateBuilder.not(predicate);
148 }
149 boolean answer = predicate.matches(exchange);
150 return exchange.getContext().getTypeConverter().convertTo(type, answer);
151 }
152
153 @Override
154 public String toString() {
155 return left + " " + token.getText() + " " + right;
156 }
157 };
158 }
159
160 private Expression createInExpression(final Expression leftExp, final Expression rightExp) {
161 return new Expression() {
162 @Override
163 public <T> T evaluate(Exchange exchange, Class<T> type) {
164 // okay the in operator is a bit more complex as we need to build a list of values
165 // from the right hand side expression.
166 // each element on the right hand side must be separated by comma (default for create iterator)
167 Iterator<Object> it = ObjectHelper.createIterator(rightExp.evaluate(exchange, Object.class));
168 List<Object> values = new ArrayList<Object>();
169 while (it.hasNext()) {
170 values.add(it.next());
171 }
172 // then reuse value builder to create the in predicate with the list of values
173 ValueBuilder vb = new ValueBuilder(leftExp);
174 Predicate predicate = vb.in(values.toArray());
175 if (operator == BinaryOperatorType.NOT_IN) {
176 predicate = PredicateBuilder.not(predicate);
177 }
178 boolean answer = predicate.matches(exchange);
179 return exchange.getContext().getTypeConverter().convertTo(type, answer);
180 }
181
182 @Override
183 public String toString() {
184 return left + " " + token.getText() + " " + right;
185 }
186 };
187 }
188
189 private Expression createRangeExpression(final String expression, final Expression leftExp, final Expression rightExp) {
190 return new Expression() {
191 @Override
192 public <T> T evaluate(Exchange exchange, Class<T> type) {
193 Predicate predicate;
194
195 String range = rightExp.evaluate(exchange, String.class);
196 Matcher matcher = RANGE_PATTERN.matcher(range);
197 if (matcher.matches()) {
198 // wrap as constant expression for the from and to values
199 Expression from = ExpressionBuilder.constantExpression(matcher.group(1));
200 Expression to = ExpressionBuilder.constantExpression(matcher.group(3));
201
202 // build a compound predicate for the range
203 predicate = PredicateBuilder.isGreaterThanOrEqualTo(leftExp, from);
204 predicate = PredicateBuilder.and(predicate, PredicateBuilder.isLessThanOrEqualTo(leftExp, to));
205 } else {
206 throw new SimpleIllegalSyntaxException(expression, right.getToken().getIndex(), operator + " operator is not valid. Valid syntax:'from..to' (where from and to are numbers).");
207 }
208 if (operator == BinaryOperatorType.NOT_RANGE) {
209 predicate = PredicateBuilder.not(predicate);
210 }
211
212 boolean answer = predicate.matches(exchange);
213 return exchange.getContext().getTypeConverter().convertTo(type, answer);
214 }
215
216 @Override
217 public String toString() {
218 return left + " " + token.getText() + " " + right;
219 }
220 };
221 }
222
223 private Expression createExpression(final Expression left, final Expression right, final Predicate predicate) {
224 return new Expression() {
225 @Override
226 public <T> T evaluate(Exchange exchange, Class<T> type) {
227 boolean answer = predicate.matches(exchange);
228 return exchange.getContext().getTypeConverter().convertTo(type, answer);
229 }
230
231 @Override
232 public String toString() {
233 return left + " " + token.getText() + " " + right;
234 }
235 };
236 }
237
238 }