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 isNull(final Expression expression) {
335        return new BinaryPredicateSupport(expression, ExpressionBuilder.constantExpression(null)) {
336
337            protected boolean matches(Exchange exchange, Object leftValue, Object rightValue) {
338                if (leftValue == null) {
339                    // the left operator is null so its true
340                    return true;
341                } 
342
343                return ObjectHelper.typeCoerceEquals(exchange.getContext().getTypeConverter(), leftValue, rightValue);
344            }
345
346            protected String getOperationText() {
347                // leave the operation text as "is not" as Camel will insert right and left expression around it
348                // so it will be displayed as: XXX is null
349                return "is";
350            }
351        };
352    }
353
354    public static Predicate isNotNull(final Expression expression) {
355        return new BinaryPredicateSupport(expression, ExpressionBuilder.constantExpression(null)) {
356
357            protected boolean matches(Exchange exchange, Object leftValue, Object rightValue) {
358                if (leftValue != null) {
359                    // the left operator is not null so its true
360                    return true;
361                }
362
363                return ObjectHelper.typeCoerceNotEquals(exchange.getContext().getTypeConverter(), leftValue, rightValue);
364            }
365
366            protected String getOperationText() {
367                // leave the operation text as "is not" as Camel will insert right and left expression around it
368                // so it will be displayed as: XXX is not null
369                return "is not";
370            }
371        };
372    }
373
374    public static Predicate isInstanceOf(final Expression expression, final Class<?> type) {
375        notNull(expression, "expression");
376        notNull(type, "type");
377
378        return new Predicate() {
379            public boolean matches(Exchange exchange) {
380                Object value = expression.evaluate(exchange, Object.class);
381                return type.isInstance(value);
382            }
383
384            @Override
385            public String toString() {
386                return expression + " instanceof " + type.getCanonicalName();
387            }
388        };
389    }
390
391    public static Predicate startsWith(final Expression left, final Expression right) {
392        return new BinaryPredicateSupport(left, right) {
393
394            protected boolean matches(Exchange exchange, Object leftValue, Object rightValue) {
395                if (leftValue == null && rightValue == null) {
396                    // they are equal
397                    return true;
398                } else if (leftValue == null || rightValue == null) {
399                    // only one of them is null so they are not equal
400                    return false;
401                }
402                String leftStr = exchange.getContext().getTypeConverter().convertTo(String.class, leftValue);
403                String rightStr = exchange.getContext().getTypeConverter().convertTo(String.class, rightValue);
404                if (leftStr != null && rightStr != null) {
405                    return leftStr.startsWith(rightStr);
406                } else {
407                    return false;
408                }
409            }
410
411            protected String getOperationText() {
412                return "startsWith";
413            }
414        };
415    }
416
417    public static Predicate endsWith(final Expression left, final Expression right) {
418        return new BinaryPredicateSupport(left, right) {
419
420            protected boolean matches(Exchange exchange, Object leftValue, Object rightValue) {
421                if (leftValue == null && rightValue == null) {
422                    // they are equal
423                    return true;
424                } else if (leftValue == null || rightValue == null) {
425                    // only one of them is null so they are not equal
426                    return false;
427                }
428                String leftStr = exchange.getContext().getTypeConverter().convertTo(String.class, leftValue);
429                String rightStr = exchange.getContext().getTypeConverter().convertTo(String.class, rightValue);
430                if (leftStr != null && rightStr != null) {
431                    return leftStr.endsWith(rightStr);
432                } else {
433                    return false;
434                }
435            }
436
437            protected String getOperationText() {
438                return "endsWith";
439            }
440        };
441    }
442
443    /**
444     * Returns a predicate which is true if the expression matches the given
445     * regular expression
446     *
447     * @param expression the expression to evaluate
448     * @param regex the regular expression to match against
449     * @return a new predicate
450     */
451    public static Predicate regex(final Expression expression, final String regex) {
452        return regex(expression, Pattern.compile(regex));
453    }
454
455    /**
456     * Returns a predicate which is true if the expression matches the given
457     * regular expression
458     *
459     * @param expression the expression to evaluate
460     * @param pattern the regular expression to match against
461     * @return a new predicate
462     */
463    public static Predicate regex(final Expression expression, final Pattern pattern) {
464        notNull(expression, "expression");
465        notNull(pattern, "pattern");
466
467        return new Predicate() {
468            public boolean matches(Exchange exchange) {
469                String value = expression.evaluate(exchange, String.class);
470                if (value != null) {
471                    Matcher matcher = pattern.matcher(value);
472                    return matcher.matches();
473                }
474                return false;
475            }
476
477            @Override
478            public String toString() {
479                return expression + ".matches('" + pattern + "')";
480            }
481        };
482    }
483
484    /**
485     * Concat the given predicates into a single predicate, which
486     * only matches if all the predicates matches.
487     *
488     * @param predicates predicates
489     * @return a single predicate containing all the predicates
490     */
491    public static Predicate and(List<Predicate> predicates) {
492        Predicate answer = null;
493        for (Predicate predicate : predicates) {
494            if (answer == null) {
495                answer = predicate;
496            } else {
497                answer = and(answer, predicate);
498            }
499        }
500        return answer;
501    }
502
503    /**
504     * Concat the given predicates into a single predicate, which only matches
505     * if all the predicates matches.
506     *
507     * @param predicates predicates
508     * @return a single predicate containing all the predicates
509     */
510    public static Predicate and(Predicate... predicates) {
511        return and(Arrays.asList(predicates));
512    }
513
514    /**
515     * A constant predicate.
516     *
517     * @param answer the constant matches
518     * @return a predicate that always returns the given answer.
519     */
520    public static Predicate constant(final boolean answer) {
521        return new Predicate() {
522            @Override
523            public boolean matches(Exchange exchange) {
524                return answer;
525            }
526
527            @Override
528            public String toString() {
529                return "" + answer;
530            }
531        };
532    }
533}