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