Coverage Report - org.kuali.rice.krad.dao.impl.LookupDaoJpa
 
Classes in this File Line Coverage Branch Coverage Complexity
LookupDaoJpa
0%
0/356
0%
0/226
5.857
 
 1  
 /**
 2  
  * Copyright 2005-2011 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.rice.krad.dao.impl;
 17  
 
 18  
 import org.apache.commons.beanutils.PropertyUtils;
 19  
 import org.apache.commons.lang.StringUtils;
 20  
 import org.kuali.rice.core.api.datetime.DateTimeService;
 21  
 import org.kuali.rice.core.api.search.SearchOperator;
 22  
 import org.kuali.rice.core.api.util.RiceKeyConstants;
 23  
 import org.kuali.rice.core.api.util.type.TypeUtils;
 24  
 import org.kuali.rice.core.framework.persistence.jpa.criteria.Criteria;
 25  
 import org.kuali.rice.core.framework.persistence.jpa.criteria.QueryByCriteria;
 26  
 import org.kuali.rice.core.framework.persistence.jpa.metadata.EntityDescriptor;
 27  
 import org.kuali.rice.core.framework.persistence.jpa.metadata.FieldDescriptor;
 28  
 import org.kuali.rice.core.framework.persistence.jpa.metadata.MetadataManager;
 29  
 import org.kuali.rice.core.framework.persistence.ojb.conversion.OjbCharBooleanConversion;
 30  
 import org.kuali.rice.krad.bo.InactivatableFromTo;
 31  
 import org.kuali.rice.krad.bo.PersistableBusinessObject;
 32  
 import org.kuali.rice.krad.bo.PersistableBusinessObjectExtension;
 33  
 import org.kuali.rice.krad.dao.LookupDao;
 34  
 import org.kuali.rice.krad.lookup.CollectionIncomplete;
 35  
 import org.kuali.rice.krad.lookup.LookupUtils;
 36  
 import org.kuali.rice.krad.service.DataDictionaryService;
 37  
 import org.kuali.rice.krad.service.KRADServiceLocatorInternal;
 38  
 import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
 39  
 import org.kuali.rice.krad.service.PersistenceStructureService;
 40  
 import org.kuali.rice.krad.util.GlobalVariables;
 41  
 import org.kuali.rice.krad.util.KRADConstants;
 42  
 import org.kuali.rice.krad.util.KRADPropertyConstants;
 43  
 import org.kuali.rice.krad.util.ObjectUtils;
 44  
 import org.springframework.dao.DataIntegrityViolationException;
 45  
 
 46  
 import javax.persistence.EntityManager;
 47  
 import javax.persistence.PersistenceContext;
 48  
 import javax.persistence.PersistenceException;
 49  
 import java.lang.reflect.Field;
 50  
 import java.math.BigDecimal;
 51  
 import java.sql.Timestamp;
 52  
 import java.text.ParseException;
 53  
 import java.util.ArrayList;
 54  
 import java.util.Collection;
 55  
 import java.util.Iterator;
 56  
 import java.util.List;
 57  
 import java.util.Map;
 58  
 
 59  
 /**
 60  
  * This class is the JPA implementation of the LookupDao interface.
 61  
  */
 62  0
 public class LookupDaoJpa implements LookupDao {
 63  0
         private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(LookupDao.class);
 64  
 
 65  
         private DateTimeService dateTimeService;
 66  
         private PersistenceStructureService persistenceStructureService;
 67  
         private DataDictionaryService dataDictionaryService;
 68  
 
 69  
         @PersistenceContext
 70  
         private EntityManager entityManager;
 71  
 
 72  
     public Long findCountByMap(Object example, Map formProps) {
 73  0
                 Criteria criteria = new Criteria(example.getClass().getName());
 74  
 
 75  
                 // iterate through the parameter map for key values search criteria
 76  0
                 Iterator propsIter = formProps.keySet().iterator();
 77  0
                 while (propsIter.hasNext()) {
 78  0
                         String propertyName = (String) propsIter.next();
 79  0
                         String searchValue = (String) formProps.get(propertyName);
 80  
 
 81  
                         // if searchValue is empty and the key is not a valid property ignore
 82  0
                         if (StringUtils.isBlank(searchValue) || !(PropertyUtils.isWriteable(example, propertyName))) {
 83  0
                                 continue;
 84  
                         }
 85  
 
 86  
                         // get property type which is used to determine type of criteria
 87  0
                         Class propertyType = ObjectUtils.getPropertyType(example, propertyName, persistenceStructureService);
 88  0
                         if (propertyType == null) {
 89  0
                                 continue;
 90  
                         }
 91  0
                         Boolean caseInsensitive = Boolean.TRUE;
 92  0
                         if (KRADServiceLocatorWeb.getDataDictionaryService().isAttributeDefined(example.getClass(), propertyName)) {
 93  
                         // If forceUppercase is true, both the database value and the user entry should be converted to Uppercase -- so change the caseInsensitive to false since we don't need to 
 94  
                         // worry about the values not matching.  However, if forceUppercase is false, make sure to do a caseInsensitive search because the database value and user entry 
 95  
                         // could be mixed case.  Thus, caseInsensitive will be the opposite of forceUppercase. 
 96  0
                                 caseInsensitive = !KRADServiceLocatorWeb.getDataDictionaryService().getAttributeForceUppercase(example.getClass(), propertyName);
 97  
                         }
 98  0
                         if (caseInsensitive == null) {
 99  0
                                 caseInsensitive = Boolean.TRUE;
 100  
                         }
 101  
 
 102  0
                         boolean treatWildcardsAndOperatorsAsLiteral = KRADServiceLocatorWeb.
 103  
                                         getBusinessObjectDictionaryService().isLookupFieldTreatWildcardsAndOperatorsAsLiteral(example.getClass(), propertyName); 
 104  
                         // build criteria
 105  0
                     if (!caseInsensitive) { 
 106  
                             // Verify that the searchValue is uppercased if caseInsensitive is false 
 107  0
                             searchValue = searchValue.toUpperCase(); 
 108  
                     }
 109  0
                         addCriteria(propertyName, searchValue, propertyType, caseInsensitive, treatWildcardsAndOperatorsAsLiteral, criteria);
 110  0
                 }
 111  
 
 112  
                 // execute query and return result list
 113  0
                 return (Long) new QueryByCriteria(entityManager, criteria).toCountQuery().getSingleResult();
 114  
         }
 115  
 
 116  
         public Collection findCollectionBySearchHelper(Class businessObjectClass, Map formProps, boolean unbounded, boolean usePrimaryKeyValuesOnly) {
 117  0
                 PersistableBusinessObject businessObject = checkBusinessObjectClass(businessObjectClass);
 118  0
                 if (usePrimaryKeyValuesOnly) {
 119  0
                         return executeSearch(businessObjectClass, getCollectionCriteriaFromMapUsingPrimaryKeysOnly(businessObjectClass, formProps), unbounded);
 120  
                 } else {
 121  0
                         Criteria crit = getCollectionCriteriaFromMap(businessObject, formProps);
 122  0
                         return executeSearch(businessObjectClass, crit, unbounded);
 123  
                 }
 124  
         }
 125  
 
 126  
         public Criteria getCollectionCriteriaFromMap(PersistableBusinessObject example, Map formProps) {
 127  0
                 Criteria criteria = new Criteria(example.getClass().getName());
 128  0
                 Iterator propsIter = formProps.keySet().iterator();
 129  0
                 while (propsIter.hasNext()) {
 130  0
                         String propertyName = (String) propsIter.next();
 131  0
                         Boolean caseInsensitive = Boolean.TRUE;
 132  0
                         if (KRADServiceLocatorWeb.getDataDictionaryService().isAttributeDefined(example.getClass(), propertyName)) {
 133  0
                                 caseInsensitive = !KRADServiceLocatorWeb.getDataDictionaryService().getAttributeForceUppercase(example.getClass(), propertyName);
 134  
                         }
 135  0
                         if (caseInsensitive == null) {
 136  0
                                 caseInsensitive = Boolean.TRUE;
 137  
                         }
 138  0
             boolean treatWildcardsAndOperatorsAsLiteral = KRADServiceLocatorWeb.
 139  
                                     getBusinessObjectDictionaryService().isLookupFieldTreatWildcardsAndOperatorsAsLiteral(example.getClass(), propertyName);
 140  0
                         if (formProps.get(propertyName) instanceof Collection) {
 141  0
                                 Iterator iter = ((Collection) formProps.get(propertyName)).iterator();
 142  0
                                 while (iter.hasNext()) {
 143  0
                     String searchValue = (String) iter.next();
 144  0
                             if (!caseInsensitive) { 
 145  
                                     // Verify that the searchValue is uppercased if caseInsensitive is false 
 146  0
                                     searchValue = searchValue.toUpperCase(); 
 147  
                             }
 148  0
                                         if (!createCriteria(example, searchValue, propertyName, caseInsensitive, treatWildcardsAndOperatorsAsLiteral, criteria, formProps)) {
 149  0
                                                 throw new RuntimeException("Invalid value in Collection");
 150  
                                         }
 151  0
                                 }
 152  0
                         } else {
 153  0
                 String searchValue = (String) formProps.get(propertyName);
 154  0
                         if (!caseInsensitive) { 
 155  
                                 // Verify that the searchValue is uppercased if caseInsensitive is false 
 156  0
                                 searchValue = searchValue.toUpperCase(); 
 157  
                         }
 158  0
                                 if (!createCriteria(example, searchValue, propertyName, caseInsensitive, treatWildcardsAndOperatorsAsLiteral, criteria, formProps)) {
 159  0
                                         continue;
 160  
                                 }
 161  
                         }
 162  0
                 }
 163  0
                 return criteria;
 164  
         }
 165  
 
 166  
         public Criteria getCollectionCriteriaFromMapUsingPrimaryKeysOnly(Class businessObjectClass, Map formProps) {
 167  0
                 PersistableBusinessObject businessObject = checkBusinessObjectClass(businessObjectClass);
 168  0
                 Criteria criteria = new Criteria(businessObjectClass.getName());
 169  0
                 List pkFields = persistenceStructureService.listPrimaryKeyFieldNames(businessObjectClass);
 170  0
                 Iterator pkIter = pkFields.iterator();
 171  0
                 while (pkIter.hasNext()) {
 172  0
                         String pkFieldName = (String) pkIter.next();
 173  0
                         String pkValue = (String) formProps.get(pkFieldName);
 174  
 
 175  0
                         if (StringUtils.isBlank(pkValue)) {
 176  0
                                 throw new RuntimeException("Missing pk value for field " + pkFieldName + " when a search based on PK values only is performed.");
 177  
                         } else {
 178  0
                                 for (SearchOperator op :  SearchOperator.QUERY_CHARACTERS) {
 179  0
                     if (pkValue.contains(op.op())) {
 180  0
                         throw new RuntimeException("Value \"" + pkValue + "\" for PK field " + pkFieldName + " contains wildcard/operator characters.");
 181  
                     }
 182  
                 }
 183  
                         }
 184  0
             boolean treatWildcardsAndOperatorsAsLiteral = KRADServiceLocatorWeb.
 185  
                                     getBusinessObjectDictionaryService().isLookupFieldTreatWildcardsAndOperatorsAsLiteral(businessObjectClass, pkFieldName);
 186  0
                         createCriteria(businessObject, pkValue, pkFieldName, false, treatWildcardsAndOperatorsAsLiteral, criteria);
 187  0
                 }
 188  0
                 return criteria;
 189  
         }
 190  
 
 191  
         private PersistableBusinessObject checkBusinessObjectClass(Class businessObjectClass) {
 192  0
                 if (businessObjectClass == null) {
 193  0
                         throw new IllegalArgumentException("BusinessObject class passed to LookupDao findCollectionBySearchHelper... method was null");
 194  
                 }
 195  0
                 PersistableBusinessObject businessObject = null;
 196  
                 try {
 197  0
                         businessObject = (PersistableBusinessObject) businessObjectClass.newInstance();
 198  0
                 } catch (IllegalAccessException e) {
 199  0
                         throw new RuntimeException("LookupDao could not get instance of " + businessObjectClass.getName(), e);
 200  0
                 } catch (InstantiationException e) {
 201  0
                         throw new RuntimeException("LookupDao could not get instance of " + businessObjectClass.getName(), e);
 202  0
                 }
 203  0
                 return businessObject;
 204  
         }
 205  
 
 206  
         private Collection executeSearch(Class businessObjectClass, Criteria criteria, boolean unbounded) {
 207  0
                 Collection<PersistableBusinessObject> searchResults = new ArrayList<PersistableBusinessObject>();
 208  0
                 Long matchingResultsCount = null;
 209  
                 try {
 210  0
                         Integer searchResultsLimit = LookupUtils.getSearchResultsLimit(businessObjectClass);
 211  0
                         if (!unbounded && (searchResultsLimit != null)) {
 212  0
                                 matchingResultsCount = (Long) new QueryByCriteria(entityManager, criteria).toCountQuery().getSingleResult();
 213  0
                                 searchResults = new QueryByCriteria(entityManager, criteria).toQuery().setMaxResults(searchResultsLimit).getResultList();
 214  
                         } else {
 215  0
                                 searchResults = new QueryByCriteria(entityManager, criteria).toQuery().getResultList();
 216  
                         }
 217  0
                         if ((matchingResultsCount == null) || (matchingResultsCount.intValue() <= searchResultsLimit.intValue())) {
 218  0
                                 matchingResultsCount = new Long(0);
 219  
                         }
 220  
                         // Temp solution for loading extension objects - need to find a
 221  
                         // better way
 222  
                         // Should look for a JOIN query, for the above query, that will grab
 223  
                         // the PBOEs as well (1+n query problem)
 224  0
                         for (PersistableBusinessObject bo : searchResults) {
 225  0
                                 if (bo.getExtension() != null) {
 226  0
                                         PersistableBusinessObjectExtension boe = bo.getExtension();
 227  0
                                         EntityDescriptor entity = MetadataManager.getEntityDescriptor(bo.getExtension().getClass());
 228  0
                                         Criteria extensionCriteria = new Criteria(boe.getClass().getName());
 229  0
                                         for (FieldDescriptor fieldDescriptor : entity.getPrimaryKeys()) {
 230  
                                                 try {
 231  0
                                                         Field field = bo.getClass().getDeclaredField(fieldDescriptor.getName());
 232  0
                                                         field.setAccessible(true);
 233  0
                                                         extensionCriteria.eq(fieldDescriptor.getName(), field.get(bo));
 234  0
                                                 } catch (Exception e) {
 235  0
                                                         LOG.error(e.getMessage(), e);
 236  0
                                                 }
 237  
                                         }
 238  
                                         try {
 239  0
                                                 boe = (PersistableBusinessObjectExtension) new QueryByCriteria(entityManager, extensionCriteria).toQuery().getSingleResult();
 240  0
                                         } catch (PersistenceException e) {}
 241  0
                                         bo.setExtension(boe);
 242  0
                                 }
 243  
                         }
 244  
                         // populate Person objects in business objects
 245  0
                         List bos = new ArrayList();
 246  0
                         bos.addAll(searchResults);
 247  0
                         searchResults = bos;
 248  0
                 } catch (DataIntegrityViolationException e) {
 249  0
                         throw new RuntimeException("LookupDao encountered exception during executeSearch", e);
 250  0
                 }
 251  0
                 return new CollectionIncomplete(searchResults, matchingResultsCount);
 252  
         }
 253  
 
 254  
         /**
 255  
          * Return whether or not an attribute is writeable. This method is aware
 256  
          * that that Collections may be involved and handles them consistently with
 257  
          * the way in which OJB handles specifying the attributes of elements of a
 258  
          * Collection.
 259  
          * 
 260  
          * @param o
 261  
          * @param p
 262  
          * @return
 263  
          * @throws IllegalArgumentException
 264  
          */
 265  
         private boolean isWriteable(Object o, String p) throws IllegalArgumentException {
 266  0
                 if (null == o || null == p) {
 267  0
                         throw new IllegalArgumentException("Cannot check writeable status with null arguments.");
 268  
                 }
 269  
 
 270  0
                 boolean b = false;
 271  
 
 272  
                 // Try the easy way.
 273  0
                 if (!(PropertyUtils.isWriteable(o, p))) {
 274  
 
 275  
                         // If that fails lets try to be a bit smarter, understanding that
 276  
                         // Collections may be involved.
 277  0
                         if (-1 != p.indexOf('.')) {
 278  
 
 279  0
                                 String[] parts = p.split("\\.");
 280  
 
 281  
                                 // Get the type of the attribute.
 282  0
                                 Class c = ObjectUtils.getPropertyType(o, parts[0], persistenceStructureService);
 283  
 
 284  0
                                 Object i = null;
 285  
 
 286  
                                 // If the next level is a Collection, look into the collection,
 287  
                                 // to find out what type its elements are.
 288  0
                                 if (Collection.class.isAssignableFrom(c)) {
 289  0
                                         Map<String, Class> m = persistenceStructureService.listCollectionObjectTypes(o.getClass());
 290  0
                                         c = m.get(parts[0]);
 291  
                                 }
 292  
 
 293  
                                 // Look into the attribute class to see if it is writeable.
 294  
                                 try {
 295  0
                                         i = c.newInstance();
 296  0
                                         StringBuffer sb = new StringBuffer();
 297  0
                                         for (int x = 1; x < parts.length; x++) {
 298  0
                                                 sb.append(1 == x ? "" : ".").append(parts[x]);
 299  
                                         }
 300  0
                                         b = isWriteable(i, sb.toString());
 301  0
                                 } catch (InstantiationException ie) {
 302  0
                                         LOG.info(ie);
 303  0
                                 } catch (IllegalAccessException iae) {
 304  0
                                         LOG.info(iae);
 305  0
                                 }
 306  0
                         }
 307  
                 } else {
 308  0
                         b = true;
 309  
                 }
 310  
 
 311  0
                 return b;
 312  
         }
 313  
 
 314  
         public boolean createCriteria(Object example, String searchValue, String propertyName, Object criteria) {
 315  0
                 return createCriteria(example, searchValue, propertyName, false, false, criteria);
 316  
         }
 317  
         
 318  
     public boolean createCriteria(Object example, String searchValue, String propertyName, boolean caseInsensitive, boolean treatWildcardsAndOperatorsAsLiteral, Object criteria) {
 319  0
             return createCriteria( example, searchValue, propertyName, false, false, criteria, null );
 320  
     }
 321  
 
 322  
         public boolean createCriteria(Object example, String searchValue, String propertyName, boolean caseInsensitive, boolean treatWildcardsAndOperatorsAsLiteral, Object criteria, Map searchValues) {
 323  
                 // if searchValue is empty and the key is not a valid property ignore
 324  0
                 if (!(criteria instanceof Criteria) || StringUtils.isBlank(searchValue) || !isWriteable(example, propertyName)) {
 325  0
                         return false;
 326  
                 }
 327  
 
 328  
                 // get property type which is used to determine type of criteria
 329  0
                 Class propertyType = ObjectUtils.getPropertyType(example, propertyName, persistenceStructureService);
 330  0
                 if (propertyType == null) {
 331  0
                         return false;
 332  
                 }
 333  
 
 334  
                 // build criteria
 335  0
                 if (example instanceof InactivatableFromTo) {
 336  0
                         if (KRADPropertyConstants.ACTIVE.equals(propertyName)) {
 337  0
                                 addInactivateableFromToActiveCriteria(example, searchValue, (Criteria) criteria, searchValues);
 338  0
                         } else if (KRADPropertyConstants.CURRENT.equals(propertyName)) {
 339  0
                                 addInactivateableFromToCurrentCriteria(example, searchValue, (Criteria) criteria, searchValues);
 340  0
                         } else if (!KRADPropertyConstants.ACTIVE_AS_OF_DATE.equals(propertyName)) {
 341  0
                                 addCriteria(propertyName, searchValue, propertyType, caseInsensitive,
 342  
                                                 treatWildcardsAndOperatorsAsLiteral, (Criteria) criteria);
 343  
                         }
 344  
                 } else {
 345  0
                         addCriteria(propertyName, searchValue, propertyType, caseInsensitive, treatWildcardsAndOperatorsAsLiteral,
 346  
                                         (Criteria) criteria);
 347  
                 }
 348  
                 
 349  0
                 return true;
 350  
         }
 351  
 
 352  
         /**
 353  
          * @see org.kuali.rice.krad.dao.LookupDao#findObjectByMap(java.lang.Object,
 354  
          *      java.util.Map)
 355  
          */
 356  
         public Object findObjectByMap(Object example, Map formProps) {
 357  0
                 Criteria jpaCriteria = new Criteria(example.getClass().getName());
 358  
 
 359  0
                 Iterator propsIter = formProps.keySet().iterator();
 360  0
                 while (propsIter.hasNext()) {
 361  0
                         String propertyName = (String) propsIter.next();
 362  0
                         String searchValue = "";
 363  0
                         if (formProps.get(propertyName) != null) {
 364  0
                                 searchValue = (formProps.get(propertyName)).toString();
 365  
                         }
 366  
 
 367  0
                         if (StringUtils.isNotBlank(searchValue) & PropertyUtils.isWriteable(example, propertyName)) {
 368  0
                                 Class propertyType = ObjectUtils.getPropertyType(example, propertyName, persistenceStructureService);
 369  0
                                 if (TypeUtils.isIntegralClass(propertyType) || TypeUtils.isDecimalClass(propertyType)) {
 370  0
                                         if (propertyType.equals(Long.class)) {
 371  0
                                                 jpaCriteria.eq(propertyName, new Long(searchValue));
 372  
                                         } else {
 373  0
                                                 jpaCriteria.eq(propertyName, new Integer(searchValue));
 374  
                                         }
 375  0
                                 } else if (TypeUtils.isTemporalClass(propertyType)) {
 376  0
                                         jpaCriteria.eq(propertyName, parseDate(ObjectUtils.clean(searchValue)));
 377  
                                 } else {
 378  0
                                         jpaCriteria.eq(propertyName, searchValue);
 379  
                                 }
 380  
                         }
 381  0
                 }
 382  
 
 383  0
                 return new QueryByCriteria(entityManager, jpaCriteria).toQuery().getSingleResult();
 384  
         }
 385  
 
 386  
         private void addCriteria(String propertyName, String propertyValue, Class propertyType, boolean caseInsensitive, boolean treatWildcardsAndOperatorsAsLiteral, Criteria criteria) {
 387  0
                 String alias = "";
 388  0
                 String[] keySplit = propertyName.split("\\.");
 389  0
                 if (keySplit.length > 1) {
 390  0
                         alias = keySplit[keySplit.length-2];
 391  0
                         String variableKey = keySplit[keySplit.length-1];
 392  0
                         for (int j = 0; j < keySplit.length - 1; j++)  {
 393  0
                                 if (StringUtils.contains(keySplit[j], Criteria.JPA_ALIAS_PREFIX)) {
 394  0
                                         String tempKey = keySplit[j].substring(keySplit[j].indexOf('\'', keySplit[j].indexOf(Criteria.JPA_ALIAS_PREFIX)) + 1,
 395  
                                                         keySplit[j].lastIndexOf('\'', keySplit[j].indexOf(Criteria.JPA_ALIAS_SUFFIX)));
 396  0
                                         if (criteria.getAliasIndex(tempKey) == -1) {
 397  0
                                                 criteria.join(tempKey, tempKey, false, true);
 398  
                                         }
 399  0
                                 } else {
 400  0
                                         if (criteria.getAliasIndex(keySplit[j]) == -1) {
 401  0
                                                 criteria.join(keySplit[j], keySplit[j], false, true);
 402  
                                         }
 403  
                                 }
 404  
                         }
 405  0
                         if (!StringUtils.contains(propertyName, "__JPA_ALIAS[[")) {
 406  0
                                 propertyName = "__JPA_ALIAS[['" + alias + "']]__." + variableKey;
 407  
                         }
 408  
                 }
 409  0
                 if (!treatWildcardsAndOperatorsAsLiteral && StringUtils.contains(propertyValue, SearchOperator.OR.op())) {
 410  0
                         addOrCriteria(propertyName, propertyValue, propertyType, caseInsensitive, criteria);
 411  0
                         return;
 412  
                 }
 413  
 
 414  0
                 if (!treatWildcardsAndOperatorsAsLiteral && StringUtils.contains(propertyValue, SearchOperator.AND.op())) {
 415  0
                         addAndCriteria(propertyName, propertyValue, propertyType, caseInsensitive, criteria);
 416  0
                         return;
 417  
                 }
 418  
 
 419  0
         if (StringUtils.containsIgnoreCase(propertyValue, SearchOperator.NULL.op())) {
 420  0
                 if (StringUtils.contains(propertyValue, SearchOperator.NOT.op())) {
 421  0
                         criteria.notNull(propertyName);
 422  
                 }
 423  
                 else {
 424  0
                         criteria.isNull(propertyName);
 425  
                 }
 426  
         }
 427  0
         else if (TypeUtils.isStringClass(propertyType)) {
 428  
                         // KULRICE-85 : made string searches case insensitive - used new
 429  
                         // DBPlatform function to force strings to upper case
 430  0
                         if (caseInsensitive) {
 431  
                                 // TODO: What to do here now that the JPA version does not extend platform aware?
 432  
                                 //propertyName = getDbPlatform().getUpperCaseFunction() + "(__JPA_ALIAS[[0]]__." + propertyName + ")";
 433  0
                                 if (StringUtils.contains(propertyName, "__JPA_ALIAS[[")) {
 434  0
                                         propertyName = "UPPER(" + propertyName + ")";
 435  
                                 } else {
 436  0
                                         propertyName = "UPPER(__JPA_ALIAS[[0]]__." + propertyName + ")";
 437  
                                 }
 438  0
                                 propertyValue = propertyValue.toUpperCase();
 439  
                         }
 440  0
                         if (!treatWildcardsAndOperatorsAsLiteral && StringUtils.contains(propertyValue,
 441  
                                         SearchOperator.NOT.op())) {
 442  0
                                 addNotCriteria(propertyName, propertyValue, propertyType,
 443  
                                                 caseInsensitive, criteria);
 444  0
             } else if (
 445  
                             !treatWildcardsAndOperatorsAsLiteral && propertyValue != null && (
 446  
                                             StringUtils.contains(propertyValue, SearchOperator.BETWEEN.op())
 447  
                                             || propertyValue.startsWith(">")
 448  
                                             || propertyValue.startsWith("<") ) ) {
 449  0
                                 addStringRangeCriteria(propertyName, propertyValue, criteria);
 450  
                         } else {
 451  0
                                 if (treatWildcardsAndOperatorsAsLiteral) {
 452  0
                                         propertyValue = StringUtils.replace(propertyValue, "*", "\\*");
 453  
                                 }
 454  0
                                 criteria.like(propertyName, propertyValue);
 455  
                         }
 456  0
                 } else if (TypeUtils.isIntegralClass(propertyType) || TypeUtils.isDecimalClass(propertyType)) {
 457  0
                         addNumericRangeCriteria(propertyName, propertyValue, criteria);
 458  0
                 } else if (TypeUtils.isTemporalClass(propertyType)) {
 459  0
                         addDateRangeCriteria(propertyName, propertyValue, criteria);
 460  0
                 } else if (TypeUtils.isBooleanClass(propertyType)) {
 461  0
                         String temp = ObjectUtils.clean(propertyValue);
 462  0
                         criteria.eq(propertyName, ("Y".equalsIgnoreCase(temp) || "T".equalsIgnoreCase(temp) || "1".equalsIgnoreCase(temp) || "true".equalsIgnoreCase(temp)) ? true : false);
 463  0
                 } else {
 464  0
                         LOG.error("not adding criterion for: " + propertyName + "," + propertyType + "," + propertyValue);
 465  
                 }
 466  0
         }
 467  
         
 468  
     
 469  
     /**
 470  
      * Translates criteria for active status to criteria on the active from and to fields
 471  
      * 
 472  
      * @param example - business object being queried on
 473  
      * @param activeSearchValue - value for the active search field, should convert to boolean
 474  
      * @param criteria - Criteria object being built
 475  
      * @param searchValues - Map containing all search keys and values
 476  
      */
 477  
     protected void addInactivateableFromToActiveCriteria(Object example, String activeSearchValue, Criteria criteria, Map searchValues) {
 478  0
             Timestamp activeTimestamp = LookupUtils.getActiveDateTimestampForCriteria(searchValues);
 479  
                 
 480  0
             String activeBooleanStr = (String) (new OjbCharBooleanConversion()).javaToSql(activeSearchValue);
 481  0
             if (OjbCharBooleanConversion.DATABASE_BOOLEAN_TRUE_STRING_REPRESENTATION.equals(activeBooleanStr)) {
 482  
                     // (active from date <= date or active from date is null) and (date < active to date or active to date is null)
 483  0
                     Criteria criteriaBeginDate = new Criteria(example.getClass().getName());
 484  0
                     criteriaBeginDate.lte(KRADPropertyConstants.ACTIVE_FROM_DATE, activeTimestamp);
 485  
                     
 486  0
                     Criteria criteriaBeginDateNull = new Criteria(example.getClass().getName());
 487  0
                     criteriaBeginDateNull.isNull(KRADPropertyConstants.ACTIVE_FROM_DATE);
 488  0
                     criteriaBeginDate.or(criteriaBeginDateNull);
 489  
                     
 490  0
                     criteria.and(criteriaBeginDate);
 491  
                     
 492  0
                     Criteria criteriaEndDate = new Criteria(example.getClass().getName());
 493  0
                     criteriaEndDate.gt(KRADPropertyConstants.ACTIVE_TO_DATE, activeTimestamp);
 494  
             
 495  0
                     Criteria criteriaEndDateNull = new Criteria(example.getClass().getName());
 496  0
                     criteriaEndDateNull.isNull(KRADPropertyConstants.ACTIVE_TO_DATE);
 497  0
                     criteriaEndDate.or(criteriaEndDateNull);
 498  
                     
 499  0
                     criteria.and(criteriaEndDate);
 500  0
             }
 501  0
             else if (OjbCharBooleanConversion.DATABASE_BOOLEAN_FALSE_STRING_REPRESENTATION.equals(activeBooleanStr)) {
 502  
                     // (date < active from date) or (active from date is null) or (date >= active to date) 
 503  0
                     Criteria criteriaNonActive = new Criteria(example.getClass().getName());
 504  0
                     criteriaNonActive.gt(KRADPropertyConstants.ACTIVE_FROM_DATE, activeTimestamp);
 505  
                     
 506  0
                     Criteria criteriaBeginDateNull = new Criteria(example.getClass().getName());
 507  0
                     criteriaBeginDateNull.isNull(KRADPropertyConstants.ACTIVE_FROM_DATE);
 508  0
                     criteriaNonActive.or(criteriaBeginDateNull);
 509  
                     
 510  0
                     Criteria criteriaEndDate = new Criteria(example.getClass().getName());
 511  0
                     criteriaEndDate.lte(KRADPropertyConstants.ACTIVE_TO_DATE, activeTimestamp);
 512  0
                     criteriaNonActive.or(criteriaEndDate);
 513  
                     
 514  0
                     criteria.and(criteriaNonActive);
 515  
             }
 516  0
     }
 517  
     
 518  
     /**
 519  
      * Translates criteria for current status to a sub-query on active begin date
 520  
      * 
 521  
      * @param example - business object being queried on
 522  
      * @param currentSearchValue - value for the current search field, should convert to boolean
 523  
      * @param criteria - Criteria object being built
 524  
      */
 525  
         protected void addInactivateableFromToCurrentCriteria(Object example, String currentSearchValue, Criteria criteria, Map searchValues) {
 526  0
                 Timestamp activeTimestamp = LookupUtils.getActiveDateTimestampForCriteria(searchValues);
 527  
                 
 528  0
                 List<String> groupByFieldList = dataDictionaryService.getGroupByAttributesForEffectiveDating(example
 529  
                                 .getClass());
 530  0
                 if (groupByFieldList == null) {
 531  0
                         return;
 532  
                 }
 533  
 
 534  0
                 String alias = "c";
 535  
 
 536  0
                 String jpql = " (select max(" + alias + "." + KRADPropertyConstants.ACTIVE_FROM_DATE + ") from "
 537  
                                 + example.getClass().getName() + " as " + alias + " where ";
 538  0
                 String activeDateDBStr = KRADServiceLocatorInternal.getDatabasePlatform().getDateSQL(dateTimeService.toDateTimeString(activeTimestamp), null);
 539  0
                 jpql += alias + "." + KRADPropertyConstants.ACTIVE_FROM_DATE + " <= '" + activeDateDBStr + "'";
 540  
 
 541  
                 // join back to main query with the group by fields
 542  0
                 boolean firstGroupBy = true;
 543  0
                 String groupByJpql = "";
 544  0
                 for (String groupByField : groupByFieldList) {
 545  0
                         if (!firstGroupBy) {
 546  0
                                 groupByJpql += ", ";
 547  
                         }
 548  
 
 549  0
                         jpql += " AND " + alias + "." + groupByField + " = " + criteria.getAlias() + "." + groupByField + " ";
 550  0
                         groupByJpql += alias + "." + groupByField;
 551  0
                         firstGroupBy = false;
 552  
                 }
 553  
 
 554  0
                 jpql += " group by " + groupByJpql + " )";
 555  
 
 556  0
                 String currentBooleanStr = (String) (new OjbCharBooleanConversion()).javaToSql(currentSearchValue);
 557  0
                 if (OjbCharBooleanConversion.DATABASE_BOOLEAN_TRUE_STRING_REPRESENTATION.equals(currentBooleanStr)) {
 558  0
                         jpql = criteria.getAlias() + "." + KRADPropertyConstants.ACTIVE_FROM_DATE + " in " + jpql;
 559  0
                 } else if (OjbCharBooleanConversion.DATABASE_BOOLEAN_FALSE_STRING_REPRESENTATION.equals(currentBooleanStr)) {
 560  0
                         jpql = criteria.getAlias() + "." + KRADPropertyConstants.ACTIVE_FROM_DATE + " not in " + jpql;
 561  
                 }
 562  
 
 563  0
                 criteria.rawJpql(jpql);
 564  0
         }
 565  
 
 566  
         private void addOrCriteria(String propertyName, String propertyValue, Class propertyType, boolean caseInsensitive, Criteria criteria) {
 567  0
                 addLogicalOperatorCriteria(propertyName, propertyValue, propertyType, caseInsensitive, criteria, SearchOperator.OR.op());
 568  0
         }
 569  
 
 570  
         private void addAndCriteria(String propertyName, String propertyValue, Class propertyType, boolean caseInsensitive, Criteria criteria) {
 571  0
                 addLogicalOperatorCriteria(propertyName, propertyValue, propertyType, caseInsensitive, criteria, SearchOperator.AND.op());
 572  0
         }
 573  
 
 574  
         private void addNotCriteria(String propertyName, String propertyValue, Class propertyType, boolean caseInsensitive, Criteria criteria) {
 575  0
                 String[] splitPropVal = StringUtils.split(propertyValue, SearchOperator.NOT.op());
 576  
 
 577  0
                 int strLength = splitPropVal.length;
 578  
                 // if more than one NOT operator assume an implicit and (i.e. !a!b = !a&!b)
 579  0
                 if (strLength > 1) {
 580  0
                         String expandedNot = "!" + StringUtils.join(splitPropVal, SearchOperator.AND.op() + SearchOperator.NOT.op());
 581  
                         // we know that since this method is called, treatWildcardsAndOperatorsAsLiteral is false
 582  0
                         addCriteria(propertyName, expandedNot, propertyType, caseInsensitive, false, criteria);
 583  0
                 } else {
 584  
                         // only one so add a not like
 585  0
                         criteria.notLike(propertyName, splitPropVal[0]);
 586  
                 }
 587  0
         }
 588  
 
 589  
         private void addLogicalOperatorCriteria(String propertyName, String propertyValue, Class propertyType, boolean caseInsensitive, Criteria criteria, String splitValue) {
 590  0
                 String[] splitPropVal = StringUtils.split(propertyValue, splitValue);
 591  
 
 592  0
                 Criteria subCriteria = new Criteria("N/A");
 593  0
                 for (int i = 0; i < splitPropVal.length; i++) {
 594  0
                         Criteria predicate = new Criteria("N/A");
 595  
                         // we know that since this method is called, treatWildcardsAndOperatorsAsLiteral is false
 596  0
                         addCriteria(propertyName, splitPropVal[i], propertyType, caseInsensitive, false, predicate);
 597  0
                         if (splitValue == SearchOperator.OR.op()) {
 598  0
                                 subCriteria.or(predicate);
 599  
                         }
 600  0
                         if (splitValue == SearchOperator.AND.op()) {
 601  0
                                 subCriteria.and(predicate);
 602  
                         }
 603  
                 }
 604  
 
 605  0
                 criteria.and(subCriteria);
 606  0
         }
 607  
 
 608  
         private java.sql.Date parseDate(String dateString) {
 609  0
                 dateString = dateString.trim();
 610  
                 try {
 611  0
                         return dateTimeService.convertToSqlDate(dateString);
 612  0
                 } catch (ParseException ex) {
 613  0
                         return null;
 614  
                 }
 615  
         }
 616  
 
 617  
         private void addDateRangeCriteria(String propertyName, String propertyValue, Criteria criteria) {
 618  
 
 619  0
                 if (StringUtils.contains(propertyValue, SearchOperator.BETWEEN.op())) {
 620  0
                         String[] rangeValues = StringUtils.split(propertyValue, SearchOperator.BETWEEN.op());
 621  0
                         criteria.between(propertyName, parseDate(ObjectUtils.clean(rangeValues[0])), parseDate(ObjectUtils.clean(rangeValues[1])));
 622  0
                 } else if (propertyValue.startsWith(">=")) {
 623  0
                         criteria.gte(propertyName, parseDate(ObjectUtils.clean(propertyValue)));
 624  0
                 } else if (propertyValue.startsWith("<=")) {
 625  0
                         criteria.lte(propertyName, parseDate(ObjectUtils.clean(propertyValue)));
 626  0
                 } else if (propertyValue.startsWith(">")) {
 627  0
                         criteria.gt(propertyName, parseDate(ObjectUtils.clean(propertyValue)));
 628  0
                 } else if (propertyValue.startsWith("<")) {
 629  0
                         criteria.lt(propertyName, parseDate(ObjectUtils.clean(propertyValue)));
 630  
                 } else {
 631  0
                         criteria.eq(propertyName, parseDate(ObjectUtils.clean(propertyValue)));
 632  
                 }
 633  0
         }
 634  
 
 635  
         private BigDecimal cleanNumeric(String value) {
 636  0
                 String cleanedValue = value.replaceAll("[^-0-9.]", "");
 637  
                 // ensure only one "minus" at the beginning, if any
 638  0
                 if (cleanedValue.lastIndexOf('-') > 0) {
 639  0
                         if (cleanedValue.charAt(0) == '-') {
 640  0
                                 cleanedValue = "-" + cleanedValue.replaceAll("-", "");
 641  
                         } else {
 642  0
                                 cleanedValue = cleanedValue.replaceAll("-", "");
 643  
                         }
 644  
                 }
 645  
                 // ensure only one decimal in the string
 646  0
                 int decimalLoc = cleanedValue.lastIndexOf('.');
 647  0
                 if (cleanedValue.indexOf('.') != decimalLoc) {
 648  0
                         cleanedValue = cleanedValue.substring(0, decimalLoc).replaceAll("\\.", "") + cleanedValue.substring(decimalLoc);
 649  
                 }
 650  
                 try {
 651  0
                         return new BigDecimal(cleanedValue);
 652  0
                 } catch (NumberFormatException ex) {
 653  0
                         GlobalVariables.getMessageMap().putError(KRADConstants.DOCUMENT_ERRORS, RiceKeyConstants.ERROR_CUSTOM, new String[] { "Invalid Numeric Input: " + value });
 654  0
                         return null;
 655  
                 }
 656  
         }
 657  
 
 658  
         private void addNumericRangeCriteria(String propertyName, String propertyValue, Criteria criteria) {
 659  
 
 660  0
                 if (StringUtils.contains(propertyValue, SearchOperator.BETWEEN.op())) {
 661  0
                         String[] rangeValues = StringUtils.split(propertyValue, SearchOperator.BETWEEN.op());
 662  0
                         criteria.between(propertyName, cleanNumeric(rangeValues[0]), cleanNumeric(rangeValues[1]));
 663  0
                 } else if (propertyValue.startsWith(">=")) {
 664  0
                         criteria.gte(propertyName, cleanNumeric(propertyValue));
 665  0
                 } else if (propertyValue.startsWith("<=")) {
 666  0
                         criteria.lte(propertyName, cleanNumeric(propertyValue));
 667  0
                 } else if (propertyValue.startsWith(">")) {
 668  0
                         criteria.gt(propertyName, cleanNumeric(propertyValue));
 669  0
                 } else if (propertyValue.startsWith("<")) {
 670  0
                         criteria.lt(propertyName, cleanNumeric(propertyValue));
 671  
                 } else {
 672  0
                         criteria.eq(propertyName, cleanNumeric(propertyValue));
 673  
                 }
 674  0
         }
 675  
 
 676  
         private void addStringRangeCriteria(String propertyName, String propertyValue, Criteria criteria) {
 677  
 
 678  0
                 if (StringUtils.contains(propertyValue, SearchOperator.BETWEEN.op())) {
 679  0
                         String[] rangeValues = StringUtils.split(propertyValue, SearchOperator.BETWEEN.op());
 680  0
                         criteria.between(propertyName, rangeValues[0], rangeValues[1]);
 681  0
                 } else if (propertyValue.startsWith(">=")) {
 682  0
                         criteria.gte(propertyName, ObjectUtils.clean(propertyValue));
 683  0
                 } else if (propertyValue.startsWith("<=")) {
 684  0
                         criteria.lte(propertyName, ObjectUtils.clean(propertyValue));
 685  0
                 } else if (propertyValue.startsWith(">")) {
 686  0
                         criteria.gt(propertyName, ObjectUtils.clean(propertyValue));
 687  0
                 } else if (propertyValue.startsWith("<")) {
 688  0
                         criteria.lt(propertyName, ObjectUtils.clean(propertyValue));
 689  
                 }
 690  0
         }
 691  
 
 692  
         /**
 693  
          * @param dateTimeService
 694  
          *            the dateTimeService to set
 695  
          */
 696  
         public void setDateTimeService(DateTimeService dateTimeService) {
 697  0
                 this.dateTimeService = dateTimeService;
 698  0
         }
 699  
 
 700  
     /**
 701  
      * @return the entityManager
 702  
      */
 703  
     public EntityManager getEntityManager() {
 704  0
         return this.entityManager;
 705  
     }
 706  
 
 707  
     /**
 708  
      * @param entityManager the entityManager to set
 709  
      */
 710  
     public void setEntityManager(EntityManager entityManager) {
 711  0
         this.entityManager = entityManager;
 712  0
     }
 713  
     
 714  
         public void setPersistenceStructureService(PersistenceStructureService persistenceStructureService) {
 715  0
                 this.persistenceStructureService = persistenceStructureService;
 716  0
         }
 717  
 
 718  
         public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
 719  0
         this.dataDictionaryService = dataDictionaryService;
 720  0
     }
 721  
         
 722  
 }