1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.student.r2.common.criteria.impl;
17
18 import org.apache.commons.lang.StringUtils;
19 import org.joda.time.DateTime;
20 import org.kuali.rice.core.api.criteria.AndPredicate;
21 import org.kuali.rice.core.api.criteria.CompositePredicate;
22 import org.kuali.rice.core.api.criteria.CountFlag;
23 import org.kuali.rice.core.api.criteria.CriteriaValue;
24 import org.kuali.rice.core.api.criteria.EqualIgnoreCasePredicate;
25 import org.kuali.rice.core.api.criteria.EqualPredicate;
26 import org.kuali.rice.core.api.criteria.GenericQueryResults;
27 import org.kuali.rice.core.api.criteria.GreaterThanOrEqualPredicate;
28 import org.kuali.rice.core.api.criteria.GreaterThanPredicate;
29 import org.kuali.rice.core.api.criteria.InIgnoreCasePredicate;
30 import org.kuali.rice.core.api.criteria.InPredicate;
31 import org.kuali.rice.core.api.criteria.LessThanOrEqualPredicate;
32 import org.kuali.rice.core.api.criteria.LessThanPredicate;
33 import org.kuali.rice.core.api.criteria.LikePredicate;
34 import org.kuali.rice.core.api.criteria.MultiValuedPredicate;
35 import org.kuali.rice.core.api.criteria.NotEqualIgnoreCasePredicate;
36 import org.kuali.rice.core.api.criteria.NotEqualPredicate;
37 import org.kuali.rice.core.api.criteria.NotInIgnoreCasePredicate;
38 import org.kuali.rice.core.api.criteria.NotInPredicate;
39 import org.kuali.rice.core.api.criteria.NotLikePredicate;
40 import org.kuali.rice.core.api.criteria.NotNullPredicate;
41 import org.kuali.rice.core.api.criteria.NullPredicate;
42 import org.kuali.rice.core.api.criteria.OrPredicate;
43 import org.kuali.rice.core.api.criteria.Predicate;
44 import org.kuali.rice.core.api.criteria.PropertyPathPredicate;
45 import org.kuali.rice.core.api.criteria.QueryByCriteria;
46 import org.kuali.rice.core.api.criteria.SingleValuedPredicate;
47 import org.kuali.student.r2.common.criteria.Criteria;
48 import org.kuali.student.r2.common.criteria.LookupCustomizer;
49
50 import javax.persistence.EntityManager;
51 import javax.persistence.PersistenceContext;
52 import javax.persistence.Query;
53 import java.sql.Timestamp;
54 import java.util.ArrayList;
55 import java.util.HashSet;
56 import java.util.List;
57 import java.util.Set;
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80 public class CriteriaLookupDaoJpaImpl {
81 @PersistenceContext
82 private EntityManager entityManager;
83
84 public <T> GenericQueryResults<T> lookup(final Class<T> queryClass, final QueryByCriteria criteria, LookupCustomizer<T> customizer) {
85 if (queryClass == null) {
86 throw new IllegalArgumentException("queryClass is null");
87 }
88
89 if (criteria == null) {
90 throw new IllegalArgumentException("criteria is null");
91 }
92
93 if (customizer == null) {
94 throw new IllegalArgumentException("customizer is null");
95 }
96
97 final Criteria parent = new Criteria(queryClass.getName());
98
99 if (criteria.getPredicate() != null) {
100 Predicate predicate = customizer.applyAdditionalTransforms(criteria.getPredicate(), parent);
101 addPredicate(predicate, parent, parent, customizer);
102 }
103
104 switch (criteria.getCountFlag()) {
105 case ONLY:
106 return forCountOnly(queryClass, criteria, parent);
107 case NONE:
108 return forRowResults(queryClass, criteria, parent, criteria.getCountFlag(), customizer.getResultTransform());
109 case INCLUDE:
110 return forRowResults(queryClass, criteria, parent, criteria.getCountFlag(), customizer.getResultTransform());
111 default: throw new UnsupportedCountFlagException(criteria.getCountFlag());
112 }
113 }
114
115
116 public <T> GenericQueryResults<String> lookupIds(final Class<T> queryClass, final QueryByCriteria criteria, LookupCustomizer<T> customizer) {
117 if (queryClass == null) {
118 throw new IllegalArgumentException("queryClass is null");
119 }
120
121 if (criteria == null) {
122 throw new IllegalArgumentException("criteria is null");
123 }
124
125 if (customizer == null) {
126 throw new IllegalArgumentException("customizer is null");
127 }
128
129
130 Criteria parent = new Criteria(queryClass.getName(), false);
131 parent.select("id");
132
133
134 if (criteria.getPredicate() != null) {
135 Predicate predicate = customizer.applyAdditionalTransforms(criteria.getPredicate(), parent);
136 addPredicate(predicate, parent, parent, customizer);
137 }
138
139 switch (criteria.getCountFlag()) {
140 case ONLY:
141 return (GenericQueryResults<String>) forCountOnly(queryClass, criteria, parent);
142 case NONE:
143 return (GenericQueryResults<String>) forRowResults(queryClass, criteria, parent, criteria.getCountFlag(), customizer.getResultTransform());
144 case INCLUDE:
145 return (GenericQueryResults<String>) forRowResults(queryClass, criteria, parent, criteria.getCountFlag(), customizer.getResultTransform());
146 default: throw new UnsupportedCountFlagException(criteria.getCountFlag());
147 }
148 }
149
150
151 public <T> GenericQueryResults<List<String>> genericLookup(final Class<T> queryClass, final QueryByCriteria criteria, LookupCustomizer<T> customizer, List<String> fields) {
152 if (queryClass == null) {
153 throw new IllegalArgumentException("queryClass is null");
154 }
155
156 if (criteria == null) {
157 throw new IllegalArgumentException("criteria is null");
158 }
159
160 if (customizer == null) {
161 throw new IllegalArgumentException("customizer is null");
162 }
163
164
165 Criteria parent = new Criteria(queryClass.getName(), false);
166 for(String field: fields){
167 parent.select(field);
168 }
169
170
171 if (criteria.getPredicate() != null) {
172 Predicate predicate = customizer.applyAdditionalTransforms(criteria.getPredicate(), parent);
173 addPredicate(predicate, parent, parent, customizer);
174 }
175
176 switch (criteria.getCountFlag()) {
177 case ONLY:
178 return (GenericQueryResults<List<String>>) forCountOnly(queryClass, criteria, parent);
179 case NONE:
180 return (GenericQueryResults<List<String>>) forRowResults(queryClass, criteria, parent, criteria.getCountFlag(), customizer.getResultTransform());
181 case INCLUDE:
182 return (GenericQueryResults<List<String>>) forRowResults(queryClass, criteria, parent, criteria.getCountFlag(), customizer.getResultTransform());
183 default: throw new UnsupportedCountFlagException(criteria.getCountFlag());
184 }
185 }
186
187
188 private <T> GenericQueryResults<T> forRowResults(final Class<T> queryClass, final QueryByCriteria criteria, final Criteria jpaCriteria, CountFlag flag, LookupCustomizer.Transform<T, T> transform) {
189 final Query jpaQuery = new org.kuali.student.r2.common.criteria.QueryByCriteria(entityManager, jpaCriteria).toQuery();
190 final GenericQueryResults.Builder<T> results = GenericQueryResults.Builder.<T>create();
191
192
193 jpaQuery.setFirstResult(criteria.getStartAtIndex() != null ? criteria.getStartAtIndex() : 0);
194
195 if (criteria.getMaxResults() != null) {
196
197
198 jpaQuery.setMaxResults(criteria.getMaxResults() + 1);
199 }
200
201 @SuppressWarnings("unchecked")
202 final List<T> rows = new ArrayList<T>(jpaQuery.getResultList());
203 if (flag == CountFlag.INCLUDE) {
204 results.setTotalRowCount(rows.size());
205 }
206
207 if (criteria.getMaxResults() != null && rows.size() > criteria.getMaxResults()) {
208 results.setMoreResultsAvailable(true);
209
210 rows.remove(criteria.getMaxResults().intValue());
211 }
212
213 results.setResults(transformResults(rows, transform));
214 return results.build();
215 }
216
217 private static <T> List<T> transformResults(List<T> results, LookupCustomizer.Transform<T, T> transform) {
218 final List<T> list = new ArrayList<T>();
219 for (T r : results) {
220 list.add(transform.apply(r));
221 }
222 return list;
223 }
224
225
226 private <T> GenericQueryResults<T> forCountOnly(final Class<T> queryClass, final QueryByCriteria criteria, final Criteria jpaCriteria) {
227 final Query jpaQuery = new org.kuali.student.r2.common.criteria.QueryByCriteria(entityManager, jpaCriteria).toQuery();
228 final GenericQueryResults.Builder<T> results = GenericQueryResults.Builder.<T>create();
229
230 results.setTotalRowCount(jpaQuery.getResultList().size());
231
232 return results.build();
233 }
234
235
236 private void addPredicate(Predicate p, Criteria parent, Criteria root, LookupCustomizer<?> customizer) {
237 p = customizer.applyPredicateTransforms(p, root);
238
239 if (p instanceof PropertyPathPredicate) {
240 final String pp = ((PropertyPathPredicate) p).getPropertyPath();
241 if (p instanceof NotNullPredicate) {
242 parent.notNull(pp);
243 } else if (p instanceof NullPredicate) {
244 parent.isNull(pp);
245 } else if (p instanceof SingleValuedPredicate) {
246 addSingleValuePredicate((SingleValuedPredicate) p, parent);
247 } else if (p instanceof MultiValuedPredicate) {
248 addMultiValuePredicate((MultiValuedPredicate) p, parent);
249 } else {
250 throw new UnsupportedPredicateException(p);
251 }
252 } else if (p instanceof CompositePredicate) {
253 addCompositePredicate((CompositePredicate) p, parent, root, customizer);
254 } else {
255 throw new UnsupportedPredicateException(p);
256 }
257 }
258
259
260 private void addSingleValuePredicate(SingleValuedPredicate p, Criteria parent) {
261 final Object value = getVal(p.getValue());
262 final String pp = p.getPropertyPath();
263 if (p instanceof EqualPredicate) {
264 parent.eq(pp, value);
265 } else if (p instanceof EqualIgnoreCasePredicate) {
266 parent.eq(genUpperFunc(pp), ((String) value).toUpperCase());
267 } else if (p instanceof GreaterThanOrEqualPredicate) {
268 parent.gte(pp, value);
269 } else if (p instanceof GreaterThanPredicate) {
270 parent.gt(pp, value);
271 } else if (p instanceof LessThanOrEqualPredicate) {
272 parent.lte(pp, value);
273 } else if (p instanceof LessThanPredicate) {
274 parent.lt(pp, value);
275 } else if (p instanceof LikePredicate) {
276
277 parent.like(genUpperFunc(pp), ((String) value).toUpperCase());
278 } else if (p instanceof NotEqualPredicate) {
279 parent.ne(pp, value);
280 } else if (p instanceof NotEqualIgnoreCasePredicate) {
281 parent.ne(genUpperFunc(pp), ((String) value).toUpperCase());
282 } else if (p instanceof NotLikePredicate) {
283 parent.notLike(pp, value);
284 } else {
285 throw new UnsupportedPredicateException(p);
286 }
287 }
288
289
290 private void addMultiValuePredicate(MultiValuedPredicate p, Criteria parent) {
291 final String pp = p.getPropertyPath();
292 if (p instanceof InPredicate) {
293 final Set<?> values = getVals(p.getValues());
294 parent.in(pp, values);
295 } else if (p instanceof InIgnoreCasePredicate) {
296 final Set<String> values = toUpper(getValsUnsafe(((InIgnoreCasePredicate) p).getValues()));
297 parent.in(genUpperFunc(pp), values);
298 } else if (p instanceof NotInPredicate) {
299 final Set<?> values = getVals(p.getValues());
300 parent.notIn(pp, values);
301 } else if (p instanceof NotInIgnoreCasePredicate) {
302 final Set<String> values = toUpper(getValsUnsafe(((NotInIgnoreCasePredicate) p).getValues()));
303 parent.notIn(genUpperFunc(pp), values);
304 } else {
305 throw new UnsupportedPredicateException(p);
306 }
307 }
308
309
310 private void addCompositePredicate(final CompositePredicate p, final Criteria parent, Criteria root, LookupCustomizer<?> customizer) {
311 for (Predicate ip : p.getPredicates()) {
312 final Criteria inner = new Criteria(parent.getEntityName());
313 addPredicate(ip, inner, root, customizer);
314 if (p instanceof AndPredicate) {
315 parent.and(inner);
316 } else if (p instanceof OrPredicate) {
317 parent.or(inner);
318 } else {
319 throw new UnsupportedPredicateException(p);
320 }
321 }
322 }
323
324 private static <U extends CriteriaValue<?>> Object getVal(U toConv) {
325 Object o = toConv.getValue();
326 if (o instanceof DateTime) {
327 return new Timestamp(((DateTime) o).getMillis());
328 }
329 return o;
330 }
331
332
333 @SuppressWarnings("unchecked")
334 private static <T, U extends CriteriaValue<T>> Set<T> getValsUnsafe(Set<? extends U> toConv) {
335 return (Set<T>) getVals(toConv);
336 }
337
338 private static Set<?> getVals(Set<? extends CriteriaValue<?>> toConv) {
339 final Set<Object> values = new HashSet<Object>();
340 for (CriteriaValue<?> value : toConv) {
341 values.add(getVal(value));
342 }
343 return values;
344 }
345
346
347 private static Set<String> toUpper(Set<String> strs) {
348 final Set<String> values = new HashSet<String>();
349 for (String value : strs) {
350 values.add(value.toUpperCase());
351 }
352 return values;
353 }
354
355
356 private String genUpperFunc(String pp) {
357 if (StringUtils.contains(pp, "__JPA_ALIAS[[")) {
358 pp = "UPPER(" + pp + ")";
359 } else {
360 pp = "UPPER(__JPA_ALIAS[[0]]__." + pp + ")";
361 }
362 return pp;
363 }
364
365
366
367
368 public void setEntityManager(EntityManager entityManager) {
369 this.entityManager = entityManager;
370 }
371
372
373 private static class UnsupportedPredicateException extends RuntimeException {
374 private UnsupportedPredicateException(Predicate predicate) {
375 super("Unsupported predicate [" + String.valueOf(predicate) + "]");
376 }
377 }
378
379
380 private static class UnsupportedCountFlagException extends RuntimeException {
381 private UnsupportedCountFlagException(CountFlag flag) {
382 super("Unsupported predicate [" + String.valueOf(flag) + "]");
383 }
384 }
385 }