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.collections.CollectionUtils;
19 import org.apache.ojb.broker.query.Criteria;
20 import org.apache.ojb.broker.query.Query;
21 import org.apache.ojb.broker.query.QueryFactory;
22 import org.joda.time.DateTime;
23 import org.kuali.rice.core.api.criteria.AndPredicate;
24 import org.kuali.rice.core.api.criteria.CompositePredicate;
25 import org.kuali.rice.core.api.criteria.CountFlag;
26 import org.kuali.rice.core.api.criteria.CriteriaValue;
27 import org.kuali.rice.core.api.criteria.EqualIgnoreCasePredicate;
28 import org.kuali.rice.core.api.criteria.EqualPredicate;
29 import org.kuali.rice.core.api.criteria.GenericQueryResults;
30 import org.kuali.rice.core.api.criteria.GreaterThanOrEqualPredicate;
31 import org.kuali.rice.core.api.criteria.GreaterThanPredicate;
32 import org.kuali.rice.core.api.criteria.InIgnoreCasePredicate;
33 import org.kuali.rice.core.api.criteria.InPredicate;
34 import org.kuali.rice.core.api.criteria.LessThanOrEqualPredicate;
35 import org.kuali.rice.core.api.criteria.LessThanPredicate;
36 import org.kuali.rice.core.api.criteria.LikePredicate;
37 import org.kuali.rice.core.api.criteria.LookupCustomizer;
38 import org.kuali.rice.core.api.criteria.MultiValuedPredicate;
39 import org.kuali.rice.core.api.criteria.NotEqualIgnoreCasePredicate;
40 import org.kuali.rice.core.api.criteria.NotEqualPredicate;
41 import org.kuali.rice.core.api.criteria.NotInIgnoreCasePredicate;
42 import org.kuali.rice.core.api.criteria.NotInPredicate;
43 import org.kuali.rice.core.api.criteria.NotLikePredicate;
44 import org.kuali.rice.core.api.criteria.NotNullPredicate;
45 import org.kuali.rice.core.api.criteria.NullPredicate;
46 import org.kuali.rice.core.api.criteria.OrPredicate;
47 import org.kuali.rice.core.api.criteria.OrderByField;
48 import org.kuali.rice.core.api.criteria.OrderDirection;
49 import org.kuali.rice.core.api.criteria.Predicate;
50 import org.kuali.rice.core.api.criteria.PropertyPathPredicate;
51 import org.kuali.rice.core.api.criteria.QueryByCriteria;
52 import org.kuali.rice.core.api.criteria.SingleValuedPredicate;
53 import org.kuali.rice.core.framework.persistence.ojb.dao.PlatformAwareDaoBaseOjb;
54
55 import java.sql.Timestamp;
56 import java.util.ArrayList;
57 import java.util.HashSet;
58 import java.util.List;
59 import java.util.Set;
60
61 public class CriteriaLookupDaoOjb extends PlatformAwareDaoBaseOjb implements CriteriaLookupDao {
62
63 @Override
64 public <T> GenericQueryResults<T> lookup(final Class<T> queryClass, final QueryByCriteria criteria) {
65 return lookup(queryClass, criteria, LookupCustomizer.Builder.<T>create().build());
66 }
67
68 @Override
69 public <T> GenericQueryResults<T> lookup(final Class<T> queryClass, final QueryByCriteria criteria, LookupCustomizer<T> customizer) {
70 if (queryClass == null) {
71 throw new IllegalArgumentException("queryClass is null");
72 }
73
74 if (criteria == null) {
75 throw new IllegalArgumentException("criteria is null");
76 }
77
78 if (customizer == null) {
79 throw new IllegalArgumentException("customizer is null");
80 }
81
82 final Criteria parent = new Criteria();
83
84 if (criteria.getPredicate() != null) {
85 addPredicate(criteria.getPredicate(), parent, customizer.getPredicateTransform());
86 }
87
88 switch (criteria.getCountFlag()) {
89 case ONLY:
90 return forCountOnly(queryClass, criteria, parent);
91 case NONE:
92 return forRowResults(queryClass, criteria, parent, criteria.getCountFlag(), customizer.getResultTransform());
93 case INCLUDE:
94 return forRowResults(queryClass, criteria, parent, criteria.getCountFlag(), customizer.getResultTransform());
95 default: throw new UnsupportedCountFlagException(criteria.getCountFlag());
96 }
97 }
98
99
100 private <T> GenericQueryResults<T> forRowResults(final Class<T> queryClass, final QueryByCriteria criteria, final Criteria ojbCriteria, CountFlag flag, LookupCustomizer.Transform<T, T> transform) {
101 final org.apache.ojb.broker.query.QueryByCriteria ojbQuery = QueryFactory.newQuery(queryClass, ojbCriteria);
102 final GenericQueryResults.Builder<T> results = GenericQueryResults.Builder.<T>create();
103
104 if (flag == CountFlag.INCLUDE) {
105 results.setTotalRowCount(getPersistenceBrokerTemplate().getCount(ojbQuery));
106 }
107
108
109 final int startAtIndex = criteria.getStartAtIndex() != null ? criteria.getStartAtIndex() + 1 : 1;
110 ojbQuery.setStartAtIndex(startAtIndex);
111
112 if (criteria.getMaxResults() != null) {
113
114
115 ojbQuery.setEndAtIndex(criteria.getMaxResults() + startAtIndex);
116 }
117
118 if (CollectionUtils.isNotEmpty(criteria.getOrderByFields())) {
119 for (OrderByField orderByField : criteria.getOrderByFields()) {
120 if (OrderDirection.ASCENDING.equals(orderByField.getOrderDirection())) {
121 ojbQuery.addOrderBy(orderByField.getFieldName(), true);
122 } else if (OrderDirection.DESCENDING.equals(orderByField.getOrderDirection())) {
123 ojbQuery.addOrderBy(orderByField.getFieldName(), false);
124 }
125 }
126 }
127
128 @SuppressWarnings("unchecked")
129 final List<T> rows = new ArrayList<T>(getPersistenceBrokerTemplate().getCollectionByQuery(ojbQuery));
130
131 if (criteria.getMaxResults() != null && rows.size() > criteria.getMaxResults()) {
132 results.setMoreResultsAvailable(true);
133
134 rows.remove(criteria.getMaxResults().intValue());
135 }
136
137 results.setResults(transformResults(rows, transform));
138 return results.build();
139 }
140
141 private static <T> List<T> transformResults(List<T> results, LookupCustomizer.Transform<T, T> transform) {
142 final List<T> list = new ArrayList<T>();
143 for (T r : results) {
144 list.add(transform.apply(r));
145 }
146 return list;
147 }
148
149
150 private <T> GenericQueryResults<T> forCountOnly(final Class<T> queryClass, final QueryByCriteria criteria, final Criteria ojbCriteria) {
151 final Query ojbQuery = QueryFactory.newQuery(queryClass, ojbCriteria);
152 final GenericQueryResults.Builder<T> results = GenericQueryResults.Builder.<T>create();
153 results.setTotalRowCount(getPersistenceBrokerTemplate().getCount(ojbQuery));
154
155 return results.build();
156 }
157
158
159 private void addPredicate(Predicate p, Criteria parent, LookupCustomizer.Transform<Predicate, Predicate> transform) {
160 p = transform.apply(p);
161
162 if (p instanceof PropertyPathPredicate) {
163 final String pp = ((PropertyPathPredicate) p).getPropertyPath();
164 if (p instanceof NotNullPredicate) {
165 parent.addNotNull(pp);
166 } else if (p instanceof NullPredicate) {
167 parent.addIsNull(pp);
168 } else if (p instanceof SingleValuedPredicate) {
169 addSingleValuePredicate((SingleValuedPredicate) p, parent);
170 } else if (p instanceof MultiValuedPredicate) {
171 addMultiValuePredicate((MultiValuedPredicate) p, parent);
172 } else {
173 throw new UnsupportedPredicateException(p);
174 }
175 } else if (p instanceof CompositePredicate) {
176 addCompositePredicate((CompositePredicate) p, parent, transform);
177 } else {
178 throw new UnsupportedPredicateException(p);
179 }
180 }
181
182
183 private void addSingleValuePredicate(SingleValuedPredicate p, Criteria parent) {
184 final Object value = getVal(p.getValue());
185 final String pp = p.getPropertyPath();
186 if (p instanceof EqualPredicate) {
187 parent.addEqualTo(pp, value);
188 } else if (p instanceof EqualIgnoreCasePredicate) {
189 parent.addEqualTo(genUpperFunc(pp), ((String) value).toUpperCase());
190 } else if (p instanceof GreaterThanOrEqualPredicate) {
191 parent.addGreaterOrEqualThan(pp, value);
192 } else if (p instanceof GreaterThanPredicate) {
193 parent.addGreaterThan(pp, value);
194 } else if (p instanceof LessThanOrEqualPredicate) {
195 parent.addLessOrEqualThan(pp, value);
196 } else if (p instanceof LessThanPredicate) {
197 parent.addLessThan(pp, value);
198 } else if (p instanceof LikePredicate) {
199
200 parent.addLike(genUpperFunc(pp), ((String) value).toUpperCase());
201 } else if (p instanceof NotEqualPredicate) {
202 parent.addNotEqualTo(pp, value);
203 } else if (p instanceof NotEqualIgnoreCasePredicate) {
204 parent.addNotEqualTo(genUpperFunc(pp), ((String) value).toUpperCase());
205 } else if (p instanceof NotLikePredicate) {
206 parent.addNotLike(pp, value);
207 } else {
208 throw new UnsupportedPredicateException(p);
209 }
210 }
211
212
213 private void addMultiValuePredicate(MultiValuedPredicate p, Criteria parent) {
214 final String pp = p.getPropertyPath();
215 if (p instanceof InPredicate) {
216 final Set<?> values = getVals(p.getValues());
217 parent.addIn(pp, values);
218 } else if (p instanceof InIgnoreCasePredicate) {
219 final Set<String> values = toUpper(getValsUnsafe(((InIgnoreCasePredicate) p).getValues()));
220 parent.addIn(genUpperFunc(pp), values);
221 } else if (p instanceof NotInPredicate) {
222 final Set<?> values = getVals(p.getValues());
223 parent.addNotIn(pp, values);
224 } else if (p instanceof NotInIgnoreCasePredicate) {
225 final Set<String> values = toUpper(getValsUnsafe(((NotInIgnoreCasePredicate) p).getValues()));
226 parent.addNotIn(genUpperFunc(pp), values);
227 } else {
228 throw new UnsupportedPredicateException(p);
229 }
230 }
231
232
233 private void addCompositePredicate(final CompositePredicate p, final Criteria parent, LookupCustomizer.Transform<Predicate, Predicate> transform) {
234 for (Predicate ip : p.getPredicates()) {
235 final Criteria inner = new Criteria();
236 addPredicate(ip, inner, transform);
237 if (p instanceof AndPredicate) {
238 parent.addAndCriteria(inner);
239 } else if (p instanceof OrPredicate) {
240 parent.addOrCriteria(inner);
241 } else {
242 throw new UnsupportedPredicateException(p);
243 }
244 }
245 }
246
247 private static <U extends CriteriaValue<?>> Object getVal(U toConv) {
248 Object o = toConv.getValue();
249 if (o instanceof DateTime) {
250 return new Timestamp(((DateTime) o).getMillis());
251 }
252 return o;
253 }
254
255
256 @SuppressWarnings("unchecked")
257 private static <T, U extends CriteriaValue<T>> Set<T> getValsUnsafe(Set<? extends U> toConv) {
258 return (Set<T>) getVals(toConv);
259 }
260
261 private static Set<?> getVals(Set<? extends CriteriaValue<?>> toConv) {
262 final Set<Object> values = new HashSet<Object>();
263 for (CriteriaValue<?> value : toConv) {
264 values.add(getVal(value));
265 }
266 return values;
267 }
268
269
270 private static Set<String> toUpper(Set<String> strs) {
271 final Set<String> values = new HashSet<String>();
272 for (String value : strs) {
273 values.add(value.toUpperCase());
274 }
275 return values;
276 }
277
278 private String getUpperFunction() {
279 return getDbPlatform().getUpperCaseFunction();
280 }
281
282 private String genUpperFunc(String pp) {
283 return new StringBuilder(getUpperFunction()).append("(").append(pp).append(")").toString();
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 }