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 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, false, false, 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 if (StringUtils.contains(propertyValue, SearchOperator.NOT.op())) { 329 criteria.addColumnNotNull(propertyName); 330 } 331 else { 332 criteria.addColumnIsNull(propertyName); 333 } 334 } 335 else if (TypeUtils.isStringClass(propertyType)) { 336 // KULRICE-85 : made string searches case insensitive - used new DBPlatform function to force strings to upper case 337 if ( caseInsensitive ) { 338 propertyName = getDbPlatform().getUpperCaseFunction() + "(" + propertyName + ")"; 339 propertyValue = propertyValue.toUpperCase(); 340 } 341 if (!treatWildcardsAndOperatorsAsLiteral && StringUtils.contains(propertyValue, SearchOperator.NOT.op())) { 342 addNotCriteria(propertyName, propertyValue, propertyType, caseInsensitive, criteria); 343 } else if ( 344 !treatWildcardsAndOperatorsAsLiteral && propertyValue != null && ( 345 StringUtils.contains(propertyValue, SearchOperator.BETWEEN.op()) 346 || propertyValue.startsWith(">") 347 || propertyValue.startsWith("<") ) ) { 348 addStringRangeCriteria(propertyName, propertyValue, criteria); 349 } else { 350 if (treatWildcardsAndOperatorsAsLiteral) { 351 propertyValue = StringUtils.replace(propertyValue, "*", "\\*"); 352 } 353 criteria.addLike(propertyName, propertyValue); 354 } 355 } else if (TypeUtils.isIntegralClass(propertyType) || TypeUtils.isDecimalClass(propertyType) ) { 356 addNumericRangeCriteria(propertyName, propertyValue, treatWildcardsAndOperatorsAsLiteral, criteria); 357 } else if (TypeUtils.isTemporalClass(propertyType)) { 358 addDateRangeCriteria(propertyName, propertyValue, treatWildcardsAndOperatorsAsLiteral, criteria); 359 } else if (TypeUtils.isBooleanClass(propertyType)) { 360 criteria.addEqualTo(propertyName, ObjectUtils.clean(propertyValue)); 361 } else { 362 LOG.error("not adding criterion for: " + propertyName + "," + propertyType + "," + propertyValue); 363 } 364 } 365 366 /** 367 * Translates criteria for active status to criteria on the active from and to fields 368 * 369 * @param example - business object being queried on 370 * @param activeSearchValue - value for the active search field, should convert to boolean 371 * @param criteria - Criteria object being built 372 * @param searchValues - Map containing all search keys and values 373 */ 374 protected void addInactivateableFromToActiveCriteria(Object example, String activeSearchValue, Criteria criteria, Map searchValues) { 375 Timestamp activeTimestamp = LookupUtils.getActiveDateTimestampForCriteria(searchValues); 376 377 String activeBooleanStr = (String) (new OjbCharBooleanConversion()).javaToSql(activeSearchValue); 378 if (OjbCharBooleanConversion.DATABASE_BOOLEAN_TRUE_STRING_REPRESENTATION.equals(activeBooleanStr)) { 379 // (active from date <= date or active from date is null) and (date < active to date or active to date is null) 380 Criteria criteriaBeginDate = new Criteria(); 381 criteriaBeginDate.addLessOrEqualThan(KRADPropertyConstants.ACTIVE_FROM_DATE, activeTimestamp); 382 383 Criteria criteriaBeginDateNull = new Criteria(); 384 criteriaBeginDateNull.addIsNull(KRADPropertyConstants.ACTIVE_FROM_DATE); 385 criteriaBeginDate.addOrCriteria(criteriaBeginDateNull); 386 387 criteria.addAndCriteria(criteriaBeginDate); 388 389 Criteria criteriaEndDate = new Criteria(); 390 criteriaEndDate.addGreaterThan(KRADPropertyConstants.ACTIVE_TO_DATE, activeTimestamp); 391 392 Criteria criteriaEndDateNull = new Criteria(); 393 criteriaEndDateNull.addIsNull(KRADPropertyConstants.ACTIVE_TO_DATE); 394 criteriaEndDate.addOrCriteria(criteriaEndDateNull); 395 396 criteria.addAndCriteria(criteriaEndDate); 397 } 398 else if (OjbCharBooleanConversion.DATABASE_BOOLEAN_FALSE_STRING_REPRESENTATION.equals(activeBooleanStr)) { 399 // (date < active from date) or (active from date is null) or (date >= active to date) 400 Criteria criteriaNonActive = new Criteria(); 401 criteriaNonActive.addGreaterThan(KRADPropertyConstants.ACTIVE_FROM_DATE, activeTimestamp); 402 403 Criteria criteriaEndDate = new Criteria(); 404 criteriaEndDate.addLessOrEqualThan(KRADPropertyConstants.ACTIVE_TO_DATE, activeTimestamp); 405 criteriaNonActive.addOrCriteria(criteriaEndDate); 406 407 criteria.addAndCriteria(criteriaNonActive); 408 } 409 } 410 411 /** 412 * Translates criteria for current status to criteria on the active from field 413 * 414 * @param example - business object being queried on 415 * @param currentSearchValue - value for the current search field, should convert to boolean 416 * @param criteria - Criteria object being built 417 */ 418 protected void addInactivateableFromToCurrentCriteria(Object example, String currentSearchValue, Criteria criteria, Map searchValues) { 419 Criteria maxBeginDateCriteria = new Criteria(); 420 421 Timestamp activeTimestamp = LookupUtils.getActiveDateTimestampForCriteria(searchValues); 422 423 maxBeginDateCriteria.addLessOrEqualThan(KRADPropertyConstants.ACTIVE_FROM_DATE, activeTimestamp); 424 425 List<String> groupByFieldList = dataDictionaryService.getGroupByAttributesForEffectiveDating(example 426 .getClass()); 427 if (groupByFieldList == null) { 428 return; 429 } 430 431 // join back to main query with the group by fields 432 String[] groupBy = new String[groupByFieldList.size()]; 433 for (int i = 0; i < groupByFieldList.size(); i++) { 434 String groupByField = groupByFieldList.get(i); 435 groupBy[i] = groupByField; 436 437 maxBeginDateCriteria.addEqualToField(groupByField, Criteria.PARENT_QUERY_PREFIX + groupByField); 438 } 439 440 String[] columns = new String[1]; 441 columns[0] = "max(" + KRADPropertyConstants.ACTIVE_FROM_DATE + ")"; 442 443 QueryByCriteria query = QueryFactory.newReportQuery(example.getClass(), columns, maxBeginDateCriteria, true); 444 query.addGroupBy(groupBy); 445 446 String currentBooleanStr = (String) (new OjbCharBooleanConversion()).javaToSql(currentSearchValue); 447 if (OjbCharBooleanConversion.DATABASE_BOOLEAN_TRUE_STRING_REPRESENTATION.equals(currentBooleanStr)) { 448 criteria.addIn(KRADPropertyConstants.ACTIVE_FROM_DATE, query); 449 } else if (OjbCharBooleanConversion.DATABASE_BOOLEAN_FALSE_STRING_REPRESENTATION.equals(currentBooleanStr)) { 450 criteria.addNotIn(KRADPropertyConstants.ACTIVE_FROM_DATE, query); 451 } 452 } 453 454 /** 455 * @param propertyName 456 * @param propertyValue 457 * @param propertyType 458 * @param criteria 459 */ 460 private void addOrCriteria(String propertyName, String propertyValue, Class propertyType, boolean caseInsensitive, Criteria criteria) { 461 addLogicalOperatorCriteria(propertyName, propertyValue, propertyType, caseInsensitive, criteria, SearchOperator.OR.op()); 462 } 463 464 /** 465 * @param propertyName 466 * @param propertyValue 467 * @param propertyType 468 * @param criteria 469 */ 470 private void addAndCriteria(String propertyName, String propertyValue, Class propertyType, boolean caseInsensitive, Criteria criteria) { 471 addLogicalOperatorCriteria(propertyName, propertyValue, propertyType, caseInsensitive, criteria, SearchOperator.AND.op()); 472 } 473 474 private void addNotCriteria(String propertyName, String propertyValue, Class propertyType, boolean caseInsensitive, Criteria criteria) { 475 476 String[] splitPropVal = StringUtils.split(propertyValue, SearchOperator.NOT.op()); 477 478 int strLength = splitPropVal.length; 479 // if more than one NOT operator assume an implicit and (i.e. !a!b = !a&!b) 480 if (strLength > 1) { 481 String expandedNot = SearchOperator.NOT + StringUtils.join(splitPropVal, SearchOperator.AND.op() + SearchOperator.NOT.op()); 482 // we know that since this method was called, treatWildcardsAndOperatorsAsLiteral must be false 483 addCriteria(propertyName, expandedNot, propertyType, caseInsensitive, false, criteria); 484 } 485 else { 486 // only one so add a not like 487 criteria.addNotLike(propertyName, splitPropVal[0]); 488 } 489 } 490 491 /** 492 * Builds a sub criteria object joined with an 'AND' or 'OR' (depending on splitValue) using the split values of propertyValue. Then joins back the 493 * sub criteria to the main criteria using an 'AND'. 494 */ 495 private void addLogicalOperatorCriteria(String propertyName, String propertyValue, Class propertyType, boolean caseInsensitive, Criteria criteria, String splitValue) { 496 String[] splitPropVal = StringUtils.split(propertyValue, splitValue); 497 498 Criteria subCriteria = new Criteria(); 499 for (int i = 0; i < splitPropVal.length; i++) { 500 Criteria predicate = new Criteria(); 501 502 addCriteria(propertyName, splitPropVal[i], propertyType, caseInsensitive, false, predicate); 503 if (splitValue.equals(SearchOperator.OR.op())) { 504 subCriteria.addOrCriteria(predicate); 505 } 506 if (splitValue.equals(SearchOperator.AND.op())) { 507 subCriteria.addAndCriteria(predicate); 508 } 509 } 510 511 criteria.addAndCriteria(subCriteria); 512 } 513 514 private java.sql.Date parseDate(String dateString) { 515 dateString = dateString.trim(); 516 try { 517 return dateTimeService.convertToSqlDate(dateString); 518 } catch (ParseException ex) { 519 return null; 520 } 521 } 522 523 /** 524 * Adds to the criteria object based on query characters given 525 */ 526 private void addDateRangeCriteria(String propertyName, String propertyValue, boolean treatWildcardsAndOperatorsAsLiteral, Criteria criteria) { 527 528 if (StringUtils.contains(propertyValue, SearchOperator.BETWEEN.op())) { 529 if (treatWildcardsAndOperatorsAsLiteral) 530 throw new RuntimeException("Wildcards and operators are not allowed on this date field: " + propertyName); 531 String[] rangeValues = StringUtils.split(propertyValue, SearchOperator.BETWEEN.op()); 532 criteria.addBetween(propertyName, parseDate( ObjectUtils.clean(rangeValues[0] ) ), parseDate( ObjectUtils.clean(rangeValues[1] ) ) ); 533 } else if (propertyValue.startsWith(SearchOperator.GREATER_THAN_EQUAL.op())) { 534 if (treatWildcardsAndOperatorsAsLiteral) 535 throw new RuntimeException("Wildcards and operators are not allowed on this date field: " + propertyName); 536 criteria.addGreaterOrEqualThan(propertyName, parseDate( ObjectUtils.clean(propertyValue) ) ); 537 } else if (propertyValue.startsWith(SearchOperator.LESS_THAN_EQUAL.op())) { 538 if (treatWildcardsAndOperatorsAsLiteral) 539 throw new RuntimeException("Wildcards and operators are not allowed on this date field: " + propertyName); 540 criteria.addLessOrEqualThan(propertyName, parseDate( ObjectUtils.clean(propertyValue) ) ); 541 } else if (propertyValue.startsWith(SearchOperator.GREATER_THAN.op())) { 542 if (treatWildcardsAndOperatorsAsLiteral) 543 throw new RuntimeException("Wildcards and operators are not allowed on this date field: " + propertyName); 544 criteria.addGreaterThan(propertyName, parseDate( ObjectUtils.clean(propertyValue) ) ); 545 } else if (propertyValue.startsWith(SearchOperator.LESS_THAN.op())) { 546 if (treatWildcardsAndOperatorsAsLiteral) 547 throw new RuntimeException("Wildcards and operators are not allowed on this date field: " + propertyName); 548 criteria.addLessThan(propertyName, parseDate( ObjectUtils.clean(propertyValue) ) ); 549 } else { 550 criteria.addEqualTo(propertyName, parseDate( ObjectUtils.clean(propertyValue) ) ); 551 } 552 } 553 554 private BigDecimal cleanNumeric( String value ) { 555 String cleanedValue = value.replaceAll( "[^-0-9.]", "" ); 556 // ensure only one "minus" at the beginning, if any 557 if ( cleanedValue.lastIndexOf( '-' ) > 0 ) { 558 if ( cleanedValue.charAt( 0 ) == '-' ) { 559 cleanedValue = "-" + cleanedValue.replaceAll( "-", "" ); 560 } else { 561 cleanedValue = cleanedValue.replaceAll( "-", "" ); 562 } 563 } 564 // ensure only one decimal in the string 565 int decimalLoc = cleanedValue.lastIndexOf( '.' ); 566 if ( cleanedValue.indexOf( '.' ) != decimalLoc ) { 567 cleanedValue = cleanedValue.substring( 0, decimalLoc ).replaceAll( "\\.", "" ) + cleanedValue.substring( decimalLoc ); 568 } 569 try { 570 return new BigDecimal( cleanedValue ); 571 } catch ( NumberFormatException ex ) { 572 GlobalVariables.getMessageMap().putError(KRADConstants.DOCUMENT_ERRORS, RiceKeyConstants.ERROR_CUSTOM, new String[] { "Invalid Numeric Input: " + value }); 573 return null; 574 } 575 } 576 577 /** 578 * Adds to the criteria object based on query characters given 579 */ 580 private void addNumericRangeCriteria(String propertyName, String propertyValue, boolean treatWildcardsAndOperatorsAsLiteral, Criteria criteria) { 581 582 if (StringUtils.contains(propertyValue, SearchOperator.BETWEEN.op())) { 583 if (treatWildcardsAndOperatorsAsLiteral) 584 throw new RuntimeException("Cannot use wildcards and operators on numeric field " + propertyName); 585 String[] rangeValues = StringUtils.split(propertyValue, SearchOperator.BETWEEN.op()); 586 criteria.addBetween(propertyName, cleanNumeric( rangeValues[0] ), cleanNumeric( rangeValues[1] )); 587 } else if (propertyValue.startsWith(SearchOperator.GREATER_THAN_EQUAL.op())) { 588 if (treatWildcardsAndOperatorsAsLiteral) 589 throw new RuntimeException("Cannot use wildcards and operators on numeric field " + propertyName); 590 criteria.addGreaterOrEqualThan(propertyName, cleanNumeric(propertyValue)); 591 } else if (propertyValue.startsWith(SearchOperator.LESS_THAN_EQUAL.op())) { 592 if (treatWildcardsAndOperatorsAsLiteral) 593 throw new RuntimeException("Cannot use wildcards and operators on numeric field " + propertyName); 594 criteria.addLessOrEqualThan(propertyName, cleanNumeric(propertyValue)); 595 } else if (propertyValue.startsWith(SearchOperator.GREATER_THAN.op())) { 596 if (treatWildcardsAndOperatorsAsLiteral) 597 throw new RuntimeException("Cannot use wildcards and operators on numeric field " + propertyName); 598 criteria.addGreaterThan(propertyName, cleanNumeric( propertyValue ) ); 599 } else if (propertyValue.startsWith(SearchOperator.LESS_THAN.op())) { 600 if (treatWildcardsAndOperatorsAsLiteral) 601 throw new RuntimeException("Cannot use wildcards and operators on numeric field " + propertyName); 602 criteria.addLessThan(propertyName, cleanNumeric(propertyValue)); 603 } else { 604 criteria.addEqualTo(propertyName, cleanNumeric(propertyValue)); 605 } 606 } 607 608 /** 609 * Adds to the criteria object based on query characters given 610 */ 611 private void addStringRangeCriteria(String propertyName, String propertyValue, Criteria criteria) { 612 613 if (StringUtils.contains(propertyValue, SearchOperator.BETWEEN.op())) { 614 String[] rangeValues = StringUtils.split(propertyValue, SearchOperator.BETWEEN.op()); 615 criteria.addBetween(propertyName, rangeValues[0], rangeValues[1]); 616 } else if (propertyValue.startsWith(SearchOperator.GREATER_THAN_EQUAL.op())) { 617 criteria.addGreaterOrEqualThan(propertyName, ObjectUtils.clean(propertyValue)); 618 } else if (propertyValue.startsWith(SearchOperator.LESS_THAN_EQUAL.op())) { 619 criteria.addLessOrEqualThan(propertyName, ObjectUtils.clean(propertyValue)); 620 } else if (propertyValue.startsWith(SearchOperator.GREATER_THAN.op())) { 621 criteria.addGreaterThan(propertyName, ObjectUtils.clean(propertyValue)); 622 } else if (propertyValue.startsWith(SearchOperator.LESS_THAN.op())) { 623 criteria.addLessThan(propertyName, ObjectUtils.clean(propertyValue)); 624 } else { 625 criteria.addEqualTo(propertyName, ObjectUtils.clean(propertyValue)); 626 } 627 } 628 629 public void setDateTimeService(DateTimeService dateTimeService) { 630 this.dateTimeService = dateTimeService; 631 } 632 633 public void setPersistenceStructureService(PersistenceStructureService persistenceStructureService) { 634 this.persistenceStructureService = persistenceStructureService; 635 } 636 637 public void setDataDictionaryService(DataDictionaryService dataDictionaryService) { 638 this.dataDictionaryService = dataDictionaryService; 639 } 640 }