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 = NotLikePredicate.Constants.ROOT_ELEMENT_NAME, type = NotLikePredicate.class, required = false),
97 @XmlElement(name = NotNullPredicate.Constants.ROOT_ELEMENT_NAME, type = NotNullPredicate.class, required = false),
98 @XmlElement(name = NullPredicate.Constants.ROOT_ELEMENT_NAME, type = NullPredicate.class, required = false),
99 @XmlElement(name = OrPredicate.Constants.ROOT_ELEMENT_NAME, type = OrPredicate.class, required = false)
100 })
101 private final Predicate predicate;
102
103 @XmlElement(name = Elements.START_AT_INDEX, required = false)
104 private final Integer startAtIndex;
105
106 @XmlElement(name = Elements.MAX_RESULTS, required = false)
107 private final Integer maxResults;
108
109 @XmlJavaTypeAdapter(CountFlag.Adapter.class)
110 @XmlElement(name = Elements.COUNT_FLAG, required = true)
111 private final String countFlag;
112
113 @XmlElementWrapper(name = Elements.ORDER_BY_FIELDS, required = false)
114 @XmlElement(name = Elements.ORDER_BY_FIELD, required = false)
115 private final List<OrderByField> orderByFields;
116
117
118 @SuppressWarnings("unused")
119 @XmlAnyElement
120 private final Collection<Element> _futureElements = null;
121
122 private QueryByCriteria() {
123 this.predicate = null;
124 this.startAtIndex = null;
125 this.maxResults = null;
126 this.countFlag = null;
127 this.orderByFields = null;
128 }
129
130 private QueryByCriteria(Builder builder) {
131 final Predicate[] preds = builder.predicates;
132 if (preds != null && preds.length > 1) {
133
134 this.predicate = PredicateFactory.and(builder.predicates);
135 } else if (preds != null && preds.length == 1) {
136 this.predicate = builder.predicates[0];
137 } else {
138 this.predicate = null;
139 }
140
141 this.startAtIndex = builder.getStartAtIndex();
142 this.maxResults = builder.getMaxResults();
143 this.countFlag = builder.getCountFlag() == null ? null : builder.getCountFlag().getFlag();
144 this.orderByFields = new ArrayList<OrderByField>(builder.getOrderByFields());
145 }
146
147
148
149
150
151
152 public Predicate getPredicate() {
153 return this.predicate;
154 }
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171 public Integer getStartAtIndex() {
172 return this.startAtIndex;
173 }
174
175
176
177
178
179
180
181
182
183
184
185
186 public Integer getMaxResults() {
187 return this.maxResults;
188 }
189
190
191
192
193
194
195
196
197
198
199 public CountFlag getCountFlag() {
200 return this.countFlag == null ? null : CountFlag.valueOf(this.countFlag);
201 }
202
203
204
205
206
207
208
209 public List<OrderByField> getOrderByFields() {
210 return CollectionUtils.unmodifiableListNullSafe(this.orderByFields);
211 }
212
213 public static final class Builder implements ModelBuilder, Serializable {
214
215 private Predicate[] predicates;
216 private Integer startAtIndex;
217 private Integer maxResults;
218 private CountFlag countFlag;
219 private List<OrderByField> orderByFields;
220
221 private Builder() {
222 setCountFlag(CountFlag.NONE);
223 setOrderByFields(new ArrayList<OrderByField>());
224 }
225
226 public static Builder create() {
227 return new Builder();
228 }
229
230 public static Builder create(QueryByCriteria queryByCriteria) {
231 Builder builder = new Builder();
232 builder.setPredicates(queryByCriteria.getPredicate());
233 builder.setStartAtIndex(queryByCriteria.getStartAtIndex());
234 builder.setMaxResults(queryByCriteria.getMaxResults());
235 builder.setCountFlag(queryByCriteria.getCountFlag());
236 builder.setOrderByFields(queryByCriteria.getOrderByFields());
237 return builder;
238 }
239
240 public Integer getStartAtIndex() {
241 return this.startAtIndex;
242 }
243
244 public void setStartAtIndex(Integer startAtIndex) {
245 if (startAtIndex != null && startAtIndex < 0) {
246 throw new IllegalArgumentException("startAtIndex < 0");
247 }
248
249 this.startAtIndex = startAtIndex;
250 }
251
252 public Integer getMaxResults() {
253 return this.maxResults;
254 }
255
256 public void setMaxResults(Integer maxResults) {
257 if (maxResults != null && maxResults < 0) {
258 throw new IllegalArgumentException("maxResults < 0");
259 }
260
261 this.maxResults = maxResults;
262 }
263
264 public CountFlag getCountFlag() {
265 return this.countFlag;
266 }
267
268 public QueryByCriteria.Builder setCountFlag(CountFlag countFlag) {
269 if (countFlag == null) {
270 throw new IllegalArgumentException("countFlag was null");
271 }
272 this.countFlag = countFlag;
273 return this;
274 }
275
276 public List<OrderByField> getOrderByFields() {
277 return this.orderByFields;
278 }
279
280 public QueryByCriteria.Builder setOrderByFields(List<OrderByField> orderByFields) {
281 if (orderByFields == null) {
282 throw new IllegalArgumentException("orderByFields was null");
283 }
284 this.orderByFields = orderByFields;
285 return this;
286 }
287
288 public QueryByCriteria.Builder setOrderByFields(OrderByField... orderByFields) {
289 if (orderByFields == null) {
290 throw new IllegalArgumentException("orderByFields was null");
291 }
292 setOrderByFields(new ArrayList<OrderByField>(Arrays.asList(orderByFields)));
293 return this;
294 }
295
296 public QueryByCriteria.Builder setOrderByAscending(String... orderByFields) {
297 if (orderByFields == null) {
298 throw new IllegalArgumentException("orderByFields was null");
299 }
300 List<OrderByField> obf = new ArrayList<OrderByField>(orderByFields.length);
301 for ( String fieldName : orderByFields ) {
302 obf.add(OrderByField.Builder.create(fieldName, OrderDirection.ASCENDING).build());
303 }
304 setOrderByFields(obf);
305 return this;
306 }
307
308
309
310
311
312 public Predicate[] getPredicates() {
313 if (this.predicates == null) {
314 return null;
315 }
316
317
318 return Arrays.copyOf(predicates, predicates.length);
319 }
320
321
322
323
324
325
326
327 public QueryByCriteria.Builder setPredicates(Predicate... predicates) {
328
329 this.predicates = predicates != null ? Arrays.copyOf(predicates, predicates.length) : null;
330 return this;
331 }
332
333 @Override
334 public QueryByCriteria build() {
335 return new QueryByCriteria(this);
336 }
337
338
339 public static QueryByCriteria fromPredicates(Predicate... predicates) {
340 final Builder b = Builder.create();
341 b.setPredicates(predicates);
342 return b.build();
343 }
344
345
346 public static QueryByCriteria fromPredicates(Collection<Predicate> predicates) {
347 final Builder b = Builder.create();
348 if ( predicates != null ) {
349 b.setPredicates(predicates.toArray(new Predicate[predicates.size()]));
350 } else {
351 b.setPredicates( (Predicate[])null );
352 }
353 return b.build();
354 }
355
356
357
358
359
360
361
362
363
364 public static QueryByCriteria.Builder orAttributes(Map<String, ?> attributes) {
365 List<Predicate> predicates = new ArrayList<Predicate>();
366 if (attributes != null) {
367 for (Map.Entry<String, ?> entry: attributes.entrySet()) {
368 if(entry.getValue() instanceof Collection<?>){
369 for(Object entryVal : (Collection<?>)entry.getValue()) {
370 predicates.add(buildPredicate(entry.getKey(),entryVal));
371 }
372 } else {
373 predicates.add(buildPredicate(entry.getKey(),entry.getValue()));
374 }
375 }
376 }
377 QueryByCriteria.Builder qbc = QueryByCriteria.Builder.create();
378 qbc.setPredicates(PredicateFactory.or(predicates.toArray(new Predicate[predicates.size()])));
379 return qbc;
380 }
381
382
383
384
385
386
387
388
389
390
391 public static QueryByCriteria.Builder andAttributes(Map<String, ?> attributes) {
392 List<Predicate> predicates = new ArrayList<Predicate>();
393 if (attributes != null) {
394 for (Map.Entry<String, ?> entry: attributes.entrySet()) {
395 if(entry.getValue() instanceof Collection<?>) {
396 Collection<?> values = (Collection<?>)entry.getValue();
397 if (!values.isEmpty()) {
398 List<Predicate> orPredicates = new ArrayList<Predicate>();
399 for(Object entryVal : values) {
400 orPredicates.add(buildPredicate(entry.getKey(),entryVal));
401 }
402 predicates.add(PredicateFactory.or(orPredicates.toArray(new Predicate[orPredicates.size()])));
403 }
404 } else {
405 predicates.add(buildPredicate(entry.getKey(),entry.getValue()));
406 }
407 }
408 }
409 QueryByCriteria.Builder qbc = QueryByCriteria.Builder.create();
410 qbc.setPredicates(PredicateFactory.and(predicates.toArray(new Predicate[predicates.size()])));
411 return qbc;
412 }
413
414 private static Predicate buildPredicate(String attributeKey, Object attributeValue){
415 if(attributeValue == null){
416 return PredicateFactory.isNull(attributeKey);
417 } else {
418 return PredicateFactory.equal(attributeKey,attributeValue);
419 }
420
421 }
422
423
424
425
426
427
428
429 public static QueryByCriteria.Builder forAttribute(String name, Object value) {
430 Map<String, Object> attrib = new HashMap<String, Object>();
431 attrib.put(name, value);
432 return andAttributes(attrib);
433 }
434
435
436
437
438
439
440
441
442 public static QueryByCriteria.Builder orAttributes(Object object, Collection<String> attributes) {
443 return orAttributes(getAttributeValueMap(object, attributes));
444 }
445
446
447
448
449
450
451
452
453 public static QueryByCriteria.Builder andAttributes(Object object, Collection<String> attributes) {
454 return andAttributes(getAttributeValueMap(object, attributes));
455 }
456
457
458
459
460
461
462
463
464 private static Map<String, ?> getAttributeValueMap(Object object, Collection<String> attribNames) {
465 Map<String, Object> attributeMap = new HashMap<String, Object>();
466 for (String attr: attribNames) {
467 Object value;
468 try {
469 value = PropertyUtils.getProperty(object, attr);
470 } catch (IllegalAccessException iae) {
471 throw new RuntimeException(iae);
472 } catch (InvocationTargetException ite) {
473 throw new RuntimeException(ite);
474 } catch (NoSuchMethodException nsme) {
475 throw new RuntimeException(nsme);
476 }
477 attributeMap.put(attr, value);
478 }
479 return attributeMap;
480 }
481 }
482
483
484
485
486 static class Constants {
487 final static String ROOT_ELEMENT_NAME = "queryByCriteria";
488 final static String TYPE_NAME = "QueryByCriteriaType";
489 }
490
491
492
493
494
495 static class Elements {
496 final static String PREDICATE = "predicate";
497 final static String START_AT_INDEX = "startAtIndex";
498 final static String MAX_RESULTS = "maxResults";
499 final static String COUNT_FLAG = "countFlag";
500 final static String ORDER_BY_FIELDS = "orderByFields";
501 final static String ORDER_BY_FIELD = "orderByField";
502 }
503
504 }