1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.krad.criteria;
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.CriteriaLookupService;
24 import org.kuali.rice.core.api.criteria.CriteriaValue;
25 import org.kuali.rice.core.api.criteria.EqualIgnoreCasePredicate;
26 import org.kuali.rice.core.api.criteria.EqualPredicate;
27 import org.kuali.rice.core.api.criteria.GenericQueryResults;
28 import org.kuali.rice.core.api.criteria.GreaterThanOrEqualPredicate;
29 import org.kuali.rice.core.api.criteria.GreaterThanPredicate;
30 import org.kuali.rice.core.api.criteria.InIgnoreCasePredicate;
31 import org.kuali.rice.core.api.criteria.InPredicate;
32 import org.kuali.rice.core.api.criteria.LessThanOrEqualPredicate;
33 import org.kuali.rice.core.api.criteria.LessThanPredicate;
34 import org.kuali.rice.core.api.criteria.LikePredicate;
35 import org.kuali.rice.core.api.criteria.LookupCustomizer;
36 import org.kuali.rice.core.api.criteria.MultiValuedPredicate;
37 import org.kuali.rice.core.api.criteria.NotEqualIgnoreCasePredicate;
38 import org.kuali.rice.core.api.criteria.NotEqualPredicate;
39 import org.kuali.rice.core.api.criteria.NotInIgnoreCasePredicate;
40 import org.kuali.rice.core.api.criteria.NotInPredicate;
41 import org.kuali.rice.core.api.criteria.NotLikePredicate;
42 import org.kuali.rice.core.api.criteria.NotNullPredicate;
43 import org.kuali.rice.core.api.criteria.NullPredicate;
44 import org.kuali.rice.core.api.criteria.OrPredicate;
45 import org.kuali.rice.core.api.criteria.Predicate;
46 import org.kuali.rice.core.api.criteria.PropertyPathPredicate;
47 import org.kuali.rice.core.api.criteria.QueryByCriteria;
48 import org.kuali.rice.core.api.criteria.SingleValuedPredicate;
49 import org.kuali.rice.core.framework.persistence.jpa.criteria.Criteria;
50
51 import javax.persistence.EntityManager;
52 import javax.persistence.PersistenceContext;
53 import javax.persistence.Query;
54 import java.sql.Timestamp;
55 import java.util.ArrayList;
56 import java.util.HashSet;
57 import java.util.List;
58 import java.util.Set;
59
60 public class CriteriaLookupDaoJpa implements CriteriaLookupDao {
61 @PersistenceContext
62 private EntityManager entityManager;
63
64 @Override
65 public <T> GenericQueryResults<T> lookup(final Class<T> queryClass, final QueryByCriteria criteria) {
66 return lookup(queryClass, criteria, LookupCustomizer.Builder.<T>create().build());
67 }
68
69 @Override
70 public <T> GenericQueryResults<T> lookup(final Class<T> queryClass, final QueryByCriteria criteria, LookupCustomizer<T> customizer) {
71 if (queryClass == null) {
72 throw new IllegalArgumentException("queryClass is null");
73 }
74
75 if (criteria == null) {
76 throw new IllegalArgumentException("criteria is null");
77 }
78
79 if (customizer == null) {
80 throw new IllegalArgumentException("customizer is null");
81 }
82
83 final Criteria parent = new Criteria(queryClass.getClass().getName());
84
85 if (criteria.getPredicate() != null) {
86 addPredicate(criteria.getPredicate(), parent, customizer.getPredicateTransform());
87 }
88
89 switch (criteria.getCountFlag()) {
90 case ONLY:
91 return forCountOnly(queryClass, criteria, parent);
92 case NONE:
93 return forRowResults(queryClass, criteria, parent, criteria.getCountFlag(), customizer.getResultTransform());
94 case INCLUDE:
95 return forRowResults(queryClass, criteria, parent, criteria.getCountFlag(), customizer.getResultTransform());
96 default: throw new UnsupportedCountFlagException(criteria.getCountFlag());
97 }
98 }
99
100
101 private <T> GenericQueryResults<T> forRowResults(final Class<T> queryClass, final QueryByCriteria criteria, final Criteria jpaCriteria, CountFlag flag, LookupCustomizer.Transform<T, T> transform) {
102 final Query jpaQuery = new org.kuali.rice.core.framework.persistence.jpa.criteria.QueryByCriteria(entityManager, jpaCriteria).toQuery();
103 final GenericQueryResults.Builder<T> results = GenericQueryResults.Builder.<T>create();
104
105
106 final int startAtIndex = criteria.getStartAtIndex() != null ? criteria.getStartAtIndex() + 1 : 1;
107 jpaQuery.setFirstResult(startAtIndex);
108
109 if (criteria.getMaxResults() != null) {
110
111
112 jpaQuery.setMaxResults(criteria.getMaxResults());
113 }
114
115 @SuppressWarnings("unchecked")
116 final List<T> rows = new ArrayList<T>(jpaQuery.getResultList());
117 if (flag == CountFlag.INCLUDE) {
118 results.setTotalRowCount(rows.size());
119 }
120
121 if (criteria.getMaxResults() != null && rows.size() > criteria.getMaxResults()) {
122 results.setMoreResultsAvailable(true);
123
124 rows.remove(criteria.getMaxResults().intValue());
125 }
126
127 results.setResults(transformResults(rows, transform));
128 return results.build();
129 }
130
131 private static <T> List<T> transformResults(List<T> results, LookupCustomizer.Transform<T, T> transform) {
132 final List<T> list = new ArrayList<T>();
133 for (T r : results) {
134 list.add(transform.apply(r));
135 }
136 return list;
137 }
138
139
140 private <T> GenericQueryResults<T> forCountOnly(final Class<T> queryClass, final QueryByCriteria criteria, final Criteria jpaCriteria) {
141 final Query jpaQuery = new org.kuali.rice.core.framework.persistence.jpa.criteria.QueryByCriteria(entityManager, jpaCriteria).toQuery();
142 final GenericQueryResults.Builder<T> results = GenericQueryResults.Builder.<T>create();
143
144 results.setTotalRowCount(jpaQuery.getResultList().size());
145
146 return results.build();
147 }
148
149
150 private void addPredicate(Predicate p, Criteria parent, LookupCustomizer.Transform<Predicate, Predicate> transform) {
151 p = transform.apply(p);
152
153 if (p instanceof PropertyPathPredicate) {
154 final String pp = ((PropertyPathPredicate) p).getPropertyPath();
155 if (p instanceof NotNullPredicate) {
156 parent.notNull(pp);
157 } else if (p instanceof NullPredicate) {
158 parent.isNull(pp);
159 } else if (p instanceof SingleValuedPredicate) {
160 addSingleValuePredicate((SingleValuedPredicate) p, parent);
161 } else if (p instanceof MultiValuedPredicate) {
162 addMultiValuePredicate((MultiValuedPredicate) p, parent);
163 } else {
164 throw new UnsupportedPredicateException(p);
165 }
166 } else if (p instanceof CompositePredicate) {
167 addCompositePredicate((CompositePredicate) p, parent, transform);
168 } else {
169 throw new UnsupportedPredicateException(p);
170 }
171 }
172
173
174 private void addSingleValuePredicate(SingleValuedPredicate p, Criteria parent) {
175 final Object value = getVal(p.getValue());
176 final String pp = p.getPropertyPath();
177 if (p instanceof EqualPredicate) {
178 parent.eq(pp, value);
179 } else if (p instanceof EqualIgnoreCasePredicate) {
180 parent.eq(genUpperFunc(pp), ((String) value).toUpperCase());
181 } else if (p instanceof GreaterThanOrEqualPredicate) {
182 parent.gte(pp, value);
183 } else if (p instanceof GreaterThanPredicate) {
184 parent.gt(pp, value);
185 } else if (p instanceof LessThanOrEqualPredicate) {
186 parent.lte(pp, value);
187 } else if (p instanceof LessThanPredicate) {
188 parent.lt(pp, value);
189 } else if (p instanceof LikePredicate) {
190
191 parent.like(pp, value);
192 } else if (p instanceof NotEqualPredicate) {
193 parent.ne(pp, value);
194 } else if (p instanceof NotEqualIgnoreCasePredicate) {
195 parent.ne(genUpperFunc(pp), ((String) value).toUpperCase());
196 } else if (p instanceof NotLikePredicate) {
197 parent.notLike(pp, value);
198 } else {
199 throw new UnsupportedPredicateException(p);
200 }
201 }
202
203
204 private void addMultiValuePredicate(MultiValuedPredicate p, Criteria parent) {
205 final String pp = p.getPropertyPath();
206 if (p instanceof InPredicate) {
207 final Set<?> values = getVals(p.getValues());
208 parent.in(pp, values);
209 } else if (p instanceof InIgnoreCasePredicate) {
210 final Set<String> values = toUpper(getValsUnsafe(((InIgnoreCasePredicate) p).getValues()));
211 parent.in(genUpperFunc(pp), values);
212 } else if (p instanceof NotInPredicate) {
213 final Set<?> values = getVals(p.getValues());
214 parent.notIn(pp, values);
215 } else if (p instanceof NotInIgnoreCasePredicate) {
216 final Set<String> values = toUpper(getValsUnsafe(((NotInIgnoreCasePredicate) p).getValues()));
217 parent.notIn(genUpperFunc(pp), values);
218 } else {
219 throw new UnsupportedPredicateException(p);
220 }
221 }
222
223
224 private void addCompositePredicate(final CompositePredicate p, final Criteria parent, LookupCustomizer.Transform<Predicate, Predicate> transform) {
225 for (Predicate ip : p.getPredicates()) {
226 final Criteria inner = new Criteria(parent.getEntityName());
227 addPredicate(ip, inner, transform);
228 if (p instanceof AndPredicate) {
229 parent.and(inner);
230 } else if (p instanceof OrPredicate) {
231 parent.or(inner);
232 } else {
233 throw new UnsupportedPredicateException(p);
234 }
235 }
236 }
237
238 private static <U extends CriteriaValue<?>> Object getVal(U toConv) {
239 Object o = toConv.getValue();
240 if (o instanceof DateTime) {
241 return new Timestamp(((DateTime) o).getMillis());
242 }
243 return o;
244 }
245
246
247 @SuppressWarnings("unchecked")
248 private static <T, U extends CriteriaValue<T>> Set<T> getValsUnsafe(Set<? extends U> toConv) {
249 return (Set<T>) getVals(toConv);
250 }
251
252 private static Set<?> getVals(Set<? extends CriteriaValue<?>> toConv) {
253 final Set<Object> values = new HashSet<Object>();
254 for (CriteriaValue<?> value : toConv) {
255 values.add(getVal(value));
256 }
257 return values;
258 }
259
260
261 private static Set<String> toUpper(Set<String> strs) {
262 final Set<String> values = new HashSet<String>();
263 for (String value : strs) {
264 values.add(value.toUpperCase());
265 }
266 return values;
267 }
268
269
270 private String genUpperFunc(String pp) {
271 if (StringUtils.contains(pp, "__JPA_ALIAS[[")) {
272 pp = "UPPER(" + pp + ")";
273 } else {
274 pp = "UPPER(__JPA_ALIAS[[0]]__." + pp + ")";
275 }
276 return pp;
277 }
278
279
280
281
282 public void setEntityManager(EntityManager entityManager) {
283 this.entityManager = entityManager;
284 }
285
286
287 private static class UnsupportedPredicateException extends RuntimeException {
288 private UnsupportedPredicateException(Predicate predicate) {
289 super("Unsupported predicate [" + String.valueOf(predicate) + "]");
290 }
291 }
292
293
294 private static class UnsupportedCountFlagException extends RuntimeException {
295 private UnsupportedCountFlagException(CountFlag flag) {
296 super("Unsupported predicate [" + String.valueOf(flag) + "]");
297 }
298 }
299 }