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