001 /** 002 * Copyright 2005-2013 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.kuali.rice.krad.dao.impl; 017 018 import org.apache.commons.beanutils.PropertyUtils; 019 import org.apache.commons.lang.StringUtils; 020 import org.apache.ojb.broker.query.Criteria; 021 import org.apache.ojb.broker.query.Query; 022 import org.apache.ojb.broker.query.QueryByCriteria; 023 import org.apache.ojb.broker.query.QueryFactory; 024 import org.kuali.rice.core.api.datetime.DateTimeService; 025 import org.kuali.rice.core.api.search.SearchOperator; 026 import org.kuali.rice.core.api.util.RiceKeyConstants; 027 import org.kuali.rice.core.api.util.type.TypeUtils; 028 import org.kuali.rice.core.framework.persistence.ojb.conversion.OjbCharBooleanConversion; 029 import org.kuali.rice.core.framework.persistence.ojb.dao.PlatformAwareDaoBaseOjb; 030 import org.kuali.rice.core.framework.persistence.platform.DatabasePlatform; 031 import org.kuali.rice.kns.service.KNSServiceLocator; 032 import org.kuali.rice.krad.bo.BusinessObject; 033 import org.kuali.rice.krad.bo.InactivatableFromTo; 034 import org.kuali.rice.krad.dao.LookupDao; 035 import org.kuali.rice.krad.lookup.CollectionIncomplete; 036 import org.kuali.rice.krad.lookup.LookupUtils; 037 import org.kuali.rice.krad.service.DataDictionaryService; 038 import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 039 import org.kuali.rice.krad.service.PersistenceStructureService; 040 import org.kuali.rice.krad.util.GlobalVariables; 041 import org.kuali.rice.krad.util.KRADConstants; 042 import org.kuali.rice.krad.util.KRADPropertyConstants; 043 import org.kuali.rice.krad.util.ObjectUtils; 044 import org.springframework.dao.DataIntegrityViolationException; 045 import org.springmodules.orm.ojb.OjbOperationException; 046 047 import java.math.BigDecimal; 048 import java.sql.Timestamp; 049 import java.text.ParseException; 050 import java.util.ArrayList; 051 import java.util.Collection; 052 import java.util.Iterator; 053 import java.util.List; 054 import java.util.Map; 055 056 /** 057 * OJB implementation of the LookupDao interface 058 * 059 * @deprecated use new KRAD Data framework {@link org.kuali.rice.krad.data.DataObjectService} 060 */ 061 @Deprecated 062 public class LookupDaoOjb extends PlatformAwareDaoBaseOjb implements LookupDao { 063 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(LookupDaoOjb.class); 064 private DateTimeService dateTimeService; 065 private PersistenceStructureService persistenceStructureService; 066 private DataDictionaryService dataDictionaryService; 067 068 /** 069 * Since 2.3 070 * This version of findCollectionBySearchHelper is needed for version compatibility. It allows executeSearch 071 * to behave the same way as it did prior to 2.3. The value for searchResultsLimit will be retrieved from the 072 * KNS version of LookupUtils. 073 * 074 * @see org.kuali.rice.krad.dao.LookupDao#findCollectionBySearchHelper(java.lang.Class, java.util.Map, boolean, 075 * boolean) 076 */ 077 public Collection findCollectionBySearchHelper(Class businessObjectClass, Map formProps, boolean unbounded, 078 boolean usePrimaryKeyValuesOnly) { 079 Integer searchResultsLimit = org.kuali.rice.kns.lookup.LookupUtils.getSearchResultsLimit(businessObjectClass); 080 return findCollectionBySearchHelper(businessObjectClass, formProps, unbounded, usePrimaryKeyValuesOnly, 081 searchResultsLimit); 082 } 083 084 /** 085 * @see org.kuali.rice.krad.dao.LookupDao#findCollectionBySearchHelper(java.lang.Class, java.util.Map, boolean, 086 * boolean, Integer) 087 * 088 * If searchResultsLimit is null, the search results will not be limited by any other means. 089 */ 090 public Collection findCollectionBySearchHelper(Class businessObjectClass, Map formProps, boolean unbounded, 091 boolean usePrimaryKeyValuesOnly, Integer searchResultsLimit) { 092 BusinessObject businessObject = checkBusinessObjectClass(businessObjectClass); 093 if (usePrimaryKeyValuesOnly) { 094 return executeSearch(businessObjectClass, getCollectionCriteriaFromMapUsingPrimaryKeysOnly( 095 businessObjectClass, formProps), unbounded, searchResultsLimit); 096 } 097 098 Criteria crit = getCollectionCriteriaFromMap(businessObject, formProps); 099 return executeSearch(businessObjectClass, crit, unbounded, searchResultsLimit); 100 } 101 102 /** 103 * Builds up criteria object based on the object and map. 104 */ 105 public Criteria getCollectionCriteriaFromMap(BusinessObject example, Map formProps) { 106 Criteria criteria = new Criteria(); 107 Iterator propsIter = formProps.keySet().iterator(); 108 while (propsIter.hasNext()) { 109 String propertyName = (String) propsIter.next(); 110 Boolean caseInsensitive = Boolean.TRUE; 111 if ( KRADServiceLocatorWeb.getDataDictionaryService().isAttributeDefined( example.getClass(), propertyName )) { 112 // 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 113 // 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 114 // could be mixed case. Thus, caseInsensitive will be the opposite of forceUppercase. 115 caseInsensitive = !KRADServiceLocatorWeb.getDataDictionaryService().getAttributeForceUppercase( example.getClass(), propertyName ); 116 } 117 if ( caseInsensitive == null ) { caseInsensitive = Boolean.TRUE; } 118 boolean treatWildcardsAndOperatorsAsLiteral = KNSServiceLocator 119 .getBusinessObjectDictionaryService().isLookupFieldTreatWildcardsAndOperatorsAsLiteral(example.getClass(), propertyName); 120 121 if (formProps.get(propertyName) instanceof Collection) { 122 Iterator iter = ((Collection) formProps.get(propertyName)).iterator(); 123 while (iter.hasNext()) { 124 String searchValue = (String) iter.next(); 125 if (!caseInsensitive) { 126 // Verify that the searchValue is uppercased if caseInsensitive is false 127 searchValue = searchValue.toUpperCase(); 128 } 129 if (!createCriteria(example, searchValue, propertyName, caseInsensitive, treatWildcardsAndOperatorsAsLiteral, criteria, formProps )) { 130 throw new RuntimeException("Invalid value in Collection"); 131 } 132 } 133 } 134 else { 135 String searchValue = (String) formProps.get(propertyName); 136 if (!caseInsensitive) { 137 // Verify that the searchValue is uppercased if caseInsensitive is false 138 searchValue = searchValue.toUpperCase(); 139 } 140 if (!createCriteria(example, searchValue, propertyName, caseInsensitive, treatWildcardsAndOperatorsAsLiteral, criteria, formProps)) { 141 continue; 142 } 143 } 144 } 145 return criteria; 146 } 147 148 public Criteria getCollectionCriteriaFromMapUsingPrimaryKeysOnly(Class businessObjectClass, Map formProps) { 149 BusinessObject businessObject = checkBusinessObjectClass(businessObjectClass); 150 Criteria criteria = new Criteria(); 151 List pkFields = KRADServiceLocatorWeb.getLegacyDataAdapter().listPrimaryKeyFieldNames(businessObjectClass); 152 Iterator pkIter = pkFields.iterator(); 153 while (pkIter.hasNext()) { 154 String pkFieldName = (String) pkIter.next(); 155 String pkValue = (String) formProps.get(pkFieldName); 156 157 if (StringUtils.isBlank(pkValue)) { 158 throw new RuntimeException("Missing pk value for field " + pkFieldName + " when a search based on PK values only is performed."); 159 } 160 else { 161 for (SearchOperator op : SearchOperator.QUERY_CHARACTERS) { 162 if (pkValue.contains(op.op())) { 163 throw new RuntimeException("Value \"" + pkValue + "\" for PK field " + pkFieldName + " contains wildcard/operator characters."); 164 } 165 } 166 } 167 boolean treatWildcardsAndOperatorsAsLiteral = KNSServiceLocator. 168 getBusinessObjectDictionaryService().isLookupFieldTreatWildcardsAndOperatorsAsLiteral(businessObjectClass, pkFieldName); 169 createCriteria(businessObject, pkValue, pkFieldName, false, treatWildcardsAndOperatorsAsLiteral, criteria); 170 } 171 return criteria; 172 } 173 174 private BusinessObject checkBusinessObjectClass(Class businessObjectClass) { 175 if (businessObjectClass == null) { 176 throw new IllegalArgumentException("BusinessObject class passed to LookupDaoOjb findCollectionBySearchHelper... method was null"); 177 } 178 BusinessObject businessObject = null; 179 try { 180 businessObject = (BusinessObject) businessObjectClass.newInstance(); 181 } 182 catch (IllegalAccessException e) { 183 throw new RuntimeException("LookupDaoOjb could not get instance of " + businessObjectClass.getName(), e); 184 } 185 catch (InstantiationException e) { 186 throw new RuntimeException("LookupDaoOjb could not get instance of " + businessObjectClass.getName(), e); 187 } 188 return businessObject; 189 } 190 191 private Collection executeSearch(Class businessObjectClass, Criteria criteria, boolean unbounded, Integer searchResultsLimit) { 192 Collection searchResults = new ArrayList(); 193 Long matchingResultsCount = null; 194 try { 195 if (!unbounded && searchResultsLimit == null) { 196 searchResultsLimit = org.kuali.rice.kns.lookup.LookupUtils.getSearchResultsLimit(businessObjectClass); 197 } 198 // A negative number in searchResultsLimit means the search results should be unlimited. 199 if (!unbounded && (searchResultsLimit != null) && searchResultsLimit >= 0) { 200 matchingResultsCount = new Long(getPersistenceBrokerTemplate().getCount(QueryFactory.newQuery(businessObjectClass, criteria))); 201 applySearchResultsLimit(businessObjectClass, criteria, getDbPlatform(), searchResultsLimit); 202 } 203 if ((matchingResultsCount == null) || (matchingResultsCount.intValue() <= searchResultsLimit.intValue())) { 204 matchingResultsCount = new Long(0); 205 } 206 searchResults = getPersistenceBrokerTemplate().getCollectionByQuery(QueryFactory.newQuery(businessObjectClass, criteria)); 207 // populate Person objects in business objects 208 List bos = new ArrayList(); 209 bos.addAll(searchResults); 210 searchResults = bos; 211 212 213 } 214 catch (OjbOperationException e) { 215 throw new RuntimeException("LookupDaoOjb encountered exception during executeSearch", e); 216 } 217 catch (DataIntegrityViolationException e) { 218 throw new RuntimeException("LookupDaoOjb encountered exception during executeSearch", e); 219 } 220 return new CollectionIncomplete(searchResults, matchingResultsCount); 221 } 222 223 224 /** 225 * This method applies the search results limit to the search criteria for this BO 226 * 227 * @param businessObjectClass BO class to search on / get limit for 228 * @param criteria search criteria 229 * @param platform database platform 230 * @param limit limit to use. If limit is null, getSearchResultsLimit will be called using the businessObjectClass 231 * to see if a limit can be found for this particular businessObjectClass. 232 */ 233 protected void applySearchResultsLimit(Class businessObjectClass, Criteria criteria, DatabasePlatform platform, 234 Integer limit) { 235 String limitSql = null; 236 if (limit != null) { 237 limitSql = platform.applyLimitSql(limit); 238 } else { 239 limit = LookupUtils.getSearchResultsLimit(businessObjectClass, null); 240 if (limit != null) { 241 limitSql = platform.applyLimitSql(limit); 242 } 243 } 244 if (StringUtils.isNotBlank(limitSql)) { 245 criteria.addSql(limitSql); 246 } 247 } 248 249 public boolean createCriteria(Object example, String searchValue, String propertyName, Object criteria) { 250 return createCriteria( example, searchValue, propertyName, false, false, criteria ); 251 } 252 253 public boolean createCriteria(Object example, String searchValue, String propertyName, boolean caseInsensitive, boolean treatWildcardsAndOperatorsAsLiteral, Object criteria) { 254 return createCriteria( example, searchValue, propertyName, caseInsensitive, treatWildcardsAndOperatorsAsLiteral, criteria, null ); 255 } 256 257 public boolean createCriteria(Object example, String searchValue, String propertyName, boolean caseInsensitive, boolean treatWildcardsAndOperatorsAsLiteral, Object criteria, Map searchValues) { 258 // if searchValue is empty and the key is not a valid property ignore 259 if (!(criteria instanceof Criteria) || StringUtils.isBlank(searchValue) || !ObjectUtils.isWriteable(example, propertyName, persistenceStructureService)) { 260 return false; 261 } 262 263 // get property type which is used to determine type of criteria 264 Class propertyType = ObjectUtils.getPropertyType(example, propertyName, persistenceStructureService); 265 if (propertyType == null) { 266 return false; 267 } 268 269 // build criteria 270 if (example instanceof InactivatableFromTo) { 271 if (KRADPropertyConstants.ACTIVE.equals(propertyName)) { 272 addInactivateableFromToActiveCriteria(example, searchValue, (Criteria) criteria, searchValues); 273 } else if (KRADPropertyConstants.CURRENT.equals(propertyName)) { 274 addInactivateableFromToCurrentCriteria(example, searchValue, (Criteria) criteria, searchValues); 275 } else if (!KRADPropertyConstants.ACTIVE_AS_OF_DATE.equals(propertyName)) { 276 addCriteria(propertyName, searchValue, propertyType, caseInsensitive, 277 treatWildcardsAndOperatorsAsLiteral, (Criteria) criteria); 278 } 279 } else { 280 addCriteria(propertyName, searchValue, propertyType, caseInsensitive, treatWildcardsAndOperatorsAsLiteral, 281 (Criteria) criteria); 282 } 283 284 return true; 285 } 286 287 /** 288 * Find count of records meeting criteria based on the object and map. 289 */ 290 public Long findCountByMap(Object example, Map formProps) { 291 Criteria criteria = new Criteria(); 292 // iterate through the parameter map for key values search criteria 293 Iterator propsIter = formProps.keySet().iterator(); 294 while (propsIter.hasNext()) { 295 String propertyName = (String) propsIter.next(); 296 String searchValue = (String) formProps.get(propertyName); 297 298 // if searchValue is empty and the key is not a valid property ignore 299 if (StringUtils.isBlank(searchValue) || !(PropertyUtils.isWriteable(example, propertyName))) { 300 continue; 301 } 302 303 // get property type which is used to determine type of criteria 304 Class propertyType = ObjectUtils.getPropertyType(example, propertyName, persistenceStructureService); 305 if (propertyType == null) { 306 continue; 307 } 308 Boolean caseInsensitive = Boolean.TRUE; 309 if ( KRADServiceLocatorWeb.getDataDictionaryService().isAttributeDefined( example.getClass(), propertyName )) { 310 caseInsensitive = !KRADServiceLocatorWeb.getDataDictionaryService().getAttributeForceUppercase( example.getClass(), propertyName ); 311 } 312 if ( caseInsensitive == null ) { caseInsensitive = Boolean.TRUE; } 313 314 boolean treatWildcardsAndOperatorsAsLiteral = KNSServiceLocator 315 .getBusinessObjectDictionaryService().isLookupFieldTreatWildcardsAndOperatorsAsLiteral(example.getClass(), propertyName); 316 317 if (!caseInsensitive) { 318 // Verify that the searchValue is uppercased if caseInsensitive is false 319 searchValue = searchValue.toUpperCase(); 320 } 321 322 // build criteria 323 addCriteria(propertyName, searchValue, propertyType, caseInsensitive, treatWildcardsAndOperatorsAsLiteral, criteria); 324 } 325 326 // execute query and return result list 327 Query query = QueryFactory.newQuery(example.getClass(), criteria); 328 329 return new Long(getPersistenceBrokerTemplate().getCount(query)); 330 } 331 332 /** 333 * @see org.kuali.rice.krad.dao.LookupDao#findObjectByMap(java.lang.Object, java.util.Map) 334 */ 335 @Override 336 public <T extends Object> T findObjectByMap(Class<T> type, Map<String, String> formProps) { 337 T example = null; 338 try { 339 example = type.newInstance(); 340 } catch (IllegalAccessException e) { 341 throw new RuntimeException("Cannot get new instance of " + type.getName(), e); 342 } catch (InstantiationException e) { 343 throw new RuntimeException("Cannot instantiate " + type.getName(), e); 344 } 345 346 if ( KRADServiceLocatorWeb.getLegacyDataAdapter().isPersistable(example.getClass())) { 347 Criteria criteria = new Criteria(); 348 349 // iterate through the parameter map for search criteria 350 for (Map.Entry<String, String> formProp : formProps.entrySet()) { 351 352 String propertyName = formProp.getKey(); 353 String searchValue = ""; 354 if (formProp.getValue() != null) { 355 searchValue = formProp.getValue(); 356 } 357 358 if (StringUtils.isNotBlank(searchValue) & PropertyUtils.isWriteable(example, propertyName)) { 359 Class propertyType = ObjectUtils.getPropertyType(example, propertyName, persistenceStructureService); 360 if (TypeUtils.isIntegralClass(propertyType) || TypeUtils.isDecimalClass(propertyType) ) { 361 criteria.addEqualTo(propertyName, cleanNumeric(searchValue)); 362 } else if (TypeUtils.isTemporalClass(propertyType)) { 363 criteria.addEqualTo(propertyName, parseDate( ObjectUtils.clean(searchValue) ) ); 364 } else { 365 criteria.addEqualTo(propertyName, searchValue); 366 } 367 } 368 } 369 370 // execute query and return result list 371 Query query = QueryFactory.newQuery(example.getClass(), criteria); 372 return (T)getPersistenceBrokerTemplate().getObjectByQuery(query); 373 } 374 return null; 375 } 376 377 378 /** 379 * Adds to the criteria object based on the property type and any query characters given. 380 */ 381 private void addCriteria(String propertyName, String propertyValue, Class propertyType, boolean caseInsensitive, boolean treatWildcardsAndOperatorsAsLiteral, Criteria criteria) { 382 if (!treatWildcardsAndOperatorsAsLiteral && StringUtils.contains(propertyValue, SearchOperator.OR.op())) { 383 addOrCriteria(propertyName, propertyValue, propertyType, caseInsensitive, criteria); 384 return; 385 } 386 387 if (!treatWildcardsAndOperatorsAsLiteral && StringUtils.contains(propertyValue, SearchOperator.AND.op())) { 388 addAndCriteria(propertyName, propertyValue, propertyType, caseInsensitive, criteria); 389 return; 390 } 391 392 if (StringUtils.equalsIgnoreCase(propertyValue, SearchOperator.NULL.op()) || StringUtils.equalsIgnoreCase(propertyValue, SearchOperator.NOT_NULL.op())) { 393 // KULRICE-6846 null Lookup criteria causes sql exception 394 if (StringUtils.contains(propertyValue, SearchOperator.NOT.op())) { 395 criteria.addNotNull(propertyName); 396 } 397 else { 398 criteria.addIsNull(propertyName); 399 } 400 } 401 else if (TypeUtils.isStringClass(propertyType)) { 402 // KULRICE-85 : made string searches case insensitive - used new DBPlatform function to force strings to upper case 403 if ( caseInsensitive ) { 404 propertyName = getDbPlatform().getUpperCaseFunction() + "(" + propertyName + ")"; 405 propertyValue = propertyValue.toUpperCase(); 406 } 407 if (!treatWildcardsAndOperatorsAsLiteral && StringUtils.contains(propertyValue, SearchOperator.NOT.op())) { 408 addNotCriteria(propertyName, propertyValue, propertyType, caseInsensitive, criteria); 409 } else if ( 410 !treatWildcardsAndOperatorsAsLiteral && propertyValue != null && ( 411 StringUtils.contains(propertyValue, SearchOperator.BETWEEN.op()) 412 || propertyValue.startsWith(">") 413 || propertyValue.startsWith("<") ) ) { 414 addStringRangeCriteria(propertyName, propertyValue, criteria); 415 } else { 416 if (treatWildcardsAndOperatorsAsLiteral) { 417 propertyValue = StringUtils.replace(propertyValue, "*", "\\*"); 418 } 419 criteria.addLike(propertyName, propertyValue); 420 } 421 } else if (TypeUtils.isIntegralClass(propertyType) || TypeUtils.isDecimalClass(propertyType) ) { 422 addNumericRangeCriteria(propertyName, propertyValue, treatWildcardsAndOperatorsAsLiteral, criteria); 423 } else if (TypeUtils.isTemporalClass(propertyType)) { 424 addDateRangeCriteria(propertyName, propertyValue, treatWildcardsAndOperatorsAsLiteral, criteria); 425 } else if (TypeUtils.isBooleanClass(propertyType)) { 426 criteria.addEqualTo(propertyName, ObjectUtils.clean(propertyValue)); 427 } else { 428 LOG.error("not adding criterion for: " + propertyName + "," + propertyType + "," + propertyValue); 429 } 430 } 431 432 /** 433 * Translates criteria for active status to criteria on the active from and to fields 434 * 435 * @param example - business object being queried on 436 * @param activeSearchValue - value for the active search field, should convert to boolean 437 * @param criteria - Criteria object being built 438 * @param searchValues - Map containing all search keys and values 439 */ 440 protected void addInactivateableFromToActiveCriteria(Object example, String activeSearchValue, Criteria criteria, Map searchValues) { 441 Timestamp activeTimestamp = LookupUtils.getActiveDateTimestampForCriteria(searchValues); 442 443 String activeBooleanStr = (String) (new OjbCharBooleanConversion()).javaToSql(activeSearchValue); 444 if (OjbCharBooleanConversion.DATABASE_BOOLEAN_TRUE_STRING_REPRESENTATION.equals(activeBooleanStr)) { 445 // (active from date <= date or active from date is null) and (date < active to date or active to date is null) 446 Criteria criteriaBeginDate = new Criteria(); 447 criteriaBeginDate.addLessOrEqualThan(KRADPropertyConstants.ACTIVE_FROM_DATE, activeTimestamp); 448 449 Criteria criteriaBeginDateNull = new Criteria(); 450 criteriaBeginDateNull.addIsNull(KRADPropertyConstants.ACTIVE_FROM_DATE); 451 criteriaBeginDate.addOrCriteria(criteriaBeginDateNull); 452 453 criteria.addAndCriteria(criteriaBeginDate); 454 455 Criteria criteriaEndDate = new Criteria(); 456 criteriaEndDate.addGreaterThan(KRADPropertyConstants.ACTIVE_TO_DATE, activeTimestamp); 457 458 Criteria criteriaEndDateNull = new Criteria(); 459 criteriaEndDateNull.addIsNull(KRADPropertyConstants.ACTIVE_TO_DATE); 460 criteriaEndDate.addOrCriteria(criteriaEndDateNull); 461 462 criteria.addAndCriteria(criteriaEndDate); 463 } 464 else if (OjbCharBooleanConversion.DATABASE_BOOLEAN_FALSE_STRING_REPRESENTATION.equals(activeBooleanStr)) { 465 // (date < active from date) or (active from date is null) or (date >= active to date) 466 Criteria criteriaNonActive = new Criteria(); 467 criteriaNonActive.addGreaterThan(KRADPropertyConstants.ACTIVE_FROM_DATE, activeTimestamp); 468 469 Criteria criteriaEndDate = new Criteria(); 470 criteriaEndDate.addLessOrEqualThan(KRADPropertyConstants.ACTIVE_TO_DATE, activeTimestamp); 471 criteriaNonActive.addOrCriteria(criteriaEndDate); 472 473 criteria.addAndCriteria(criteriaNonActive); 474 } 475 } 476 477 /** 478 * Translates criteria for current status to criteria on the active from field 479 * 480 * @param example - business object being queried on 481 * @param currentSearchValue - value for the current search field, should convert to boolean 482 * @param criteria - Criteria object being built 483 */ 484 protected void addInactivateableFromToCurrentCriteria(Object example, String currentSearchValue, Criteria criteria, Map searchValues) { 485 Criteria maxBeginDateCriteria = new Criteria(); 486 487 Timestamp activeTimestamp = LookupUtils.getActiveDateTimestampForCriteria(searchValues); 488 489 maxBeginDateCriteria.addLessOrEqualThan(KRADPropertyConstants.ACTIVE_FROM_DATE, activeTimestamp); 490 491 List<String> groupByFieldList = dataDictionaryService.getGroupByAttributesForEffectiveDating(example 492 .getClass()); 493 if (groupByFieldList == null) { 494 return; 495 } 496 497 // join back to main query with the group by fields 498 String[] groupBy = new String[groupByFieldList.size()]; 499 for (int i = 0; i < groupByFieldList.size(); i++) { 500 String groupByField = groupByFieldList.get(i); 501 groupBy[i] = groupByField; 502 503 maxBeginDateCriteria.addEqualToField(groupByField, Criteria.PARENT_QUERY_PREFIX + groupByField); 504 } 505 506 String[] columns = new String[1]; 507 columns[0] = "max(" + KRADPropertyConstants.ACTIVE_FROM_DATE + ")"; 508 509 QueryByCriteria query = QueryFactory.newReportQuery(example.getClass(), columns, maxBeginDateCriteria, true); 510 query.addGroupBy(groupBy); 511 512 String currentBooleanStr = (String) (new OjbCharBooleanConversion()).javaToSql(currentSearchValue); 513 if (OjbCharBooleanConversion.DATABASE_BOOLEAN_TRUE_STRING_REPRESENTATION.equals(currentBooleanStr)) { 514 criteria.addIn(KRADPropertyConstants.ACTIVE_FROM_DATE, query); 515 } else if (OjbCharBooleanConversion.DATABASE_BOOLEAN_FALSE_STRING_REPRESENTATION.equals(currentBooleanStr)) { 516 criteria.addNotIn(KRADPropertyConstants.ACTIVE_FROM_DATE, query); 517 } 518 } 519 520 /** 521 * @param propertyName 522 * @param propertyValue 523 * @param propertyType 524 * @param criteria 525 */ 526 private void addOrCriteria(String propertyName, String propertyValue, Class propertyType, boolean caseInsensitive, Criteria criteria) { 527 addLogicalOperatorCriteria(propertyName, propertyValue, propertyType, caseInsensitive, criteria, SearchOperator.OR.op()); 528 } 529 530 /** 531 * @param propertyName 532 * @param propertyValue 533 * @param propertyType 534 * @param criteria 535 */ 536 private void addAndCriteria(String propertyName, String propertyValue, Class propertyType, boolean caseInsensitive, Criteria criteria) { 537 addLogicalOperatorCriteria(propertyName, propertyValue, propertyType, caseInsensitive, criteria, SearchOperator.AND.op()); 538 } 539 540 private void addNotCriteria(String propertyName, String propertyValue, Class propertyType, boolean caseInsensitive, Criteria criteria) { 541 542 String[] splitPropVal = StringUtils.split(propertyValue, SearchOperator.NOT.op()); 543 544 int strLength = splitPropVal.length; 545 // if more than one NOT operator assume an implicit and (i.e. !a!b = !a&!b) 546 if (strLength > 1) { 547 String expandedNot = SearchOperator.NOT + StringUtils.join(splitPropVal, SearchOperator.AND.op() + SearchOperator.NOT.op()); 548 // we know that since this method was called, treatWildcardsAndOperatorsAsLiteral must be false 549 addCriteria(propertyName, expandedNot, propertyType, caseInsensitive, false, criteria); 550 } 551 else { 552 // only one so add a not like 553 criteria.addNotLike(propertyName, splitPropVal[0]); 554 } 555 } 556 557 /** 558 * Builds a sub criteria object joined with an 'AND' or 'OR' (depending on splitValue) using the split values of propertyValue. Then joins back the 559 * sub criteria to the main criteria using an 'AND'. 560 */ 561 private void addLogicalOperatorCriteria(String propertyName, String propertyValue, Class propertyType, boolean caseInsensitive, Criteria criteria, String splitValue) { 562 String[] splitPropVal = StringUtils.split(propertyValue, splitValue); 563 564 Criteria subCriteria = new Criteria(); 565 for (int i = 0; i < splitPropVal.length; i++) { 566 Criteria predicate = new Criteria(); 567 568 addCriteria(propertyName, splitPropVal[i], propertyType, caseInsensitive, false, predicate); 569 if (splitValue.equals(SearchOperator.OR.op())) { 570 subCriteria.addOrCriteria(predicate); 571 } 572 if (splitValue.equals(SearchOperator.AND.op())) { 573 subCriteria.addAndCriteria(predicate); 574 } 575 } 576 577 criteria.addAndCriteria(subCriteria); 578 } 579 580 private java.sql.Date parseDate(String dateString) { 581 dateString = dateString.trim(); 582 try { 583 return dateTimeService.convertToSqlDate(dateString); 584 } catch (ParseException ex) { 585 return null; 586 } 587 } 588 589 /** 590 * Adds to the criteria object based on query characters given 591 */ 592 private void addDateRangeCriteria(String propertyName, String propertyValue, boolean treatWildcardsAndOperatorsAsLiteral, Criteria criteria) { 593 594 if (StringUtils.contains(propertyValue, SearchOperator.BETWEEN.op())) { 595 if (treatWildcardsAndOperatorsAsLiteral) 596 throw new RuntimeException("Wildcards and operators are not allowed on this date field: " + propertyName); 597 String[] rangeValues = StringUtils.split(propertyValue, SearchOperator.BETWEEN.op()); 598 criteria.addBetween(propertyName, parseDate( ObjectUtils.clean(rangeValues[0] ) ), parseDate( ObjectUtils.clean(rangeValues[1] ) ) ); 599 } else if (propertyValue.startsWith(SearchOperator.GREATER_THAN_EQUAL.op())) { 600 if (treatWildcardsAndOperatorsAsLiteral) 601 throw new RuntimeException("Wildcards and operators are not allowed on this date field: " + propertyName); 602 criteria.addGreaterOrEqualThan(propertyName, parseDate( ObjectUtils.clean(propertyValue) ) ); 603 } else if (propertyValue.startsWith(SearchOperator.LESS_THAN_EQUAL.op())) { 604 if (treatWildcardsAndOperatorsAsLiteral) 605 throw new RuntimeException("Wildcards and operators are not allowed on this date field: " + propertyName); 606 criteria.addLessOrEqualThan(propertyName, parseDate( ObjectUtils.clean(propertyValue) ) ); 607 } else if (propertyValue.startsWith(SearchOperator.GREATER_THAN.op())) { 608 if (treatWildcardsAndOperatorsAsLiteral) 609 throw new RuntimeException("Wildcards and operators are not allowed on this date field: " + propertyName); 610 criteria.addGreaterThan(propertyName, parseDate( ObjectUtils.clean(propertyValue) ) ); 611 } else if (propertyValue.startsWith(SearchOperator.LESS_THAN.op())) { 612 if (treatWildcardsAndOperatorsAsLiteral) 613 throw new RuntimeException("Wildcards and operators are not allowed on this date field: " + propertyName); 614 criteria.addLessThan(propertyName, parseDate( ObjectUtils.clean(propertyValue) ) ); 615 } else { 616 criteria.addEqualTo(propertyName, parseDate( ObjectUtils.clean(propertyValue) ) ); 617 } 618 } 619 620 private BigDecimal cleanNumeric( String value ) { 621 String cleanedValue = value.replaceAll( "[^-0-9.]", "" ); 622 // ensure only one "minus" at the beginning, if any 623 if ( cleanedValue.lastIndexOf( '-' ) > 0 ) { 624 if ( cleanedValue.charAt( 0 ) == '-' ) { 625 cleanedValue = "-" + cleanedValue.replaceAll( "-", "" ); 626 } else { 627 cleanedValue = cleanedValue.replaceAll( "-", "" ); 628 } 629 } 630 // ensure only one decimal in the string 631 int decimalLoc = cleanedValue.lastIndexOf( '.' ); 632 if ( cleanedValue.indexOf( '.' ) != decimalLoc ) { 633 cleanedValue = cleanedValue.substring( 0, decimalLoc ).replaceAll( "\\.", "" ) + cleanedValue.substring( decimalLoc ); 634 } 635 try { 636 return new BigDecimal( cleanedValue ); 637 } catch ( NumberFormatException ex ) { 638 GlobalVariables.getMessageMap().putError(KRADConstants.DOCUMENT_ERRORS, RiceKeyConstants.ERROR_CUSTOM, new String[] { "Invalid Numeric Input: " + value }); 639 return null; 640 } 641 } 642 643 /** 644 * Adds to the criteria object based on query characters given 645 */ 646 private void addNumericRangeCriteria(String propertyName, String propertyValue, boolean treatWildcardsAndOperatorsAsLiteral, Criteria criteria) { 647 648 if (StringUtils.contains(propertyValue, SearchOperator.BETWEEN.op())) { 649 if (treatWildcardsAndOperatorsAsLiteral) 650 throw new RuntimeException("Cannot use wildcards and operators on numeric field " + propertyName); 651 String[] rangeValues = StringUtils.split(propertyValue, SearchOperator.BETWEEN.op()); 652 criteria.addBetween(propertyName, cleanNumeric( rangeValues[0] ), cleanNumeric( rangeValues[1] )); 653 } else if (propertyValue.startsWith(SearchOperator.GREATER_THAN_EQUAL.op())) { 654 if (treatWildcardsAndOperatorsAsLiteral) 655 throw new RuntimeException("Cannot use wildcards and operators on numeric field " + propertyName); 656 criteria.addGreaterOrEqualThan(propertyName, cleanNumeric(propertyValue)); 657 } else if (propertyValue.startsWith(SearchOperator.LESS_THAN_EQUAL.op())) { 658 if (treatWildcardsAndOperatorsAsLiteral) 659 throw new RuntimeException("Cannot use wildcards and operators on numeric field " + propertyName); 660 criteria.addLessOrEqualThan(propertyName, cleanNumeric(propertyValue)); 661 } else if (propertyValue.startsWith(SearchOperator.GREATER_THAN.op())) { 662 if (treatWildcardsAndOperatorsAsLiteral) 663 throw new RuntimeException("Cannot use wildcards and operators on numeric field " + propertyName); 664 criteria.addGreaterThan(propertyName, cleanNumeric( propertyValue ) ); 665 } else if (propertyValue.startsWith(SearchOperator.LESS_THAN.op())) { 666 if (treatWildcardsAndOperatorsAsLiteral) 667 throw new RuntimeException("Cannot use wildcards and operators on numeric field " + propertyName); 668 criteria.addLessThan(propertyName, cleanNumeric(propertyValue)); 669 } else { 670 criteria.addEqualTo(propertyName, cleanNumeric(propertyValue)); 671 } 672 } 673 674 /** 675 * Adds to the criteria object based on query characters given 676 */ 677 private void addStringRangeCriteria(String propertyName, String propertyValue, Criteria criteria) { 678 679 if (StringUtils.contains(propertyValue, SearchOperator.BETWEEN.op())) { 680 String[] rangeValues = StringUtils.split(propertyValue, SearchOperator.BETWEEN.op()); 681 criteria.addBetween(propertyName, rangeValues[0], rangeValues[1]); 682 } else if (propertyValue.startsWith(SearchOperator.GREATER_THAN_EQUAL.op())) { 683 criteria.addGreaterOrEqualThan(propertyName, ObjectUtils.clean(propertyValue)); 684 } else if (propertyValue.startsWith(SearchOperator.LESS_THAN_EQUAL.op())) { 685 criteria.addLessOrEqualThan(propertyName, ObjectUtils.clean(propertyValue)); 686 } else if (propertyValue.startsWith(SearchOperator.GREATER_THAN.op())) { 687 criteria.addGreaterThan(propertyName, ObjectUtils.clean(propertyValue)); 688 } else if (propertyValue.startsWith(SearchOperator.LESS_THAN.op())) { 689 criteria.addLessThan(propertyName, ObjectUtils.clean(propertyValue)); 690 } else { 691 criteria.addEqualTo(propertyName, ObjectUtils.clean(propertyValue)); 692 } 693 } 694 695 public void setDateTimeService(DateTimeService dateTimeService) { 696 this.dateTimeService = dateTimeService; 697 } 698 699 public void setPersistenceStructureService(PersistenceStructureService persistenceStructureService) { 700 this.persistenceStructureService = persistenceStructureService; 701 } 702 703 public void setDataDictionaryService(DataDictionaryService dataDictionaryService) { 704 this.dataDictionaryService = dataDictionaryService; 705 } 706 }