View Javadoc

1   package org.kuali.rice.core.impl.criteria;
2   
3   import org.apache.ojb.broker.query.Criteria;
4   import org.apache.ojb.broker.query.Query;
5   import org.apache.ojb.broker.query.QueryFactory;
6   import org.kuali.rice.core.api.criteria.AndPredicate;
7   import org.kuali.rice.core.api.criteria.CompositePredicate;
8   import org.kuali.rice.core.api.criteria.CountFlag;
9   import org.kuali.rice.core.api.criteria.CriteriaLookupService;
10  import org.kuali.rice.core.api.criteria.CriteriaValue;
11  import org.kuali.rice.core.api.criteria.EqualIgnoreCasePredicate;
12  import org.kuali.rice.core.api.criteria.EqualPredicate;
13  import org.kuali.rice.core.api.criteria.GenericQueryResults;
14  import org.kuali.rice.core.api.criteria.GreaterThanOrEqualPredicate;
15  import org.kuali.rice.core.api.criteria.GreaterThanPredicate;
16  import org.kuali.rice.core.api.criteria.InIgnoreCasePredicate;
17  import org.kuali.rice.core.api.criteria.InPredicate;
18  import org.kuali.rice.core.api.criteria.LessThanOrEqualPredicate;
19  import org.kuali.rice.core.api.criteria.LessThanPredicate;
20  import org.kuali.rice.core.api.criteria.LikePredicate;
21  import org.kuali.rice.core.api.criteria.LookupCustomizer;
22  import org.kuali.rice.core.api.criteria.MultiValuedPredicate;
23  import org.kuali.rice.core.api.criteria.NotEqualIgnoreCasePredicate;
24  import org.kuali.rice.core.api.criteria.NotEqualPredicate;
25  import org.kuali.rice.core.api.criteria.NotInIgnoreCasePredicate;
26  import org.kuali.rice.core.api.criteria.NotInPredicate;
27  import org.kuali.rice.core.api.criteria.NotLikePredicate;
28  import org.kuali.rice.core.api.criteria.NotNullPredicate;
29  import org.kuali.rice.core.api.criteria.NullPredicate;
30  import org.kuali.rice.core.api.criteria.OrPredicate;
31  import org.kuali.rice.core.api.criteria.Predicate;
32  import org.kuali.rice.core.api.criteria.PropertyPathPredicate;
33  import org.kuali.rice.core.api.criteria.QueryByCriteria;
34  import org.kuali.rice.core.api.criteria.SingleValuedPredicate;
35  import org.kuali.rice.core.framework.persistence.ojb.dao.PlatformAwareDaoBaseOjb;
36  
37  import java.util.ArrayList;
38  import java.util.HashSet;
39  import java.util.List;
40  import java.util.Set;
41  
42  public class CriteriaLookupServiceOjbImpl extends PlatformAwareDaoBaseOjb implements CriteriaLookupService {
43  
44      @Override
45      public <T> GenericQueryResults<T> lookup(final Class<T> queryClass, final QueryByCriteria criteria) {
46          return lookup(queryClass, criteria, LookupCustomizer.Builder.<T>create().build());
47      }
48  
49      @Override
50      public <T> GenericQueryResults<T> lookup(final Class<T> queryClass, final QueryByCriteria criteria, LookupCustomizer<T> customizer) {
51          if (queryClass == null) {
52              throw new IllegalArgumentException("queryClass is null");
53          }
54  
55          if (criteria == null) {
56              throw new IllegalArgumentException("criteria is null");
57          }
58  
59          if (customizer == null) {
60              throw new IllegalArgumentException("customizer is null");
61          }
62  
63          final Criteria parent = new Criteria();
64  
65          if (criteria.getPredicate() != null) {
66              addPredicate(criteria.getPredicate(), parent, customizer.getPredicateTransform());
67          }
68  
69          switch (criteria.getCountFlag()) {
70              case ONLY:
71                  return forCountOnly(queryClass, criteria, parent);
72              case NONE:
73                  return forRowResults(queryClass, criteria, parent, criteria.getCountFlag(), customizer.getResultTransform());
74              case INCLUDE:
75                  return forRowResults(queryClass, criteria, parent, criteria.getCountFlag(), customizer.getResultTransform());
76              default: throw new UnsupportedCountFlagException(criteria.getCountFlag());
77          }
78      }
79  
80      /** gets results where the actual rows are requested. */
81      private <T> GenericQueryResults<T> forRowResults(final Class<T> queryClass, final QueryByCriteria criteria, final Criteria ojbCriteria, CountFlag flag, LookupCustomizer.Transform<T, T> transform) {
82          final Query ojbQuery = QueryFactory.newQuery(queryClass, ojbCriteria);
83          final GenericQueryResults.Builder<T> results = GenericQueryResults.Builder.<T>create();
84  
85          if (flag == CountFlag.INCLUDE) {
86              results.setTotalRowCount(getPersistenceBrokerTemplate().getCount(ojbQuery));
87          }
88  
89          //ojb's is 1 based, our query api is zero based
90          final int startAtIndex = criteria.getStartAtIndex() != null ? criteria.getStartAtIndex() + 1 : 1;
91          ojbQuery.setStartAtIndex(startAtIndex);
92  
93          if (criteria.getMaxResults() != null) {
94              //not subtracting one from MaxResults in order to retrieve
95              //one extra row so that the MoreResultsAvailable field can be set
96              ojbQuery.setEndAtIndex(criteria.getMaxResults() + startAtIndex);
97          }
98  
99          @SuppressWarnings("unchecked")
100         final List<T> rows = new ArrayList<T>(getPersistenceBrokerTemplate().getCollectionByQuery(ojbQuery));
101 
102         if (criteria.getMaxResults() != null && rows.size() > criteria.getMaxResults()) {
103             results.setMoreResultsAvailable(true);
104             //remove the extra row that was returned
105             rows.remove(criteria.getMaxResults().intValue());
106         }
107 
108         results.setResults(transformResults(rows, transform));
109         return results.build();
110     }
111 
112     private static <T> List<T> transformResults(List<T> results, LookupCustomizer.Transform<T, T> transform) {
113         final List<T> list = new ArrayList<T>();
114         for (T r : results) {
115             list.add(transform.apply(r));
116         }
117         return list;
118     }
119 
120     /** gets results where only the count is requested. */
121     private <T> GenericQueryResults<T> forCountOnly(final Class<T> queryClass, final QueryByCriteria criteria, final Criteria ojbCriteria) {
122         final Query ojbQuery = QueryFactory.newQuery(queryClass, ojbCriteria);
123         final GenericQueryResults.Builder<T> results = GenericQueryResults.Builder.<T>create();
124         results.setTotalRowCount(getPersistenceBrokerTemplate().getCount(ojbQuery));
125 
126         return results.build();
127     }
128 
129     /** adds a predicate to a Criteria.*/
130     private void addPredicate(Predicate p, Criteria parent, LookupCustomizer.Transform<Predicate, Predicate> transform) {
131         p = transform.apply(p);
132 
133         if (p instanceof PropertyPathPredicate) {
134             final String pp = ((PropertyPathPredicate) p).getPropertyPath();
135             if (p instanceof NotNullPredicate) {
136                 parent.addNotNull(pp);
137             } else if (p instanceof NullPredicate) {
138                 parent.addIsNull(pp);
139             } else if (p instanceof SingleValuedPredicate) {
140                 addSingleValuePredicate((SingleValuedPredicate) p, parent);
141             } else if (p instanceof MultiValuedPredicate) {
142                 addMultiValuePredicate((MultiValuedPredicate) p, parent);
143             } else {
144                 throw new UnsupportedPredicateException(p);
145             }
146         } else if (p instanceof CompositePredicate) {
147             addCompositePredicate((CompositePredicate) p, parent, transform);
148         } else {
149             throw new UnsupportedPredicateException(p);
150         }
151     }
152 
153     /** adds a single valued predicate to a Criteria. */
154     private void addSingleValuePredicate(SingleValuedPredicate p, Criteria parent) {
155         final Object value = p.getValue().getValue();
156         final String pp = p.getPropertyPath();
157         if (p instanceof EqualPredicate) {
158             parent.addEqualTo(pp, value);
159         } else if (p instanceof EqualIgnoreCasePredicate) {
160             parent.addEqualTo(genUpperFunc(pp), ((String) value).toUpperCase());
161         } else if (p instanceof GreaterThanOrEqualPredicate) {
162             parent.addGreaterOrEqualThan(pp, value);
163         } else if (p instanceof GreaterThanPredicate) {
164             parent.addGreaterThan(pp, value);
165         } else if (p instanceof LessThanOrEqualPredicate) {
166             parent.addLessOrEqualThan(pp, value);
167         } else if (p instanceof LessThanPredicate) {
168             parent.addLessThan(pp, value);
169         } else if (p instanceof LikePredicate) {
170             //no need to convert * or ? since ojb handles the conversion/escaping
171             parent.addLike(pp, value);
172         } else if (p instanceof NotEqualPredicate) {
173             parent.addNotEqualTo(pp, value);
174         } else if (p instanceof NotEqualIgnoreCasePredicate) {
175             parent.addNotEqualTo(genUpperFunc(pp), ((String) value).toUpperCase());
176         } else if (p instanceof NotLikePredicate) {
177             parent.addNotLike(pp, value);
178         } else {
179             throw new UnsupportedPredicateException(p);
180         }
181     }
182 
183     /** adds a multi valued predicate to a Criteria. */
184     private void addMultiValuePredicate(MultiValuedPredicate p, Criteria parent) {
185         final String pp = p.getPropertyPath();
186         if (p instanceof InPredicate) {
187             final Set<?> values = getValsUnsafe(p.getValues());
188             parent.addIn(pp, values);
189         } else if (p instanceof InIgnoreCasePredicate) {
190             final Set<String> values = toUpper(getVals(((InIgnoreCasePredicate) p).getValues()));
191             parent.addIn(genUpperFunc(pp), values);
192         } else if (p instanceof NotInPredicate) {
193             final Set<?> values = getValsUnsafe(p.getValues());
194             parent.addNotIn(pp, values);
195         } else if (p instanceof NotInIgnoreCasePredicate) {
196             final Set<String> values = toUpper(getVals(((NotInIgnoreCasePredicate) p).getValues()));
197             parent.addNotIn(genUpperFunc(pp), values);
198         } else {
199             throw new UnsupportedPredicateException(p);
200         }
201     }
202 
203     /** adds a composite predicate to a Criteria. */
204     private void addCompositePredicate(final CompositePredicate p, final Criteria parent,  LookupCustomizer.Transform<Predicate, Predicate> transform) {
205         for (Predicate ip : p.getPredicates()) {
206             final Criteria inner = new Criteria();
207             addPredicate(ip, inner, transform);
208             if (p instanceof AndPredicate) {
209                 parent.addAndCriteria(inner);
210             } else if (p instanceof OrPredicate) {
211                 parent.addOrCriteria(inner);
212             } else {
213                 throw new UnsupportedPredicateException(p);
214             }
215         }
216     }
217 
218     @SuppressWarnings("unchecked")
219     private static <T, U extends CriteriaValue<T>> Set<T> getVals(Set<? extends U> toConv) {
220         return (Set<T>) getValsUnsafe(toConv);
221     }
222 
223     //FIXME: this method exists to make javac happy - to bad I didn't know how to fix this....
224     private static Set<?> getValsUnsafe(Set<? extends CriteriaValue<?>> toConv) {
225         final Set<Object> values = new HashSet<Object>();
226         for (CriteriaValue<?> value : toConv) {
227             values.add(value.getValue());
228         }
229         return values;
230     }
231 
232     //eliding performance for function composition....
233     private static Set<String> toUpper(Set<String> strs) {
234         final Set<String> values = new HashSet<String>();
235         for (String value : strs) {
236             values.add(value.toUpperCase());
237         }
238         return values;
239     }
240 
241     private String getUpperFunction() {
242         return getDbPlatform().getUpperCaseFunction();
243     }
244 
245     private String genUpperFunc(String pp) {
246         return new StringBuilder(getUpperFunction()).append("(").append(pp).append(")").toString();
247     }
248 
249     /** this is a fatal error since this implementation should support all known predicates. */
250     private static class UnsupportedPredicateException extends RuntimeException {
251         private UnsupportedPredicateException(Predicate predicate) {
252             super("Unsupported predicate [" + String.valueOf(predicate) + "]");
253         }
254     }
255 
256     /** this is a fatal error since this implementation should support all known count flags. */
257     private static class UnsupportedCountFlagException extends RuntimeException {
258         private UnsupportedCountFlagException(CountFlag flag) {
259             super("Unsupported predicate [" + String.valueOf(flag) + "]");
260         }
261     }
262 }