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