Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
PredicateFactory |
|
| 1.8636363636363635;1.864 | ||||
PredicateFactory$DynPredicateException |
|
| 1.8636363636363635;1.864 |
1 | /** | |
2 | * Copyright 2005-2011 The Kuali Foundation | |
3 | * | |
4 | * Licensed under the Educational Community License, Version 2.0 (the "License"); | |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.opensource.org/licenses/ecl2.php | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | package org.kuali.rice.core.api.criteria; | |
17 | ||
18 | import org.apache.commons.collections.CollectionUtils; | |
19 | import org.apache.commons.lang.StringUtils; | |
20 | ||
21 | import java.lang.reflect.InvocationTargetException; | |
22 | import java.lang.reflect.Method; | |
23 | import java.util.HashSet; | |
24 | import java.util.Set; | |
25 | ||
26 | /** | |
27 | * This is a factory class to construct {@link Predicate Predicates}. | |
28 | * | |
29 | * <p> | |
30 | * For more readable predicate construction it is recommended that this class | |
31 | * is statically imported. | |
32 | * <code> | |
33 | * import static org.kuali.rice.core.api.criteria.PredicateFactory.*; | |
34 | * </code> | |
35 | * </p> | |
36 | * | |
37 | * to create a simple predicate where the property | |
38 | * foo.bar equals "baz" do the following: | |
39 | * <code> | |
40 | * Predicate simple = equals("foo.bar", "baz"); | |
41 | * </code> | |
42 | * | |
43 | * to create a compound predicate where the property | |
44 | * foo.bar equals "baz" and foo.id equals 1 do the following: | |
45 | * <code> | |
46 | * Predicate compound = and(equals("foo.bar", "baz"), equals("foo.id", 1)) | |
47 | * </code> | |
48 | * | |
49 | * to create a deeply nested predicate where lots of | |
50 | * properties are evaluated do the following: | |
51 | * | |
52 | * Predicate apiAuthors = | |
53 | * and( | |
54 | * like("display", "*Eric*"), | |
55 | * greaterThan("birthDate", gtBirthDate), | |
56 | * lessThan("birthDate", ltBirthDate), | |
57 | * or( | |
58 | * equal("name.first", "Travis"), | |
59 | * equal("name.last", "Schneeberger"))) | |
60 | * | |
61 | * <p> | |
62 | * <strong>WARNING:</strong> this class does automatic reductions | |
63 | * such that you cannot assume the concrete {@link Predicate} type | |
64 | * returned from this factory. | |
65 | * </p> | |
66 | * | |
67 | * @see QueryByCriteria | |
68 | */ | |
69 | public final class PredicateFactory { | |
70 | 0 | private PredicateFactory() { |
71 | 0 | throw new IllegalArgumentException("do not call"); |
72 | } | |
73 | ||
74 | /** | |
75 | * Creates an predicate representing equals comparison. Defines that the property | |
76 | * represented by the given path should be equal to the specified value. | |
77 | * | |
78 | * <p>Supports the following types of values: | |
79 | * | |
80 | * <ul> | |
81 | * <li>character data</li> | |
82 | * <li>decimals</li> | |
83 | * <li>integers</li> | |
84 | * <li>date-time</li> | |
85 | * </ul> | |
86 | * | |
87 | * @param propertyPath the path to the property which should be evaluated | |
88 | * @param value the value to compare with the property value located at the | |
89 | * propertyPath | |
90 | * | |
91 | * @return a predicate | |
92 | * | |
93 | * @throws IllegalArgumentException if the propertyPath is null | |
94 | * @throws IllegalArgumentException if the value is null or of an invalid type | |
95 | */ | |
96 | public static Predicate equal(String propertyPath, Object value) { | |
97 | 0 | return new EqualPredicate(propertyPath, CriteriaSupportUtils.determineCriteriaValue(value)); |
98 | } | |
99 | ||
100 | /** | |
101 | * Creates a predicate representing not equals comparison. Defines that the property | |
102 | * represented by the given path should <strong>not</strong> be | |
103 | * equal to the specified value. | |
104 | * | |
105 | * <p>Supports the following types of values: | |
106 | * | |
107 | * <ul> | |
108 | * <li>character data</li> | |
109 | * <li>decimals</li> | |
110 | * <li>integers</li> | |
111 | * <li>date-time</li> | |
112 | * </ul> | |
113 | * | |
114 | * @param propertyPath the path to the property which should be evaluated | |
115 | * @param value the value to compare with the property value located at the | |
116 | * propertyPath | |
117 | * | |
118 | * @return a predicate | |
119 | * | |
120 | * @throws IllegalArgumentException if the propertyPath is null | |
121 | * @throws IllegalArgumentException if the value is null or of an invalid type | |
122 | */ | |
123 | public static Predicate notEqual(String propertyPath, Object value) { | |
124 | 0 | return new NotEqualPredicate(propertyPath, CriteriaSupportUtils.determineCriteriaValue(value)); |
125 | } | |
126 | ||
127 | /** | |
128 | * Creates an equals ignore case predicate. Defines that the property | |
129 | * represented by the given path should be equal to the specified value ignoring | |
130 | * the case of the value. | |
131 | * | |
132 | * <p>Supports the following types of values: | |
133 | * | |
134 | * <ul> | |
135 | * <li>character data</li> | |
136 | * </ul> | |
137 | * | |
138 | * @param propertyPath the path to the property which should be evaluated | |
139 | * @param value the value to compare with the property value located at the | |
140 | * propertyPath | |
141 | * | |
142 | * @return a predicate | |
143 | * | |
144 | * @throws IllegalArgumentException if the propertyPath is null | |
145 | * @throws IllegalArgumentException if the value is null or of an invalid type | |
146 | */ | |
147 | public static Predicate equalIgnoreCase(String propertyPath, CharSequence value) { | |
148 | 0 | return new EqualIgnoreCasePredicate(propertyPath, new CriteriaStringValue(value)); |
149 | } | |
150 | ||
151 | /** | |
152 | * Creates a not equals ignore case predicate. Defines that the property | |
153 | * represented by the given path should <strong>not</strong> be | |
154 | * equal to the specified value ignoring the case of the value. | |
155 | * | |
156 | * <p>Supports the following types of values: | |
157 | * | |
158 | * <ul> | |
159 | * <li>character data</li> | |
160 | * </ul> | |
161 | * | |
162 | * @param propertyPath the path to the property which should be evaluated | |
163 | * @param value the value to compare with the property value located at the | |
164 | * propertyPath | |
165 | * | |
166 | * @return a predicate | |
167 | * | |
168 | * @throws IllegalArgumentException if the propertyPath is null | |
169 | * @throws IllegalArgumentException if the value is null or of an invalid type | |
170 | */ | |
171 | public static Predicate notEqualIgnoreCase(String propertyPath, CharSequence value) { | |
172 | 0 | return new NotEqualIgnoreCasePredicate(propertyPath, new CriteriaStringValue(value)); |
173 | } | |
174 | ||
175 | /** | |
176 | * Creates a like predicate. Defines that the property | |
177 | * represented by the given path should match the specified value, | |
178 | * but supports the use of wildcards in the given value. | |
179 | * | |
180 | * <p>The supported wildcards include: | |
181 | * | |
182 | * <ul> | |
183 | * <li><strong>?</strong> - matches on any single character</li> | |
184 | * <li><strong>*</strong> - matches any string of any length (including zero length)</li> | |
185 | * </ul> | |
186 | * | |
187 | * <p>Because of this, the like predicate only supports character data | |
188 | * for the passed-in value. | |
189 | * | |
190 | * @param propertyPath the path to the property which should be evaluated | |
191 | * @param value the value to compare with the property value located at the | |
192 | * propertyPath | |
193 | * | |
194 | * @return a predicate | |
195 | * | |
196 | * @throws IllegalArgumentException if the propertyPath is null | |
197 | * @throws IllegalArgumentException if the value is null | |
198 | */ | |
199 | public static Predicate like(String propertyPath, CharSequence value) { | |
200 | 0 | return new LikePredicate(propertyPath, CriteriaSupportUtils.determineCriteriaValue(value)); |
201 | } | |
202 | ||
203 | /** | |
204 | * Creates a not like predicate. Defines that the property | |
205 | * represented by the given path should <strong>not</strong> match the specified value, | |
206 | * but supports the use of wildcards in the given value. | |
207 | * | |
208 | * <p>The supported wildcards include: | |
209 | * | |
210 | * <ul> | |
211 | * <li><strong>?</strong> - matches on any single character</li> | |
212 | * <li><strong>*</strong> - matches any string of any length (including zero length)</li> | |
213 | * </ul> | |
214 | * | |
215 | * <p>Because of this, the like predicate only supports character data | |
216 | * for the passed-in value. | |
217 | * | |
218 | * @param propertyPath the path to the property which should be evaluated | |
219 | * @param value the value to compare with the property value located at the | |
220 | * propertyPath | |
221 | * | |
222 | * @return a predicate | |
223 | * | |
224 | * @throws IllegalArgumentException if the propertyPath is null | |
225 | * @throws IllegalArgumentException if the value is null | |
226 | */ | |
227 | public static Predicate notLike(String propertyPath, CharSequence value) { | |
228 | 0 | return new NotLikePredicate(propertyPath, CriteriaSupportUtils.determineCriteriaValue(value)); |
229 | } | |
230 | ||
231 | /** | |
232 | * Create an in predicate. Defines that the property | |
233 | * represented by the given path should be contained within the | |
234 | * specified list of values. | |
235 | * | |
236 | * <p>Supports any of the valid types of value in the value list, with the | |
237 | * restriction that all items in the list of values must be of the same type. | |
238 | * | |
239 | * @param propertyPath the path to the property which should be evaluated | |
240 | * @param values the values to compare with the property value located at the | |
241 | * propertyPath | |
242 | * | |
243 | * @return a predicate | |
244 | * | |
245 | * @throws IllegalArgumentException if the propertyPath is null | |
246 | * @throws IllegalArgumentException if the values list is null, empty, | |
247 | * contains object of different types, or includes objects of an invalid type | |
248 | */ | |
249 | public static <T> Predicate in(String propertyPath, T... values) { | |
250 | 0 | return new InPredicate(propertyPath, CriteriaSupportUtils.determineCriteriaValueList(values)); |
251 | } | |
252 | ||
253 | /** | |
254 | * Create a not in predicate. Defines that the property | |
255 | * represented by the given path should <strong>not</strong> be | |
256 | * contained within the specified list of values. | |
257 | * | |
258 | * <p>Supports any of the valid types of value in the value list, with the | |
259 | * restriction that all items in the list of values must be of the same type. | |
260 | * | |
261 | * @param propertyPath the path to the property which should be evaluated | |
262 | * @param values the values to compare with the property value located at the | |
263 | * propertyPath | |
264 | * | |
265 | * @return a predicate | |
266 | * | |
267 | * @throws IllegalArgumentException if the propertyPath is null | |
268 | * @throws IllegalArgumentException if the values list is null, empty, | |
269 | * contains object of different types, or includes objects of an invalid type | |
270 | */ | |
271 | public static <T> Predicate notIn(String propertyPath, T... values) { | |
272 | 0 | return new NotInPredicate(propertyPath, CriteriaSupportUtils.determineCriteriaValueList(values)); |
273 | } | |
274 | ||
275 | /** | |
276 | * Create an in ignore case predicate. Defines that the property | |
277 | * represented by the given path should be contained within the | |
278 | * specified list of values ignoring the case of the values. | |
279 | * | |
280 | * <p>Supports any of CharSequence value in the value list, with the | |
281 | * restriction that all items in the list of values must be of the same type. | |
282 | * | |
283 | * @param propertyPath the path to the property which should be evaluated | |
284 | * @param values the values to compare with the property value located at the | |
285 | * propertyPath | |
286 | * | |
287 | * @return a predicate | |
288 | * | |
289 | * @throws IllegalArgumentException if the propertyPath is null | |
290 | * @throws IllegalArgumentException if the values list is null, empty, | |
291 | * contains object of different types, or includes objects of an invalid type | |
292 | */ | |
293 | public static <T extends CharSequence> Predicate inIgnoreCase(String propertyPath, T... values) { | |
294 | 0 | return new InIgnoreCasePredicate(propertyPath, CriteriaSupportUtils.createCriteriaStringValueList(values)); |
295 | } | |
296 | ||
297 | /** | |
298 | * Create a not in ignore case. Defines that the property | |
299 | * represented by the given path should <strong>not</strong> be | |
300 | * contained within the specified list of values ignoring the case of the values. | |
301 | * | |
302 | * <p>Supports any CharSequence value in the value list, with the | |
303 | * restriction that all items in the list of values must be of the same type. | |
304 | * | |
305 | * @param propertyPath the path to the property which should be evaluated | |
306 | * @param values the values to compare with the property value located at the | |
307 | * propertyPath | |
308 | * | |
309 | * @return a predicate | |
310 | * | |
311 | * @throws IllegalArgumentException if the propertyPath is null | |
312 | * @throws IllegalArgumentException if the values list is null, empty, | |
313 | * contains object of different types, or includes objects of an invalid type | |
314 | */ | |
315 | public static <T extends CharSequence> Predicate notInIgnoreCase(String propertyPath, T... values) { | |
316 | 0 | return new NotInIgnoreCasePredicate(propertyPath, CriteriaSupportUtils.createCriteriaStringValueList(values)); |
317 | } | |
318 | ||
319 | /** | |
320 | * Creates a greater than predicate. Defines that the property | |
321 | * represented by the given path should be greater than the specified value. | |
322 | * | |
323 | * <p>Supports the following types of values: | |
324 | * | |
325 | * <ul> | |
326 | * <li>decimals</li> | |
327 | * <li>integers</li> | |
328 | * <li>date-time</li> | |
329 | * </ul> | |
330 | * | |
331 | * @param propertyPath the path to the property which should be evaluated | |
332 | * @param value the value to compare with the property value located at the | |
333 | * propertyPath | |
334 | * | |
335 | * @return a predicate | |
336 | * | |
337 | * @throws IllegalArgumentException if the propertyPath is null | |
338 | * @throws IllegalArgumentException if the value is null or of an invalid type | |
339 | */ | |
340 | public static Predicate greaterThan(String propertyPath, Object value) { | |
341 | 0 | return new GreaterThanPredicate(propertyPath, CriteriaSupportUtils.determineCriteriaValue(value)); |
342 | } | |
343 | ||
344 | /** | |
345 | * Creates a greater than or equal predicate. Defines that the | |
346 | * property represented by the given path should be greater than | |
347 | * or equal to the specified value. | |
348 | * | |
349 | * <p>Supports the following types of values: | |
350 | * | |
351 | * <ul> | |
352 | * <li>decimals</li> | |
353 | * <li>integers</li> | |
354 | * <li>date-time</li> | |
355 | * </ul> | |
356 | * | |
357 | * @param propertyPath the path to the property which should be evaluated | |
358 | * @param value the value to compare with the property value located at the | |
359 | * propertyPath | |
360 | * | |
361 | * @return a predicate | |
362 | * | |
363 | * @throws IllegalArgumentException if the propertyPath is null | |
364 | * @throws IllegalArgumentException if the value is null or of an invalid type | |
365 | */ | |
366 | public static Predicate greaterThanOrEqual(String propertyPath, Object value) { | |
367 | 0 | return new GreaterThanOrEqualPredicate(propertyPath, CriteriaSupportUtils.determineCriteriaValue(value)); |
368 | } | |
369 | ||
370 | /** | |
371 | * Creates a less than predicate. Defines that the property | |
372 | * represented by the given path should be less than the specified value. | |
373 | * | |
374 | * <p>Supports the following types of values: | |
375 | * | |
376 | * <ul> | |
377 | * <li>decimals</li> | |
378 | * <li>integers</li> | |
379 | * <li>date-time</li> | |
380 | * </ul> | |
381 | * | |
382 | * @param propertyPath the path to the property which should be evaluated | |
383 | * @param value the value to compare with the property value located at the | |
384 | * propertyPath | |
385 | * | |
386 | * @return a predicate | |
387 | * | |
388 | * @throws IllegalArgumentException if the propertyPath is null | |
389 | * @throws IllegalArgumentException if the value is null or of an invalid type | |
390 | */ | |
391 | public static Predicate lessThan(String propertyPath, Object value) { | |
392 | 0 | return new LessThanPredicate(propertyPath, CriteriaSupportUtils.determineCriteriaValue(value)); |
393 | } | |
394 | ||
395 | /** | |
396 | * Creates a less than or equal predicate. Defines that the | |
397 | * property represented by the given path should be less than | |
398 | * or equal to the specified value. | |
399 | * | |
400 | * <p>Supports the following types of values: | |
401 | * | |
402 | * <ul> | |
403 | * <li>decimals</li> | |
404 | * <li>integers</li> | |
405 | * <li>date-time</li> | |
406 | * </ul> | |
407 | * | |
408 | * @param propertyPath the path to the property which should be evaluated | |
409 | * @param value the value to compare with the property value located at the | |
410 | * propertyPath | |
411 | * | |
412 | * @return a predicate | |
413 | * | |
414 | * @throws IllegalArgumentException if the propertyPath is null | |
415 | * @throws IllegalArgumentException if the value is null or of an invalid type | |
416 | */ | |
417 | public static Predicate lessThanOrEqual(String propertyPath, Object value) { | |
418 | 0 | return new LessThanOrEqualPredicate(propertyPath, CriteriaSupportUtils.determineCriteriaValue(value)); |
419 | } | |
420 | ||
421 | /** | |
422 | * Creates an is null predicate. Defines that the | |
423 | * property represented by the given path should be null. | |
424 | * | |
425 | * @param propertyPath the path to the property which should be evaluated | |
426 | * | |
427 | * @return a predicate | |
428 | * | |
429 | * @throws IllegalArgumentException if the propertyPath is null | |
430 | */ | |
431 | public static Predicate isNull(String propertyPath) { | |
432 | 0 | return new NullPredicate(propertyPath); |
433 | } | |
434 | ||
435 | /** | |
436 | * Creates an is not null predicate. Defines that the property | |
437 | * represented by the given path should <strong>not</strong> be null. | |
438 | * | |
439 | * @param propertyPath the path to the property which should be evaluated | |
440 | * | |
441 | * @return a predicate | |
442 | * | |
443 | * @throws IllegalArgumentException if the propertyPath is null | |
444 | */ | |
445 | public static Predicate isNotNull(String propertyPath) { | |
446 | 0 | return new NotNullPredicate(propertyPath); |
447 | } | |
448 | ||
449 | /** | |
450 | * Creates an and predicate that is used to "and" predicates together. | |
451 | * | |
452 | * <p>An "and" predicate will evaluate the truth value of of it's | |
453 | * internal predicates and, if all of them evaluate to true, then | |
454 | * the and predicate itself should evaluate to true. The implementation | |
455 | * of an and predicate may short-circuit. | |
456 | * | |
457 | * <p> | |
458 | * This factory method does automatic reductions. | |
459 | * </p> | |
460 | * | |
461 | * @param predicates to "and" together | |
462 | * | |
463 | * @return a predicate | |
464 | */ | |
465 | public static Predicate and(Predicate... predicates) { | |
466 | //reduce single item compound | |
467 | 0 | if (predicates != null && predicates.length == 1 && predicates[0] != null) { |
468 | 0 | return predicates[0]; |
469 | } | |
470 | 0 | final Set<Predicate> predicateSet = new HashSet<Predicate>(); |
471 | 0 | CollectionUtils.addAll(predicateSet, predicates); |
472 | 0 | return new AndPredicate(predicateSet); |
473 | } | |
474 | ||
475 | /** | |
476 | * Creates an or predicate that is used to "or" predicate together. | |
477 | * | |
478 | * <p>An "or" predicate will evaluate the truth value of of it's | |
479 | * internal predicates and, if any one of them evaluate to true, then | |
480 | * the or predicate itself should evaluate to true. If all predicates | |
481 | * contained within the "or" evaluate to false, then the or iself will | |
482 | * evaluate to false. The implementation of an or predicate may | |
483 | * short-circuit. | |
484 | * | |
485 | * <p> | |
486 | * This factory method does automatic reductions. | |
487 | * </p> | |
488 | * @param predicates to "or" together | |
489 | * | |
490 | * @return a predicate | |
491 | */ | |
492 | public static Predicate or(Predicate... predicates) { | |
493 | //reduce single item compound | |
494 | 0 | if (predicates != null && predicates.length == 1 && predicates[0] != null) { |
495 | 0 | return predicates[0]; |
496 | } | |
497 | ||
498 | 0 | final Set<Predicate> predicateSet = new HashSet<Predicate>(); |
499 | 0 | CollectionUtils.addAll(predicateSet, predicates); |
500 | 0 | return new OrPredicate(predicateSet); |
501 | } | |
502 | ||
503 | /** | |
504 | * This method will construct a predicate based on the predicate name. | |
505 | * | |
506 | * ex: "or", Or, "OrPredicate" passing the arguments to the construction method. | |
507 | * | |
508 | * @param name the name of the predicate to create. | |
509 | * @param args the args required to construct the predicate | |
510 | * @return the Predicate | |
511 | */ | |
512 | public static Predicate dynConstruct(String name, Object... args) { | |
513 | 0 | if (StringUtils.isBlank(name)) { |
514 | 0 | throw new IllegalArgumentException("name is blank"); |
515 | } | |
516 | ||
517 | 0 | final String correctedName = CriteriaSupportUtils.findDynName(name); |
518 | 0 | for (Method m : PredicateFactory.class.getMethods()) { |
519 | ||
520 | //currently this class does NOT overload therefore this method doesn't have to worry about | |
521 | //overload resolution - just get the Method handle based on the passed in name | |
522 | 0 | if (m.getName().equals(correctedName)) { |
523 | try { | |
524 | 0 | return (Predicate) m.invoke(null, args); |
525 | 0 | } catch (InvocationTargetException e) { |
526 | 0 | throw new DynPredicateException(e); |
527 | 0 | } catch (IllegalAccessException e) { |
528 | 0 | throw new DynPredicateException(e); |
529 | } | |
530 | } | |
531 | } | |
532 | ||
533 | 0 | throw new DynPredicateException("predicate: " + name + " doesn't exist"); |
534 | } | |
535 | ||
536 | //this is really a fatal error (programming error) | |
537 | //and therefore is just a private exception not really meant to be caught | |
538 | private static class DynPredicateException extends RuntimeException { | |
539 | DynPredicateException(Throwable t) { | |
540 | 0 | super(t); |
541 | 0 | } |
542 | ||
543 | DynPredicateException(String s) { | |
544 | 0 | super(s); |
545 | 0 | } |
546 | } | |
547 | } |