Coverage Report - org.kuali.student.r2.common.dao.CriteriaLookupDaoJpaImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
CriteriaLookupDaoJpaImpl
0%
0/123
0%
0/78
4.118
CriteriaLookupDaoJpaImpl$1
0%
0/1
N/A
4.118
CriteriaLookupDaoJpaImpl$UnsupportedCountFlagException
0%
0/3
N/A
4.118
CriteriaLookupDaoJpaImpl$UnsupportedPredicateException
0%
0/3
N/A
4.118
 
 1  
 /**
 2  
  * Copyright 2005-2012 The Kuali Foundation
 3  
  *
 4  
  * Licensed under the Educational Community License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  * http://www.opensource.org/licenses/ecl2.php
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package org.kuali.student.r2.common.dao;
 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.CriteriaValue;
 24  
 import org.kuali.rice.core.api.criteria.EqualIgnoreCasePredicate;
 25  
 import org.kuali.rice.core.api.criteria.EqualPredicate;
 26  
 import org.kuali.rice.core.api.criteria.GenericQueryResults;
 27  
 import org.kuali.rice.core.api.criteria.GreaterThanOrEqualPredicate;
 28  
 import org.kuali.rice.core.api.criteria.GreaterThanPredicate;
 29  
 import org.kuali.rice.core.api.criteria.InIgnoreCasePredicate;
 30  
 import org.kuali.rice.core.api.criteria.InPredicate;
 31  
 import org.kuali.rice.core.api.criteria.LessThanOrEqualPredicate;
 32  
 import org.kuali.rice.core.api.criteria.LessThanPredicate;
 33  
 import org.kuali.rice.core.api.criteria.LikePredicate;
 34  
 import org.kuali.rice.core.api.criteria.LookupCustomizer;
 35  
 import org.kuali.rice.core.api.criteria.MultiValuedPredicate;
 36  
 import org.kuali.rice.core.api.criteria.NotEqualIgnoreCasePredicate;
 37  
 import org.kuali.rice.core.api.criteria.NotEqualPredicate;
 38  
 import org.kuali.rice.core.api.criteria.NotInIgnoreCasePredicate;
 39  
 import org.kuali.rice.core.api.criteria.NotInPredicate;
 40  
 import org.kuali.rice.core.api.criteria.NotLikePredicate;
 41  
 import org.kuali.rice.core.api.criteria.NotNullPredicate;
 42  
 import org.kuali.rice.core.api.criteria.NullPredicate;
 43  
 import org.kuali.rice.core.api.criteria.OrPredicate;
 44  
 import org.kuali.rice.core.api.criteria.Predicate;
 45  
 import org.kuali.rice.core.api.criteria.PropertyPathPredicate;
 46  
 import org.kuali.rice.core.api.criteria.QueryByCriteria;
 47  
 import org.kuali.rice.core.api.criteria.SingleValuedPredicate;
 48  
 import org.kuali.rice.core.framework.persistence.jpa.criteria.Criteria;
 49  
 import org.kuali.rice.krad.criteria.CriteriaLookupDao;
 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  
 /**
 61  
  * 
 62  
  * This class is a copy of the org.kuali.rice.krad.criteria.CriteriaLookupDaoJpa 
 63  
  * implementation in KRAD.
 64  
  * 
 65  
  * It should be removed once these problems are solved.
 66  
  * 
 67  
  * 1) lookup: Replace "new Criteria(queryClass.getClass().getName());" with
 68  
  *  new Criteria(queryClass.getName());
 69  
  *  
 70  
  * 2) forRowResults: jpaQuery.setFirstResult(criteria.getStartAtIndex() != null ? criteria.getStartAtIndex() : 0);
 71  
  * instead of final int startAtIndex = criteria.getStartAtIndex() != null ? criteria.getStartAtIndex() + 1 : 1;
 72  
  * jpaQuery.setFirstResult(startAtIndex);
 73  
  * and
 74  
  * jpaQuery.setMaxResults(criteria.getMaxResults() + 1);
 75  
  * and
 76  
  * rows.remove(criteria.getMaxResults().intValue() + 1);
 77  
  * 
 78  
  * @author Kuali Rice Team (kuali-rice@googlegroups.com)
 79  
  *
 80  
  */
 81  0
 public class CriteriaLookupDaoJpaImpl implements CriteriaLookupDao {
 82  
     @PersistenceContext
 83  
     private EntityManager entityManager;
 84  
 
 85  
     @Override
 86  
     public <T> GenericQueryResults<T> lookup(final Class<T> queryClass, final QueryByCriteria criteria) {
 87  0
         return lookup(queryClass, criteria, LookupCustomizer.Builder.<T>create().build());
 88  
     }
 89  
 
 90  
     @Override
 91  
     public <T> GenericQueryResults<T> lookup(final Class<T> queryClass, final QueryByCriteria criteria, LookupCustomizer<T> customizer) {
 92  0
         if (queryClass == null) {
 93  0
             throw new IllegalArgumentException("queryClass is null");
 94  
         }
 95  
 
 96  0
         if (criteria == null) {
 97  0
             throw new IllegalArgumentException("criteria is null");
 98  
         }
 99  
 
 100  0
         if (customizer == null) {
 101  0
             throw new IllegalArgumentException("customizer is null");
 102  
         }
 103  
 
 104  0
         final Criteria parent = new Criteria(queryClass.getName());
 105  
 
 106  0
         if (criteria.getPredicate() != null) {
 107  0
             addPredicate(criteria.getPredicate(), parent, customizer.getPredicateTransform());
 108  
         }
 109  
 
 110  0
         switch (criteria.getCountFlag()) {
 111  
             case ONLY:
 112  0
                 return forCountOnly(queryClass, criteria, parent);
 113  
             case NONE:
 114  0
                 return forRowResults(queryClass, criteria, parent, criteria.getCountFlag(), customizer.getResultTransform());
 115  
             case INCLUDE:
 116  0
                 return forRowResults(queryClass, criteria, parent, criteria.getCountFlag(), customizer.getResultTransform());
 117  0
             default: throw new UnsupportedCountFlagException(criteria.getCountFlag());
 118  
         }
 119  
     }
 120  
 
 121  
     /** gets results where the actual rows are requested. */
 122  
     private <T> GenericQueryResults<T> forRowResults(final Class<T> queryClass, final QueryByCriteria criteria, final Criteria jpaCriteria, CountFlag flag, LookupCustomizer.Transform<T, T> transform) {
 123  0
         final Query jpaQuery = new org.kuali.rice.core.framework.persistence.jpa.criteria.QueryByCriteria(entityManager, jpaCriteria).toQuery();
 124  0
         final GenericQueryResults.Builder<T> results = GenericQueryResults.Builder.<T>create();
 125  
 
 126  
         //ojb's is 1 based, our query api is zero based
 127  0
         jpaQuery.setFirstResult(criteria.getStartAtIndex() != null ? criteria.getStartAtIndex() : 0);
 128  
 
 129  0
         if (criteria.getMaxResults() != null) {
 130  
             //adding one to MaxResults in order to retrieve
 131  
             //one extra row so that the MoreResultsAvailable field can be set
 132  0
             jpaQuery.setMaxResults(criteria.getMaxResults() + 1);
 133  
         }
 134  
 
 135  
         @SuppressWarnings("unchecked")
 136  0
         final List<T> rows = new ArrayList<T>(jpaQuery.getResultList());
 137  0
         if (flag == CountFlag.INCLUDE) {
 138  0
             results.setTotalRowCount(rows.size());
 139  
         }
 140  
 
 141  0
         if (criteria.getMaxResults() != null && rows.size() > criteria.getMaxResults()) {
 142  0
             results.setMoreResultsAvailable(true);
 143  
             //remove the extra row that was returned
 144  0
             rows.remove(criteria.getMaxResults().intValue() + 1);
 145  
         }
 146  
 
 147  0
         results.setResults(transformResults(rows, transform));
 148  0
         return results.build();
 149  
     }
 150  
 
 151  
     private static <T> List<T> transformResults(List<T> results, LookupCustomizer.Transform<T, T> transform) {
 152  0
         final List<T> list = new ArrayList<T>();
 153  0
         for (T r : results) {
 154  0
             list.add(transform.apply(r));
 155  
         }
 156  0
         return list;
 157  
     }
 158  
 
 159  
     /** gets results where only the count is requested. */
 160  
     private <T> GenericQueryResults<T> forCountOnly(final Class<T> queryClass, final QueryByCriteria criteria, final Criteria jpaCriteria) {
 161  0
         final Query jpaQuery = new org.kuali.rice.core.framework.persistence.jpa.criteria.QueryByCriteria(entityManager, jpaCriteria).toQuery();
 162  0
         final GenericQueryResults.Builder<T> results = GenericQueryResults.Builder.<T>create();
 163  
         // TODO : There has to be a better way to do this.
 164  0
         results.setTotalRowCount(jpaQuery.getResultList().size());
 165  
 
 166  0
         return results.build();
 167  
     }
 168  
 
 169  
     /** adds a predicate to a Criteria.*/
 170  
     private void addPredicate(Predicate p, Criteria parent, LookupCustomizer.Transform<Predicate, Predicate> transform) {
 171  0
         p = transform.apply(p);
 172  
 
 173  0
         if (p instanceof PropertyPathPredicate) {
 174  0
             final String pp = ((PropertyPathPredicate) p).getPropertyPath();
 175  0
             if (p instanceof NotNullPredicate) {
 176  0
                 parent.notNull(pp);
 177  0
             } else if (p instanceof NullPredicate) {
 178  0
                 parent.isNull(pp);
 179  0
             } else if (p instanceof SingleValuedPredicate) {
 180  0
                 addSingleValuePredicate((SingleValuedPredicate) p, parent);
 181  0
             } else if (p instanceof MultiValuedPredicate) {
 182  0
                 addMultiValuePredicate((MultiValuedPredicate) p, parent);
 183  
             } else {
 184  0
                 throw new UnsupportedPredicateException(p);
 185  
             }
 186  0
         } else if (p instanceof CompositePredicate) {
 187  0
             addCompositePredicate((CompositePredicate) p, parent, transform);
 188  
         } else {
 189  0
             throw new UnsupportedPredicateException(p);
 190  
         }
 191  0
     }
 192  
 
 193  
     /** adds a single valued predicate to a Criteria. */
 194  
     private void addSingleValuePredicate(SingleValuedPredicate p, Criteria parent) {
 195  0
         final Object value = getVal(p.getValue());
 196  0
         final String pp = p.getPropertyPath();
 197  0
         if (p instanceof EqualPredicate) {
 198  0
             parent.eq(pp, value);
 199  0
         } else if (p instanceof EqualIgnoreCasePredicate) {
 200  0
             parent.eq(genUpperFunc(pp), ((String) value).toUpperCase());
 201  0
         } else if (p instanceof GreaterThanOrEqualPredicate) {
 202  0
             parent.gte(pp, value);
 203  0
         } else if (p instanceof GreaterThanPredicate) {
 204  0
             parent.gt(pp, value);
 205  0
         } else if (p instanceof LessThanOrEqualPredicate) {
 206  0
             parent.lte(pp, value);
 207  0
         } else if (p instanceof LessThanPredicate) {
 208  0
             parent.lt(pp, value);
 209  0
         } else if (p instanceof LikePredicate) {
 210  
             //no need to convert * or ? since ojb handles the conversion/escaping
 211  0
             parent.like(pp, value);
 212  0
         } else if (p instanceof NotEqualPredicate) {
 213  0
             parent.ne(pp, value);
 214  0
         } else if (p instanceof NotEqualIgnoreCasePredicate) {
 215  0
             parent.ne(genUpperFunc(pp), ((String) value).toUpperCase());
 216  0
         } else if (p instanceof NotLikePredicate) {
 217  0
             parent.notLike(pp, value);
 218  
         } else {
 219  0
             throw new UnsupportedPredicateException(p);
 220  
         }
 221  0
     }
 222  
 
 223  
     /** adds a multi valued predicate to a Criteria. */
 224  
     private void addMultiValuePredicate(MultiValuedPredicate p, Criteria parent) {
 225  0
         final String pp = p.getPropertyPath();
 226  0
         if (p instanceof InPredicate) {
 227  0
             final Set<?> values = getVals(p.getValues());
 228  0
             parent.in(pp, values);
 229  0
         } else if (p instanceof InIgnoreCasePredicate) {
 230  0
             final Set<String> values = toUpper(getValsUnsafe(((InIgnoreCasePredicate) p).getValues()));
 231  0
             parent.in(genUpperFunc(pp), values);
 232  0
         } else if (p instanceof NotInPredicate) {
 233  0
             final Set<?> values = getVals(p.getValues());
 234  0
             parent.notIn(pp, values);
 235  0
         } else if (p instanceof NotInIgnoreCasePredicate) {
 236  0
             final Set<String> values = toUpper(getValsUnsafe(((NotInIgnoreCasePredicate) p).getValues()));
 237  0
             parent.notIn(genUpperFunc(pp), values);
 238  0
         } else {
 239  0
             throw new UnsupportedPredicateException(p);
 240  
         }
 241  0
     }
 242  
 
 243  
     /** adds a composite predicate to a Criteria. */
 244  
     private void addCompositePredicate(final CompositePredicate p, final Criteria parent,  LookupCustomizer.Transform<Predicate, Predicate> transform) {
 245  0
         for (Predicate ip : p.getPredicates()) {
 246  0
             final Criteria inner = new Criteria(parent.getEntityName());
 247  0
             addPredicate(ip, inner, transform);
 248  0
             if (p instanceof AndPredicate) {
 249  0
                 parent.and(inner);
 250  0
             } else if (p instanceof OrPredicate) {
 251  0
                 parent.or(inner);
 252  
             } else {
 253  0
                 throw new UnsupportedPredicateException(p);
 254  
             }
 255  0
         }
 256  0
     }
 257  
 
 258  
     private static <U extends CriteriaValue<?>> Object getVal(U toConv) {
 259  0
         Object o = toConv.getValue();
 260  0
         if (o instanceof DateTime) {
 261  0
             return new Timestamp(((DateTime) o).getMillis());
 262  
         }
 263  0
         return o;
 264  
     }
 265  
 
 266  
     //this is unsafe b/c values could be converted resulting in a classcast exception
 267  
     @SuppressWarnings("unchecked")
 268  
     private static <T, U extends CriteriaValue<T>> Set<T> getValsUnsafe(Set<? extends U> toConv) {
 269  0
         return (Set<T>) getVals(toConv);
 270  
     }
 271  
 
 272  
     private static Set<?> getVals(Set<? extends CriteriaValue<?>> toConv) {
 273  0
         final Set<Object> values = new HashSet<Object>();
 274  0
         for (CriteriaValue<?> value : toConv) {
 275  0
             values.add(getVal(value));
 276  
         }
 277  0
         return values;
 278  
     }
 279  
 
 280  
     //eliding performance for function composition....
 281  
     private static Set<String> toUpper(Set<String> strs) {
 282  0
         final Set<String> values = new HashSet<String>();
 283  0
         for (String value : strs) {
 284  0
             values.add(value.toUpperCase());
 285  
         }
 286  0
         return values;
 287  
     }
 288  
 
 289  
 
 290  
     private String genUpperFunc(String pp) {
 291  0
         if (StringUtils.contains(pp, "__JPA_ALIAS[[")) {
 292  0
             pp = "UPPER(" + pp + ")";
 293  
         } else {
 294  0
             pp = "UPPER(__JPA_ALIAS[[0]]__." + pp + ")";
 295  
         }
 296  0
         return pp;
 297  
     }
 298  
 
 299  
     /**
 300  
      * @param entityManager the entityManager to set
 301  
      */
 302  
     public void setEntityManager(EntityManager entityManager) {
 303  0
         this.entityManager = entityManager;
 304  0
     }
 305  
 
 306  
     /** this is a fatal error since this implementation should support all known predicates. */
 307  0
     private static class UnsupportedPredicateException extends RuntimeException {
 308  
         private UnsupportedPredicateException(Predicate predicate) {
 309  0
             super("Unsupported predicate [" + String.valueOf(predicate) + "]");
 310  0
         }
 311  
     }
 312  
 
 313  
     /** this is a fatal error since this implementation should support all known count flags. */
 314  0
     private static class UnsupportedCountFlagException extends RuntimeException {
 315  
         private UnsupportedCountFlagException(CountFlag flag) {
 316  0
             super("Unsupported predicate [" + String.valueOf(flag) + "]");
 317  0
         }
 318  
     }
 319  
 }