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