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