001 package org.kuali.rice.krad.data.provider.query;
002
003 import org.kuali.rice.core.api.criteria.*;
004
005 import java.util.ArrayList;
006 import java.util.List;
007
008 /**
009 * Base class for QueryByCriteria lookups for both Jpa and Ojb PersistenceProvider
010 * implementations. This class implements the core api CriteriaLookupService, as that
011 * is the exact interface required, however this class is not intended to be defined
012 * as a "service", it is merely a helper.
013 */
014 public abstract class DataObjectCriteriaQueryBase<C, Q> implements CriteriaQuery {
015 protected abstract QueryTranslator<C, Q> getQueryTranslator();
016 protected abstract int getRowCount(Q query);
017 protected abstract int getIncludedRowCount(Q query, List rows);
018 protected abstract <T> List<T> getResults(Q query);
019
020 @Override
021 public <T> GenericQueryResults<T> lookup(Class<T> queryClass, QueryByCriteria criteria) {
022 return lookup(queryClass, criteria, LookupCustomizer.Builder.<T>create().build());
023 }
024
025 @Override
026 public <T> GenericQueryResults<T> lookup(final Class<T> queryClass, final QueryByCriteria criteria, LookupCustomizer<T> customizer) {
027 if (queryClass == null) {
028 throw new IllegalArgumentException("queryClass is null");
029 }
030
031 if (criteria == null) {
032 throw new IllegalArgumentException("criteria is null");
033 }
034
035 if (customizer == null) {
036 throw new IllegalArgumentException("customizer is null");
037 }
038
039 final C parent = getQueryTranslator().translateCriteria(queryClass, criteria.getPredicate(), customizer);
040
041 switch (criteria.getCountFlag()) {
042 case ONLY:
043 return forCountOnly(queryClass, criteria, parent);
044 case NONE:
045 return forRowResults(queryClass, criteria, parent, criteria.getCountFlag(), customizer.getResultTransform());
046 case INCLUDE:
047 return forRowResults(queryClass, criteria, parent, criteria.getCountFlag(), customizer.getResultTransform());
048 default: throw new UnsupportedCountFlagException(criteria.getCountFlag());
049 }
050 }
051
052 /** gets results where the actual rows are requested. */
053 protected <T> GenericQueryResults<T> forRowResults(final Class<T> queryClass, final QueryByCriteria criteria, final C ojbCriteria, CountFlag flag, LookupCustomizer.Transform<T, T> transform) {
054 final Q query = getQueryTranslator().createQuery(queryClass, ojbCriteria);
055 final GenericQueryResults.Builder<T> results = GenericQueryResults.Builder.<T>create();
056
057 getQueryTranslator().convertQueryFlags(criteria, query);
058
059 final List<T> rows = getResults(query);
060 if (flag == CountFlag.INCLUDE) {
061 results.setTotalRowCount(getIncludedRowCount(query, rows));
062 }
063
064 if (criteria.getMaxResults() != null && rows.size() > criteria.getMaxResults()) {
065 results.setMoreResultsAvailable(true);
066 //remove the extra row that was returned
067 rows.remove(criteria.getMaxResults().intValue());
068 }
069
070 results.setResults(transformResults(rows, transform));
071 return results.build();
072 }
073
074 /** gets results where only the count is requested. */
075 protected <T> GenericQueryResults<T> forCountOnly(final Class<T> queryClass, final QueryByCriteria criteria, final C platformCriteria) {
076 final Q query = getQueryTranslator().createQuery(queryClass, platformCriteria);
077 final GenericQueryResults.Builder<T> results = GenericQueryResults.Builder.<T>create();
078 results.setTotalRowCount(getRowCount(query));
079 return results.build();
080 }
081
082 protected static <T> List<T> transformResults(List<T> results, LookupCustomizer.Transform<T, T> transform) {
083 final List<T> list = new ArrayList<T>();
084 for (T r : results) {
085 list.add(transform.apply(r));
086 }
087 return list;
088 }
089
090 /** this is a fatal error since this implementation should support all known count flags. */
091 protected static class UnsupportedCountFlagException extends RuntimeException {
092 protected UnsupportedCountFlagException(CountFlag flag) {
093 super("Unsupported predicate [" + String.valueOf(flag) + "]");
094 }
095 }
096 }