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    }