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