1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.core.api.criteria;
17
18 import java.io.Serializable;
19 import java.lang.reflect.InvocationTargetException;
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.Collection;
23 import java.util.HashMap;
24 import java.util.List;
25 import java.util.Map;
26
27 import javax.xml.bind.annotation.XmlAccessType;
28 import javax.xml.bind.annotation.XmlAccessorType;
29 import javax.xml.bind.annotation.XmlAnyElement;
30 import javax.xml.bind.annotation.XmlElement;
31 import javax.xml.bind.annotation.XmlElementWrapper;
32 import javax.xml.bind.annotation.XmlElements;
33 import javax.xml.bind.annotation.XmlRootElement;
34 import javax.xml.bind.annotation.XmlType;
35 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
36
37 import org.apache.commons.beanutils.PropertyUtils;
38 import org.kuali.rice.core.api.CoreConstants;
39 import org.kuali.rice.core.api.mo.AbstractDataTransferObject;
40 import org.kuali.rice.core.api.mo.ModelBuilder;
41 import org.kuali.rice.core.api.util.collect.CollectionUtils;
42 import org.w3c.dom.Element;
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66 @XmlRootElement(name = QueryByCriteria.Constants.ROOT_ELEMENT_NAME)
67 @XmlAccessorType(XmlAccessType.NONE)
68 @XmlType(name = QueryByCriteria.Constants.TYPE_NAME, propOrder = {
69 QueryByCriteria.Elements.PREDICATE,
70 QueryByCriteria.Elements.START_AT_INDEX,
71 QueryByCriteria.Elements.MAX_RESULTS,
72 QueryByCriteria.Elements.COUNT_FLAG,
73 QueryByCriteria.Elements.ORDER_BY_FIELDS,
74 CoreConstants.CommonElements.FUTURE_ELEMENTS })
75 public final class QueryByCriteria extends AbstractDataTransferObject {
76
77 private static final long serialVersionUID = 2210627777648920180L;
78
79 @XmlElements(value = {
80 @XmlElement(name = AndPredicate.Constants.ROOT_ELEMENT_NAME, type = AndPredicate.class, required = false),
81 @XmlElement(name = EqualPredicate.Constants.ROOT_ELEMENT_NAME, type = EqualPredicate.class, required = false),
82 @XmlElement(name = EqualIgnoreCasePredicate.Constants.ROOT_ELEMENT_NAME, type = EqualIgnoreCasePredicate.class, required = false),
83 @XmlElement(name = ExistsSubQueryPredicate.Constants.ROOT_ELEMENT_NAME, type = ExistsSubQueryPredicate.class, required = false),
84 @XmlElement(name = GreaterThanPredicate.Constants.ROOT_ELEMENT_NAME, type = GreaterThanPredicate.class, required = false),
85 @XmlElement(name = GreaterThanOrEqualPredicate.Constants.ROOT_ELEMENT_NAME, type = GreaterThanOrEqualPredicate.class, required = false),
86 @XmlElement(name = InPredicate.Constants.ROOT_ELEMENT_NAME, type = InPredicate.class, required = false),
87 @XmlElement(name = InIgnoreCasePredicate.Constants.ROOT_ELEMENT_NAME, type = InIgnoreCasePredicate.class, required = false),
88 @XmlElement(name = LessThanPredicate.Constants.ROOT_ELEMENT_NAME, type = LessThanPredicate.class, required = false),
89 @XmlElement(name = LessThanOrEqualPredicate.Constants.ROOT_ELEMENT_NAME, type = LessThanOrEqualPredicate.class, required = false),
90 @XmlElement(name = LikePredicate.Constants.ROOT_ELEMENT_NAME, type = LikePredicate.class, required = false),
91 @XmlElement(name = LikeIgnoreCasePredicate.Constants.ROOT_ELEMENT_NAME, type = LikeIgnoreCasePredicate.class, required = false),
92 @XmlElement(name = NotEqualPredicate.Constants.ROOT_ELEMENT_NAME, type = NotEqualPredicate.class, required = false),
93 @XmlElement(name = NotEqualIgnoreCasePredicate.Constants.ROOT_ELEMENT_NAME, type = NotEqualIgnoreCasePredicate.class, required = false),
94 @XmlElement(name = NotInPredicate.Constants.ROOT_ELEMENT_NAME, type = NotInPredicate.class, required = false),
95 @XmlElement(name = NotInIgnoreCasePredicate.Constants.ROOT_ELEMENT_NAME, type = NotInIgnoreCasePredicate.class, required = false),
96 @XmlElement(name = NotLikeIgnoreCasePredicate.Constants.ROOT_ELEMENT_NAME, type = NotLikeIgnoreCasePredicate.class, required = false),
97 @XmlElement(name = NotLikePredicate.Constants.ROOT_ELEMENT_NAME, type = NotLikePredicate.class, required = false),
98 @XmlElement(name = NotNullPredicate.Constants.ROOT_ELEMENT_NAME, type = NotNullPredicate.class, required = false),
99 @XmlElement(name = NullPredicate.Constants.ROOT_ELEMENT_NAME, type = NullPredicate.class, required = false),
100 @XmlElement(name = OrPredicate.Constants.ROOT_ELEMENT_NAME, type = OrPredicate.class, required = false)
101 })
102 private final Predicate predicate;
103
104 @XmlElement(name = Elements.START_AT_INDEX, required = false)
105 private final Integer startAtIndex;
106
107 @XmlElement(name = Elements.MAX_RESULTS, required = false)
108 private final Integer maxResults;
109
110 @XmlJavaTypeAdapter(CountFlag.Adapter.class)
111 @XmlElement(name = Elements.COUNT_FLAG, required = true)
112 private final String countFlag;
113
114 @XmlElementWrapper(name = Elements.ORDER_BY_FIELDS, required = false)
115 @XmlElement(name = Elements.ORDER_BY_FIELD, required = false)
116 private final List<OrderByField> orderByFields;
117
118
119 @SuppressWarnings("unused")
120 @XmlAnyElement
121 private final Collection<Element> _futureElements = null;
122
123 private QueryByCriteria() {
124 this.predicate = null;
125 this.startAtIndex = null;
126 this.maxResults = null;
127 this.countFlag = null;
128 this.orderByFields = null;
129 }
130
131 private QueryByCriteria(Builder builder) {
132 final Predicate[] preds = builder.predicates;
133 if (preds != null && preds.length > 1) {
134
135 this.predicate = PredicateFactory.and(builder.predicates);
136 } else if (preds != null && preds.length == 1) {
137 this.predicate = builder.predicates[0];
138 } else {
139 this.predicate = null;
140 }
141
142 this.startAtIndex = builder.getStartAtIndex();
143 this.maxResults = builder.getMaxResults();
144 this.countFlag = builder.getCountFlag() == null ? null : builder.getCountFlag().getFlag();
145 this.orderByFields = new ArrayList<OrderByField>(builder.getOrderByFields());
146 }
147
148
149
150
151
152
153 public Predicate getPredicate() {
154 return this.predicate;
155 }
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172 public Integer getStartAtIndex() {
173 return this.startAtIndex;
174 }
175
176
177
178
179
180
181
182
183
184
185
186
187 public Integer getMaxResults() {
188 return this.maxResults;
189 }
190
191
192
193
194
195
196
197
198
199
200 public CountFlag getCountFlag() {
201 return this.countFlag == null ? null : CountFlag.valueOf(this.countFlag);
202 }
203
204
205
206
207
208
209
210 public List<OrderByField> getOrderByFields() {
211 return CollectionUtils.unmodifiableListNullSafe(this.orderByFields);
212 }
213
214 public static final class Builder implements ModelBuilder, Serializable {
215
216 private Predicate[] predicates;
217 private Integer startAtIndex;
218 private Integer maxResults;
219 private CountFlag countFlag;
220 private List<OrderByField> orderByFields;
221
222 private Builder() {
223 setCountFlag(CountFlag.NONE);
224 setOrderByFields(new ArrayList<OrderByField>());
225 }
226
227 public static Builder create() {
228 return new Builder();
229 }
230
231 public static Builder create(QueryByCriteria queryByCriteria) {
232 Builder builder = new Builder();
233 builder.setPredicates(queryByCriteria.getPredicate());
234 builder.setStartAtIndex(queryByCriteria.getStartAtIndex());
235 builder.setMaxResults(queryByCriteria.getMaxResults());
236 builder.setCountFlag(queryByCriteria.getCountFlag());
237 builder.setOrderByFields(queryByCriteria.getOrderByFields());
238 return builder;
239 }
240
241 public Integer getStartAtIndex() {
242 return this.startAtIndex;
243 }
244
245 public void setStartAtIndex(Integer startAtIndex) {
246 if (startAtIndex != null && startAtIndex < 0) {
247 throw new IllegalArgumentException("startAtIndex < 0");
248 }
249
250 this.startAtIndex = startAtIndex;
251 }
252
253 public Integer getMaxResults() {
254 return this.maxResults;
255 }
256
257 public void setMaxResults(Integer maxResults) {
258 if (maxResults != null && maxResults < 0) {
259 throw new IllegalArgumentException("maxResults < 0");
260 }
261
262 this.maxResults = maxResults;
263 }
264
265 public CountFlag getCountFlag() {
266 return this.countFlag;
267 }
268
269 public QueryByCriteria.Builder setCountFlag(CountFlag countFlag) {
270 if (countFlag == null) {
271 throw new IllegalArgumentException("countFlag was null");
272 }
273 this.countFlag = countFlag;
274 return this;
275 }
276
277 public List<OrderByField> getOrderByFields() {
278 return this.orderByFields;
279 }
280
281 public QueryByCriteria.Builder setOrderByFields(List<OrderByField> orderByFields) {
282 if (orderByFields == null) {
283 throw new IllegalArgumentException("orderByFields was null");
284 }
285 this.orderByFields = orderByFields;
286 return this;
287 }
288
289 public QueryByCriteria.Builder setOrderByFields(OrderByField... orderByFields) {
290 if (orderByFields == null) {
291 throw new IllegalArgumentException("orderByFields was null");
292 }
293 setOrderByFields(new ArrayList<OrderByField>(Arrays.asList(orderByFields)));
294 return this;
295 }
296
297 public QueryByCriteria.Builder setOrderByAscending(String... orderByFields) {
298 if (orderByFields == null) {
299 throw new IllegalArgumentException("orderByFields was null");
300 }
301 List<OrderByField> obf = new ArrayList<OrderByField>(orderByFields.length);
302 for ( String fieldName : orderByFields ) {
303 obf.add(OrderByField.Builder.create(fieldName, OrderDirection.ASCENDING).build());
304 }
305 setOrderByFields(obf);
306 return this;
307 }
308
309
310
311
312
313 public Predicate[] getPredicates() {
314 if (this.predicates == null) {
315 return null;
316 }
317
318
319 return Arrays.copyOf(predicates, predicates.length);
320 }
321
322
323
324
325
326
327
328 public QueryByCriteria.Builder setPredicates(Predicate... predicates) {
329
330 this.predicates = predicates != null ? Arrays.copyOf(predicates, predicates.length) : null;
331 return this;
332 }
333
334 @Override
335 public QueryByCriteria build() {
336 return new QueryByCriteria(this);
337 }
338
339
340 public static QueryByCriteria fromPredicates(Predicate... predicates) {
341 final Builder b = Builder.create();
342 b.setPredicates(predicates);
343 return b.build();
344 }
345
346
347 public static QueryByCriteria fromPredicates(Collection<Predicate> predicates) {
348 final Builder b = Builder.create();
349 if ( predicates != null ) {
350 b.setPredicates(predicates.toArray(new Predicate[predicates.size()]));
351 } else {
352 b.setPredicates( (Predicate[])null );
353 }
354 return b.build();
355 }
356
357
358
359
360
361
362
363
364
365 public static QueryByCriteria.Builder orAttributes(Map<String, ?> attributes) {
366 List<Predicate> predicates = new ArrayList<Predicate>();
367 if (attributes != null) {
368 for (Map.Entry<String, ?> entry: attributes.entrySet()) {
369 if(entry.getValue() instanceof Collection<?>){
370 for(Object entryVal : (Collection<?>)entry.getValue()) {
371 predicates.add(buildPredicate(entry.getKey(),entryVal));
372 }
373 } else {
374 predicates.add(buildPredicate(entry.getKey(),entry.getValue()));
375 }
376 }
377 }
378 QueryByCriteria.Builder qbc = QueryByCriteria.Builder.create();
379 qbc.setPredicates(PredicateFactory.or(predicates.toArray(new Predicate[predicates.size()])));
380 return qbc;
381 }
382
383
384
385
386
387
388
389
390
391
392 public static QueryByCriteria.Builder andAttributes(Map<String, ?> attributes) {
393 List<Predicate> predicates = new ArrayList<Predicate>();
394 if (attributes != null) {
395 for (Map.Entry<String, ?> entry: attributes.entrySet()) {
396 if(entry.getValue() instanceof Collection<?>) {
397 Collection<?> values = (Collection<?>)entry.getValue();
398 if (!values.isEmpty()) {
399 List<Predicate> orPredicates = new ArrayList<Predicate>();
400 for(Object entryVal : values) {
401 orPredicates.add(buildPredicate(entry.getKey(),entryVal));
402 }
403 predicates.add(PredicateFactory.or(orPredicates.toArray(new Predicate[orPredicates.size()])));
404 }
405 } else {
406 predicates.add(buildPredicate(entry.getKey(),entry.getValue()));
407 }
408 }
409 }
410 QueryByCriteria.Builder qbc = QueryByCriteria.Builder.create();
411 qbc.setPredicates(PredicateFactory.and(predicates.toArray(new Predicate[predicates.size()])));
412 return qbc;
413 }
414
415 private static Predicate buildPredicate(String attributeKey, Object attributeValue){
416 if(attributeValue == null){
417 return PredicateFactory.isNull(attributeKey);
418 } else {
419 return PredicateFactory.equal(attributeKey,attributeValue);
420 }
421
422 }
423
424
425
426
427
428
429
430 public static QueryByCriteria.Builder forAttribute(String name, Object value) {
431 Map<String, Object> attrib = new HashMap<String, Object>();
432 attrib.put(name, value);
433 return andAttributes(attrib);
434 }
435
436
437
438
439
440
441
442
443 public static QueryByCriteria.Builder orAttributes(Object object, Collection<String> attributes) {
444 return orAttributes(getAttributeValueMap(object, attributes));
445 }
446
447
448
449
450
451
452
453
454 public static QueryByCriteria.Builder andAttributes(Object object, Collection<String> attributes) {
455 return andAttributes(getAttributeValueMap(object, attributes));
456 }
457
458
459
460
461
462
463
464
465 private static Map<String, ?> getAttributeValueMap(Object object, Collection<String> attribNames) {
466 Map<String, Object> attributeMap = new HashMap<String, Object>();
467 for (String attr: attribNames) {
468 Object value;
469 try {
470 value = PropertyUtils.getProperty(object, attr);
471 } catch (IllegalAccessException iae) {
472 throw new RuntimeException(iae);
473 } catch (InvocationTargetException ite) {
474 throw new RuntimeException(ite);
475 } catch (NoSuchMethodException nsme) {
476 throw new RuntimeException(nsme);
477 }
478 attributeMap.put(attr, value);
479 }
480 return attributeMap;
481 }
482 }
483
484
485
486
487 static class Constants {
488 final static String ROOT_ELEMENT_NAME = "queryByCriteria";
489 final static String TYPE_NAME = "QueryByCriteriaType";
490 }
491
492
493
494
495
496 static class Elements {
497 final static String PREDICATE = "predicate";
498 final static String START_AT_INDEX = "startAtIndex";
499 final static String MAX_RESULTS = "maxResults";
500 final static String COUNT_FLAG = "countFlag";
501 final static String ORDER_BY_FIELDS = "orderByFields";
502 final static String ORDER_BY_FIELD = "orderByField";
503 }
504
505 }