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.builder;
018
019import java.util.Arrays;
020import java.util.List;
021import java.util.regex.Matcher;
022import java.util.regex.Pattern;
023
024import org.apache.camel.Exchange;
025import org.apache.camel.Expression;
026import org.apache.camel.Predicate;
027import org.apache.camel.util.ExpressionToPredicateAdapter;
028import org.apache.camel.util.ObjectHelper;
029
030import static org.apache.camel.util.ObjectHelper.notNull;
031
032
033/**
034 * A helper class for working with predicates
035 *
036 * @version 
037 */
038public 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 two predicates by a logical OR.
090     * If you want to combine multiple predicates see {@link #in(Predicate...)}
091     */
092    public static Predicate or(final Predicate left, final Predicate right) {
093        notNull(left, "left");
094        notNull(right, "right");
095        return new Predicate() {
096            public boolean matches(Exchange exchange) {
097                return left.matches(exchange) || right.matches(exchange);
098            }
099
100            @Override
101            public String toString() {
102                return "(" + left + ") or (" + right + ")";
103            }
104        };
105    }
106
107    /**
108     * Concat the given predicates into a single predicate, which matches
109     * if at least one predicates matches.
110     *
111     * @param predicates predicates
112     * @return a single predicate containing all the predicates
113     */
114    public static Predicate or(List<Predicate> predicates) {
115        Predicate answer = null;
116        for (Predicate predicate : predicates) {
117            if (answer == null) {
118                answer = predicate;
119            } else {
120                answer = or(answer, predicate);
121            }
122        }
123        return answer;
124    }
125
126    /**
127     * Concat the given predicates into a single predicate, which matches
128     * if at least one predicates matches.
129     *
130     * @param predicates predicates
131     * @return a single predicate containing all the predicates
132     */
133    public static Predicate or(Predicate... predicates) {
134        return or(Arrays.asList(predicates));
135    }
136
137    /**
138     * A helper method to return true if any of the predicates matches.
139     */
140    public static Predicate in(final Predicate... predicates) {
141        notNull(predicates, "predicates");
142
143        return new Predicate() {
144            public boolean matches(Exchange exchange) {
145                for (Predicate in : predicates) {
146                    if (in.matches(exchange)) {
147                        return true;
148                    }
149                }
150                return false;
151            }
152
153            @Override
154            public String toString() {
155                return "in (" + Arrays.asList(predicates) + ")";
156            }
157        };
158    }
159
160    /**
161     * A helper method to return true if any of the predicates matches.
162     */
163    public static Predicate in(List<Predicate> predicates) {
164        return in(predicates.toArray(new Predicate[0]));
165    }
166
167    public static Predicate isEqualTo(final Expression left, final Expression right) {
168        return new BinaryPredicateSupport(left, right) {
169
170            protected boolean matches(Exchange exchange, Object leftValue, Object rightValue) {
171                if (leftValue == null && rightValue == null) {
172                    // they are equal
173                    return true;
174                } else if (leftValue == null || rightValue == null) {
175                    // only one of them is null so they are not equal
176                    return false;
177                }
178
179                return ObjectHelper.typeCoerceEquals(exchange.getContext().getTypeConverter(), leftValue, rightValue);
180            }
181
182            protected String getOperationText() {
183                return "==";
184            }
185        };
186    }
187
188    public static Predicate isEqualToIgnoreCase(final Expression left, final Expression right) {
189        return new BinaryPredicateSupport(left, right) {
190
191            protected boolean matches(Exchange exchange, Object leftValue, Object rightValue) {
192                if (leftValue == null && rightValue == null) {
193                    // they are equal
194                    return true;
195                } else if (leftValue == null || rightValue == null) {
196                    // only one of them is null so they are not equal
197                    return false;
198                }
199
200                return ObjectHelper.typeCoerceEquals(exchange.getContext().getTypeConverter(), leftValue, rightValue, true);
201            }
202
203            protected String getOperationText() {
204                return "=~";
205            }
206        };
207    }
208
209    public static Predicate isNotEqualTo(final Expression left, final Expression right) {
210        return new BinaryPredicateSupport(left, right) {
211
212            protected boolean matches(Exchange exchange, Object leftValue, Object rightValue) {
213                if (leftValue == null && rightValue == null) {
214                    // they are equal
215                    return false;
216                } else if (leftValue == null || rightValue == null) {
217                    // only one of them is null so they are not equal
218                    return true;
219                }
220
221                return ObjectHelper.typeCoerceNotEquals(exchange.getContext().getTypeConverter(), leftValue, rightValue);
222            }
223
224            protected String getOperationText() {
225                return "!=";
226            }
227        };
228    }
229
230    public static Predicate isLessThan(final Expression left, final Expression right) {
231        return new BinaryPredicateSupport(left, right) {
232
233            protected boolean matches(Exchange exchange, Object leftValue, Object rightValue) {
234                if (leftValue == null && rightValue == null) {
235                    // they are equal
236                    return true;
237                } else if (leftValue == null || rightValue == null) {
238                    // only one of them is null so they are not equal
239                    return false;
240                }
241
242                return ObjectHelper.typeCoerceCompare(exchange.getContext().getTypeConverter(), leftValue, rightValue) < 0;
243            }
244
245            protected String getOperationText() {
246                return "<";
247            }
248        };
249    }
250
251    public static Predicate isLessThanOrEqualTo(final Expression left, final Expression right) {
252        return new BinaryPredicateSupport(left, right) {
253
254            protected boolean matches(Exchange exchange, Object leftValue, Object rightValue) {
255                if (leftValue == null && rightValue == null) {
256                    // they are equal
257                    return true;
258                } else if (leftValue == null || rightValue == null) {
259                    // only one of them is null so they are not equal
260                    return false;
261                }
262
263                return ObjectHelper.typeCoerceCompare(exchange.getContext().getTypeConverter(), leftValue, rightValue) <= 0;
264            }
265
266            protected String getOperationText() {
267                return "<=";
268            }
269        };
270    }
271
272    public static Predicate isGreaterThan(final Expression left, final Expression right) {
273        return new BinaryPredicateSupport(left, right) {
274
275            protected boolean matches(Exchange exchange, Object leftValue, Object rightValue) {
276                if (leftValue == null && rightValue == null) {
277                    // they are equal
278                    return false;
279                } else if (leftValue == null || rightValue == null) {
280                    // only one of them is null so they are not equal
281                    return false;
282                }
283
284                return ObjectHelper.typeCoerceCompare(exchange.getContext().getTypeConverter(), leftValue, rightValue) > 0;
285            }
286
287            protected String getOperationText() {
288                return ">";
289            }
290        };
291    }
292
293    public static Predicate isGreaterThanOrEqualTo(final Expression left, final Expression right) {
294        return new BinaryPredicateSupport(left, right) {
295
296            protected boolean matches(Exchange exchange, Object leftValue, Object rightValue) {
297                if (leftValue == null && rightValue == null) {
298                    // they are equal
299                    return true;
300                } else if (leftValue == null || rightValue == null) {
301                    // only one of them is null so they are not equal
302                    return false;
303                }
304
305                return ObjectHelper.typeCoerceCompare(exchange.getContext().getTypeConverter(), leftValue, rightValue) >= 0;
306            }
307
308            protected String getOperationText() {
309                return ">=";
310            }
311        };
312    }
313
314    public static Predicate contains(final Expression left, final Expression right) {
315        return new BinaryPredicateSupport(left, right) {
316
317            protected boolean matches(Exchange exchange, Object leftValue, Object rightValue) {
318                if (leftValue == null && rightValue == null) {
319                    // they are equal
320                    return true;
321                } else if (leftValue == null || rightValue == null) {
322                    // only one of them is null so they are not equal
323                    return false;
324                }
325
326                return ObjectHelper.contains(leftValue, rightValue);
327            }
328
329            protected String getOperationText() {
330                return "contains";
331            }
332        };
333    }
334
335    public static Predicate isNull(final Expression expression) {
336        return new BinaryPredicateSupport(expression, ExpressionBuilder.constantExpression(null)) {
337
338            protected boolean matches(Exchange exchange, Object leftValue, Object rightValue) {
339                if (leftValue == null) {
340                    // the left operator is null so its true
341                    return true;
342                } 
343
344                return ObjectHelper.typeCoerceEquals(exchange.getContext().getTypeConverter(), leftValue, rightValue);
345            }
346
347            protected String getOperationText() {
348                // leave the operation text as "is not" as Camel will insert right and left expression around it
349                // so it will be displayed as: XXX is null
350                return "is";
351            }
352        };
353    }
354
355    public static Predicate isNotNull(final Expression expression) {
356        return new BinaryPredicateSupport(expression, ExpressionBuilder.constantExpression(null)) {
357
358            protected boolean matches(Exchange exchange, Object leftValue, Object rightValue) {
359                if (leftValue != null) {
360                    // the left operator is not null so its true
361                    return true;
362                }
363
364                return ObjectHelper.typeCoerceNotEquals(exchange.getContext().getTypeConverter(), leftValue, rightValue);
365            }
366
367            protected String getOperationText() {
368                // leave the operation text as "is not" as Camel will insert right and left expression around it
369                // so it will be displayed as: XXX is not null
370                return "is not";
371            }
372        };
373    }
374
375    public static Predicate isInstanceOf(final Expression expression, final Class<?> type) {
376        notNull(expression, "expression");
377        notNull(type, "type");
378
379        return new Predicate() {
380            public boolean matches(Exchange exchange) {
381                Object value = expression.evaluate(exchange, Object.class);
382                return type.isInstance(value);
383            }
384
385            @Override
386            public String toString() {
387                return expression + " instanceof " + type.getCanonicalName();
388            }
389        };
390    }
391
392    public static Predicate startsWith(final Expression left, final Expression right) {
393        return new BinaryPredicateSupport(left, right) {
394
395            protected boolean matches(Exchange exchange, Object leftValue, Object rightValue) {
396                if (leftValue == null && rightValue == null) {
397                    // they are equal
398                    return true;
399                } else if (leftValue == null || rightValue == null) {
400                    // only one of them is null so they are not equal
401                    return false;
402                }
403                String leftStr = exchange.getContext().getTypeConverter().convertTo(String.class, leftValue);
404                String rightStr = exchange.getContext().getTypeConverter().convertTo(String.class, rightValue);
405                if (leftStr != null && rightStr != null) {
406                    return leftStr.startsWith(rightStr);
407                } else {
408                    return false;
409                }
410            }
411
412            protected String getOperationText() {
413                return "startsWith";
414            }
415        };
416    }
417
418    public static Predicate endsWith(final Expression left, final Expression right) {
419        return new BinaryPredicateSupport(left, right) {
420
421            protected boolean matches(Exchange exchange, Object leftValue, Object rightValue) {
422                if (leftValue == null && rightValue == null) {
423                    // they are equal
424                    return true;
425                } else if (leftValue == null || rightValue == null) {
426                    // only one of them is null so they are not equal
427                    return false;
428                }
429                String leftStr = exchange.getContext().getTypeConverter().convertTo(String.class, leftValue);
430                String rightStr = exchange.getContext().getTypeConverter().convertTo(String.class, rightValue);
431                if (leftStr != null && rightStr != null) {
432                    return leftStr.endsWith(rightStr);
433                } else {
434                    return false;
435                }
436            }
437
438            protected String getOperationText() {
439                return "endsWith";
440            }
441        };
442    }
443
444    /**
445     * Returns a predicate which is true if the expression matches the given
446     * regular expression
447     *
448     * @param expression the expression to evaluate
449     * @param regex the regular expression to match against
450     * @return a new predicate
451     */
452    public static Predicate regex(final Expression expression, final String regex) {
453        return regex(expression, Pattern.compile(regex));
454    }
455
456    /**
457     * Returns a predicate which is true if the expression matches the given
458     * regular expression
459     *
460     * @param expression the expression to evaluate
461     * @param pattern the regular expression to match against
462     * @return a new predicate
463     */
464    public static Predicate regex(final Expression expression, final Pattern pattern) {
465        notNull(expression, "expression");
466        notNull(pattern, "pattern");
467
468        return new Predicate() {
469            public boolean matches(Exchange exchange) {
470                String value = expression.evaluate(exchange, String.class);
471                if (value != null) {
472                    Matcher matcher = pattern.matcher(value);
473                    return matcher.matches();
474                }
475                return false;
476            }
477
478            @Override
479            public String toString() {
480                return expression + ".matches('" + pattern + "')";
481            }
482        };
483    }
484
485    /**
486     * Concat the given predicates into a single predicate, which
487     * only matches if all the predicates matches.
488     *
489     * @param predicates predicates
490     * @return a single predicate containing all the predicates
491     */
492    public static Predicate and(List<Predicate> predicates) {
493        Predicate answer = null;
494        for (Predicate predicate : predicates) {
495            if (answer == null) {
496                answer = predicate;
497            } else {
498                answer = and(answer, predicate);
499            }
500        }
501        return answer;
502    }
503
504    /**
505     * Concat the given predicates into a single predicate, which only matches
506     * if all the predicates matches.
507     *
508     * @param predicates predicates
509     * @return a single predicate containing all the predicates
510     */
511    public static Predicate and(Predicate... predicates) {
512        return and(Arrays.asList(predicates));
513    }
514
515    /**
516     * A constant predicate.
517     *
518     * @param answer the constant matches
519     * @return a predicate that always returns the given answer.
520     */
521    public static Predicate constant(final boolean answer) {
522        return new Predicate() {
523            @Override
524            public boolean matches(Exchange exchange) {
525                return answer;
526            }
527
528            @Override
529            public String toString() {
530                return "" + answer;
531            }
532        };
533    }
534}