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.builder;
018    
019    import java.util.Arrays;
020    import java.util.List;
021    import java.util.regex.Matcher;
022    import java.util.regex.Pattern;
023    
024    import org.apache.camel.Exchange;
025    import org.apache.camel.Expression;
026    import org.apache.camel.Predicate;
027    import org.apache.camel.util.ExpressionToPredicateAdapter;
028    import org.apache.camel.util.ObjectHelper;
029    
030    import static org.apache.camel.util.ObjectHelper.notNull;
031    
032    
033    /**
034     * A helper class for working with predicates
035     *
036     * @version 
037     */
038    public final class PredicateBuilder {
039    
040        /**
041         * Utility classes should not have a public constructor.
042         */
043        private PredicateBuilder() {
044        }
045        
046        /**
047         * Converts the given expression into an {@link Predicate}
048         */
049        public static Predicate toPredicate(final Expression expression) {
050            return ExpressionToPredicateAdapter.toPredicate(expression);
051        }
052    
053        /**
054         * A helper method to return the logical not of the given predicate
055         */
056        public static Predicate not(final Predicate predicate) {
057            notNull(predicate, "predicate");
058            return new Predicate() {
059                public boolean matches(Exchange exchange) {
060                    return !predicate.matches(exchange);
061                }
062    
063                @Override
064                public String toString() {
065                    return "not (" + predicate + ")";
066                }
067            };
068        }
069    
070        /**
071         * A helper method to combine multiple predicates by a logical AND
072         */
073        public static Predicate and(final Predicate left, final Predicate right) {
074            notNull(left, "left");
075            notNull(right, "right");
076            return new Predicate() {
077                public boolean matches(Exchange exchange) {
078                    return left.matches(exchange) && right.matches(exchange);
079                }
080    
081                @Override
082                public String toString() {
083                    return "(" + left + ") and (" + right + ")";
084                }
085            };
086        }
087    
088        /**
089         * A helper method to combine multiple predicates by a logical OR
090         */
091        public static Predicate or(final Predicate left, final Predicate right) {
092            notNull(left, "left");
093            notNull(right, "right");
094            return new Predicate() {
095                public boolean matches(Exchange exchange) {
096                    return left.matches(exchange) || right.matches(exchange);
097                }
098    
099                @Override
100                public String toString() {
101                    return "(" + left + ") or (" + right + ")";
102                }
103            };
104        }
105    
106        /**
107         * A helper method to return true if any of the predicates matches.
108         */
109        public static Predicate in(final Predicate... predicates) {
110            notNull(predicates, "predicates");
111    
112            return new Predicate() {
113                public boolean matches(Exchange exchange) {
114                    for (Predicate in : predicates) {
115                        if (in.matches(exchange)) {
116                            return true;
117                        }
118                    }
119                    return false;
120                }
121    
122                @Override
123                public String toString() {
124                    return "in (" + Arrays.asList(predicates) + ")";
125                }
126            };
127        }
128    
129        public static Predicate isEqualTo(final Expression left, final Expression right) {
130            return new BinaryPredicateSupport(left, right) {
131    
132                protected boolean matches(Exchange exchange, Object leftValue, Object rightValue) {
133                    if (leftValue == null && rightValue == null) {
134                        // they are equal
135                        return true;
136                    } else if (leftValue == null || rightValue == null) {
137                        // only one of them is null so they are not equal
138                        return false;
139                    }
140    
141                    return ObjectHelper.typeCoerceEquals(exchange.getContext().getTypeConverter(), leftValue, rightValue);
142                }
143    
144                protected String getOperationText() {
145                    return "==";
146                }
147            };
148        }
149    
150        public static Predicate isNotEqualTo(final Expression left, final Expression right) {
151            return new BinaryPredicateSupport(left, right) {
152    
153                protected boolean matches(Exchange exchange, Object leftValue, Object rightValue) {
154                    if (leftValue == null && rightValue == null) {
155                        // they are equal
156                        return false;
157                    } else if (leftValue == null || rightValue == null) {
158                        // only one of them is null so they are not equal
159                        return true;
160                    }
161    
162                    return ObjectHelper.typeCoerceNotEquals(exchange.getContext().getTypeConverter(), leftValue, rightValue);
163                }
164    
165                protected String getOperationText() {
166                    return "!=";
167                }
168            };
169        }
170    
171        public static Predicate isLessThan(final Expression left, final Expression right) {
172            return new BinaryPredicateSupport(left, right) {
173    
174                protected boolean matches(Exchange exchange, Object leftValue, Object rightValue) {
175                    if (leftValue == null && rightValue == null) {
176                        // they are equal
177                        return true;
178                    } else if (leftValue == null || rightValue == null) {
179                        // only one of them is null so they are not equal
180                        return false;
181                    }
182    
183                    return ObjectHelper.typeCoerceCompare(exchange.getContext().getTypeConverter(), leftValue, rightValue) < 0;
184                }
185    
186                protected String getOperationText() {
187                    return "<";
188                }
189            };
190        }
191    
192        public static Predicate isLessThanOrEqualTo(final Expression left, final Expression right) {
193            return new BinaryPredicateSupport(left, right) {
194    
195                protected boolean matches(Exchange exchange, Object leftValue, Object rightValue) {
196                    if (leftValue == null && rightValue == null) {
197                        // they are equal
198                        return true;
199                    } else if (leftValue == null || rightValue == null) {
200                        // only one of them is null so they are not equal
201                        return false;
202                    }
203    
204                    return ObjectHelper.typeCoerceCompare(exchange.getContext().getTypeConverter(), leftValue, rightValue) <= 0;
205                }
206    
207                protected String getOperationText() {
208                    return "<=";
209                }
210            };
211        }
212    
213        public static Predicate isGreaterThan(final Expression left, final Expression right) {
214            return new BinaryPredicateSupport(left, right) {
215    
216                protected boolean matches(Exchange exchange, Object leftValue, Object rightValue) {
217                    if (leftValue == null && rightValue == null) {
218                        // they are equal
219                        return false;
220                    } else if (leftValue == null || rightValue == null) {
221                        // only one of them is null so they are not equal
222                        return false;
223                    }
224    
225                    return ObjectHelper.typeCoerceCompare(exchange.getContext().getTypeConverter(), leftValue, rightValue) > 0;
226                }
227    
228                protected String getOperationText() {
229                    return ">";
230                }
231            };
232        }
233    
234        public static Predicate isGreaterThanOrEqualTo(final Expression left, final Expression right) {
235            return new BinaryPredicateSupport(left, right) {
236    
237                protected boolean matches(Exchange exchange, Object leftValue, Object rightValue) {
238                    if (leftValue == null && rightValue == null) {
239                        // they are equal
240                        return true;
241                    } else if (leftValue == null || rightValue == null) {
242                        // only one of them is null so they are not equal
243                        return false;
244                    }
245    
246                    return ObjectHelper.typeCoerceCompare(exchange.getContext().getTypeConverter(), leftValue, rightValue) >= 0;
247                }
248    
249                protected String getOperationText() {
250                    return ">=";
251                }
252            };
253        }
254    
255        public static Predicate contains(final Expression left, final Expression right) {
256            return new BinaryPredicateSupport(left, right) {
257    
258                protected boolean matches(Exchange exchange, Object leftValue, Object rightValue) {
259                    if (leftValue == null && rightValue == null) {
260                        // they are equal
261                        return true;
262                    } else if (leftValue == null || rightValue == null) {
263                        // only one of them is null so they are not equal
264                        return false;
265                    }
266    
267                    return ObjectHelper.contains(leftValue, rightValue);
268                }
269    
270                protected String getOperationText() {
271                    return "contains";
272                }
273            };
274        }
275    
276        public static Predicate isNull(final Expression expression) {
277            return new BinaryPredicateSupport(expression, ExpressionBuilder.constantExpression(null)) {
278    
279                protected boolean matches(Exchange exchange, Object leftValue, Object rightValue) {
280                    if (leftValue == null) {
281                        // the left operator is null so its true
282                        return true;
283                    } 
284    
285                    return ObjectHelper.typeCoerceEquals(exchange.getContext().getTypeConverter(), leftValue, rightValue);
286                }
287    
288                protected String getOperationText() {
289                    // leave the operation text as "is not" as Camel will insert right and left expression around it
290                    // so it will be displayed as: XXX is null
291                    return "is";
292                }
293            };
294        }
295    
296        public static Predicate isNotNull(final Expression expression) {
297            return new BinaryPredicateSupport(expression, ExpressionBuilder.constantExpression(null)) {
298    
299                protected boolean matches(Exchange exchange, Object leftValue, Object rightValue) {
300                    if (leftValue != null) {
301                        // the left operator is not null so its true
302                        return true;
303                    }
304    
305                    return ObjectHelper.typeCoerceNotEquals(exchange.getContext().getTypeConverter(), leftValue, rightValue);
306                }
307    
308                protected String getOperationText() {
309                    // leave the operation text as "is not" as Camel will insert right and left expression around it
310                    // so it will be displayed as: XXX is not null
311                    return "is not";
312                }
313            };
314        }
315    
316        public static Predicate isInstanceOf(final Expression expression, final Class<?> type) {
317            notNull(expression, "expression");
318            notNull(type, "type");
319    
320            return new Predicate() {
321                public boolean matches(Exchange exchange) {
322                    Object value = expression.evaluate(exchange, Object.class);
323                    return type.isInstance(value);
324                }
325    
326                @Override
327                public String toString() {
328                    return expression + " instanceof " + type.getCanonicalName();
329                }
330            };
331        }
332    
333        public static Predicate startsWith(final Expression left, final Expression right) {
334            return new BinaryPredicateSupport(left, right) {
335    
336                protected boolean matches(Exchange exchange, Object leftValue, Object rightValue) {
337                    if (leftValue == null && rightValue == null) {
338                        // they are equal
339                        return true;
340                    } else if (leftValue == null || rightValue == null) {
341                        // only one of them is null so they are not equal
342                        return false;
343                    }
344                    String leftStr = exchange.getContext().getTypeConverter().convertTo(String.class, leftValue);
345                    String rightStr = exchange.getContext().getTypeConverter().convertTo(String.class, rightValue);
346                    if (leftStr != null && rightStr != null) {
347                        return leftStr.startsWith(rightStr);
348                    } else {
349                        return false;
350                    }
351                }
352    
353                protected String getOperationText() {
354                    return "startsWith";
355                }
356            };
357        }
358    
359        public static Predicate endsWith(final Expression left, final Expression right) {
360            return new BinaryPredicateSupport(left, right) {
361    
362                protected boolean matches(Exchange exchange, Object leftValue, Object rightValue) {
363                    if (leftValue == null && rightValue == null) {
364                        // they are equal
365                        return true;
366                    } else if (leftValue == null || rightValue == null) {
367                        // only one of them is null so they are not equal
368                        return false;
369                    }
370                    String leftStr = exchange.getContext().getTypeConverter().convertTo(String.class, leftValue);
371                    String rightStr = exchange.getContext().getTypeConverter().convertTo(String.class, rightValue);
372                    if (leftStr != null && rightStr != null) {
373                        return leftStr.endsWith(rightStr);
374                    } else {
375                        return false;
376                    }
377                }
378    
379                protected String getOperationText() {
380                    return "endsWith";
381                }
382            };
383        }
384    
385        /**
386         * Returns a predicate which is true if the expression matches the given
387         * regular expression
388         *
389         * @param expression the expression to evaluate
390         * @param regex the regular expression to match against
391         * @return a new predicate
392         */
393        public static Predicate regex(final Expression expression, final String regex) {
394            return regex(expression, Pattern.compile(regex));
395        }
396    
397        /**
398         * Returns a predicate which is true if the expression matches the given
399         * regular expression
400         *
401         * @param expression the expression to evaluate
402         * @param pattern the regular expression to match against
403         * @return a new predicate
404         */
405        public static Predicate regex(final Expression expression, final Pattern pattern) {
406            notNull(expression, "expression");
407            notNull(pattern, "pattern");
408    
409            return new Predicate() {
410                public boolean matches(Exchange exchange) {
411                    String value = expression.evaluate(exchange, String.class);
412                    if (value != null) {
413                        Matcher matcher = pattern.matcher(value);
414                        return matcher.matches();
415                    }
416                    return false;
417                }
418    
419                @Override
420                public String toString() {
421                    return expression + ".matches('" + pattern + "')";
422                }
423            };
424        }
425    
426        /**
427         * Concat the given predicates into a single predicate, which
428         * only matches if all the predicates matches.
429         *
430         * @param predicates predicates
431         * @return a single predicate containing all the predicates
432         */
433        public static Predicate and(List<Predicate> predicates) {
434            Predicate answer = null;
435            for (Predicate predicate : predicates) {
436                if (answer == null) {
437                    answer = predicate;
438                } else {
439                    answer = and(answer, predicate);
440                }
441            }
442            return answer;
443        }
444    
445        /**
446         * A constant predicate.
447         *
448         * @param answer the constant matches
449         * @return a predicate that always returns the given answer.
450         */
451        public static Predicate constant(final boolean answer) {
452            return new Predicate() {
453                @Override
454                public boolean matches(Exchange exchange) {
455                    return answer;
456                }
457    
458                @Override
459                public String toString() {
460                    return "" + answer;
461                }
462            };
463        }
464    }