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;
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.IsSingleton;
028    import org.apache.camel.Predicate;
029    import org.apache.camel.builder.ExpressionBuilder;
030    import org.apache.camel.builder.PredicateBuilder;
031    import org.apache.camel.builder.ValueBuilder;
032    import org.apache.camel.spi.Language;
033    import org.apache.camel.util.ObjectHelper;
034    import org.apache.commons.logging.Log;
035    import org.apache.commons.logging.LogFactory;
036    import static org.apache.camel.language.simple.SimpleLangaugeOperator.*;
037    
038    /**
039     * Abstract base class for Simple languages.
040     */
041    public abstract class SimpleLanguageSupport implements Language, IsSingleton {
042    
043        protected static final Pattern PATTERN = Pattern.compile(
044                "^\\$\\{(.+)\\}\\s+(==|>|>=|<|<=|!=|contains|not contains|regex|not regex|in|not in)\\s+(.+)$");
045        protected final Log log = LogFactory.getLog(getClass());
046    
047        public Predicate createPredicate(String expression) {
048            return PredicateBuilder.toPredicate(createExpression(expression));
049        }
050    
051        public Expression createExpression(String expression) {
052            Matcher matcher = PATTERN.matcher(expression);
053            if (matcher.matches()) {
054                if (log.isDebugEnabled()) {
055                    log.debug("Expression is evaluated as operator expression: " + expression);
056                }
057                return createOperatorExpression(matcher, expression);
058            } else if (expression.indexOf("${") >= 0) {
059                if (log.isDebugEnabled()) {
060                    log.debug("Expression is evaluated as complex expression: " + expression);
061                }
062                return createComplexConcatExpression(expression);
063            } else {
064                if (log.isDebugEnabled()) {
065                    log.debug("Expression is evaluated as simple expression: " + expression);
066                }
067                return createSimpleExpression(expression);
068            }
069        }
070    
071        private Expression createOperatorExpression(final Matcher matcher, final String expression) {
072            final Expression left = createSimpleExpression(matcher.group(1));
073            final SimpleLangaugeOperator operator = asOperator(matcher.group(2));
074    
075            // the right hand side expression can either be a constant expression wiht ' '
076            // or another simple expression using ${ } placeholders
077            String text = matcher.group(3);
078    
079            final Expression right;
080            final Expression rightConverted;
081            // special null handling
082            if ("null".equals(text)) {
083                right = createConstantExpression(null);
084                rightConverted = right;
085            } else {
086                // text can either be a constant enclosed by ' ' or another expression using ${ } placeholders
087                String constant = ObjectHelper.between(text, "'", "'");
088                if (constant == null) {
089                    // if no ' ' around then fallback to the text itself
090                    constant = text;
091                }
092                String simple = ObjectHelper.between(text, "${", "}");
093    
094                right = simple != null ? createSimpleExpression(simple) : createConstantExpression(constant);
095                // to support numeric comparions using > and < operators we must convert the right hand side
096                // to the same type as the left
097                rightConverted = ExpressionBuilder.convertToExpression(right, left);
098            }
099    
100            return new Expression() {
101                public Object evaluate(final Exchange exchange) {
102                    Predicate predicate = null;
103                    if (operator == EQ) {
104                        predicate = PredicateBuilder.isEqualTo(left, rightConverted);
105                    } else if (operator == GT) {
106                        predicate = PredicateBuilder.isGreaterThan(left, rightConverted);
107                    } else if (operator == GTE) {
108                        predicate = PredicateBuilder.isGreaterThanOrEqualTo(left, rightConverted);
109                    } else if (operator == LT) {
110                        predicate = PredicateBuilder.isLessThan(left, rightConverted);
111                    } else if (operator == LTE) {
112                        predicate = PredicateBuilder.isLessThanOrEqualTo(left, rightConverted);
113                    } else if (operator == NOT) {
114                        predicate = PredicateBuilder.isNotEqualTo(left, rightConverted);
115                    } else if (operator == CONTAINS || operator == NOT_CONTAINS) {
116                        predicate = PredicateBuilder.contains(left, rightConverted);
117                        if (operator == NOT_CONTAINS) {
118                            predicate = PredicateBuilder.not(predicate);
119                        }
120                    } else if (operator == REGEX || operator == NOT_REGEX) {
121                        // reg ex should use String pattern, so we evalute the right hand side as a String
122                        predicate = PredicateBuilder.regex(left, right.evaluate(exchange, String.class));
123                        if (operator == NOT_REGEX) {
124                            predicate = PredicateBuilder.not(predicate);
125                        }
126                    } else if (operator == IN || operator == NOT_IN) {
127                        // okay the in operator is a bit more complex as we need to build a list of values
128                        // from the right handside expression.
129                        // each element on the right handside must be separated by comma (default for create iterator)
130                        Iterator it = ObjectHelper.createIterator(right.evaluate(exchange));
131                        List<Object> values = new ArrayList<Object>();
132                        while (it.hasNext()) {
133                            values.add(it.next());
134                        }
135                        // then reuse value builder to create the in predicate with the list of values
136                        ValueBuilder vb = new ValueBuilder(left);
137                        predicate = vb.in(values.toArray());
138                        if (operator == NOT_IN) {
139                            predicate = PredicateBuilder.not(predicate);
140                        }
141                    }
142    
143                    if (predicate == null) {
144                        throw new IllegalArgumentException("Unsupported operator: " + operator + " for expression: " + expression);
145                    }
146                    return predicate.matches(exchange);
147                }
148    
149                public <T> T evaluate(Exchange exchange, Class<T> type) {
150                    Object result = evaluate(exchange);
151                    return exchange.getContext().getTypeConverter().convertTo(type, result);
152                }
153    
154                @Override
155                public String toString() {
156                    return left + " " + operator + " " + right;
157                }
158            };
159        }
160    
161        protected Expression createComplexConcatExpression(String expression) {
162            List<Expression> results = new ArrayList<Expression>();
163    
164            int pivot = 0;
165            int size = expression.length();
166            while (pivot < size) {
167                int idx = expression.indexOf("${", pivot);
168                if (idx < 0) {
169                    results.add(createConstantExpression(expression, pivot, size));
170                    break;
171                } else {
172                    if (pivot < idx) {
173                        results.add(createConstantExpression(expression, pivot, idx));
174                    }
175                    pivot = idx + 2;
176                    int endIdx = expression.indexOf("}", pivot);
177                    if (endIdx < 0) {
178                        throw new IllegalArgumentException("Expecting } but found end of string for simple expression: " + expression);
179                    }
180                    String simpleText = expression.substring(pivot, endIdx);
181    
182                    Expression simpleExpression = createSimpleExpression(simpleText);
183                    results.add(simpleExpression);
184                    pivot = endIdx + 1;
185                }
186            }
187            return ExpressionBuilder.concatExpression(results, expression);
188        }
189    
190        protected Expression createConstantExpression(String expression, int start, int end) {
191            return ExpressionBuilder.constantExpression(expression.substring(start, end));
192        }
193    
194        protected Expression createConstantExpression(String expression) {
195            return ExpressionBuilder.constantExpression(expression);
196        }
197    
198        /**
199         * Creates the simple expression based on the extracted content from the ${ } place holders
200         *
201         * @param expression  the content between ${ and }
202         * @return the expression
203         */
204        protected abstract Expression createSimpleExpression(String expression);
205    
206        protected String ifStartsWithReturnRemainder(String prefix, String text) {
207            if (text.startsWith(prefix)) {
208                String remainder = text.substring(prefix.length());
209                if (remainder.length() > 0) {
210                    return remainder;
211                }
212            }
213            return null;
214        }
215    }