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