View Javadoc

1   /*
2    * Copyright 2005-2007 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kns.dao.impl;
17  
18  import java.math.BigDecimal;
19  import java.sql.Date;
20  import java.sql.Timestamp;
21  import java.text.ParseException;
22  import java.util.ArrayList;
23  import java.util.Collection;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Map;
27  
28  import org.apache.commons.beanutils.PropertyUtils;
29  import org.apache.commons.lang.StringUtils;
30  import org.apache.ojb.broker.query.Criteria;
31  import org.apache.ojb.broker.query.Query;
32  import org.apache.ojb.broker.query.QueryByCriteria;
33  import org.apache.ojb.broker.query.QueryFactory;
34  import org.kuali.rice.kns.bo.BusinessObject;
35  import org.kuali.rice.kns.bo.InactivateableFromTo;
36  import org.kuali.rice.kns.dao.LookupDao;
37  import org.kuali.rice.kns.lookup.CollectionIncomplete;
38  import org.kuali.rice.kns.lookup.LookupUtils;
39  import org.kuali.rice.kns.service.BusinessObjectDictionaryService;
40  import org.kuali.rice.kns.service.DateTimeService;
41  import org.kuali.rice.kns.service.KNSServiceLocator;
42  import org.kuali.rice.kns.service.PersistenceStructureService;
43  import org.kuali.rice.kns.util.GlobalVariables;
44  import org.kuali.rice.kns.util.KNSConstants;
45  import org.kuali.rice.kns.util.KNSPropertyConstants;
46  import org.kuali.rice.kns.util.ObjectUtils;
47  import org.kuali.rice.kns.util.OjbCharBooleanConversion;
48  import org.kuali.rice.kns.util.RiceKeyConstants;
49  import org.kuali.rice.kns.util.TypeUtils;
50  import org.springframework.dao.DataIntegrityViolationException;
51  import org.springmodules.orm.ojb.OjbOperationException;
52  
53  /**
54   * OJB implementation of the LookupDao interface
55   */
56  public class LookupDaoOjb extends PlatformAwareDaoBaseOjb implements LookupDao {
57      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(LookupDaoOjb.class);
58      private DateTimeService dateTimeService;
59      private PersistenceStructureService persistenceStructureService;
60      private BusinessObjectDictionaryService businessObjectDictionaryService;
61  
62      public Collection findCollectionBySearchHelper(Class businessObjectClass, Map formProps, boolean unbounded, boolean usePrimaryKeyValuesOnly) {
63          return findCollectionBySearchHelper( businessObjectClass, formProps, unbounded, usePrimaryKeyValuesOnly, null );
64      }
65  
66      public Collection findCollectionBySearchHelper(Class businessObjectClass, Map formProps, boolean unbounded, boolean usePrimaryKeyValuesOnly, Object additionalCriteria ) {
67          BusinessObject businessObject = checkBusinessObjectClass(businessObjectClass);
68          if (usePrimaryKeyValuesOnly) {
69          	return executeSearch(businessObjectClass, getCollectionCriteriaFromMapUsingPrimaryKeysOnly(businessObjectClass, formProps), unbounded);
70          }
71          
72  		Criteria crit = getCollectionCriteriaFromMap(businessObject, formProps);
73  		if (additionalCriteria != null && additionalCriteria instanceof Criteria) {
74  			crit.addAndCriteria((Criteria) additionalCriteria);
75  		}
76  
77  		return executeSearch(businessObjectClass, crit, unbounded);
78  	}
79  
80      /**
81       * Builds up criteria object based on the object and map.
82       */
83      public Criteria getCollectionCriteriaFromMap(BusinessObject example, Map formProps) {
84          Criteria criteria = new Criteria();
85          Iterator propsIter = formProps.keySet().iterator();
86          while (propsIter.hasNext()) {
87              String propertyName = (String) propsIter.next();
88              Boolean caseInsensitive = Boolean.TRUE;
89          	if ( KNSServiceLocator.getDataDictionaryService().isAttributeDefined( example.getClass(), propertyName )) {
90          		caseInsensitive = !KNSServiceLocator.getDataDictionaryService().getAttributeForceUppercase( example.getClass(), propertyName );
91          	}
92          	if ( caseInsensitive == null ) { caseInsensitive = Boolean.TRUE; }
93          	boolean treatWildcardsAndOperatorsAsLiteral = KNSServiceLocator
94          			.getBusinessObjectDictionaryService().isLookupFieldTreatWildcardsAndOperatorsAsLiteral(example.getClass(), propertyName);
95          	
96              if (formProps.get(propertyName) instanceof Collection) {
97                  Iterator iter = ((Collection) formProps.get(propertyName)).iterator();
98                  while (iter.hasNext()) {
99                      if (!createCriteria(example, (String) iter.next(), propertyName, caseInsensitive, treatWildcardsAndOperatorsAsLiteral, criteria, formProps )) {
100                         throw new RuntimeException("Invalid value in Collection");
101                     }
102                 }
103             }
104             else {
105                 if (!createCriteria(example, (String) formProps.get(propertyName), propertyName, caseInsensitive, treatWildcardsAndOperatorsAsLiteral, criteria, formProps)) {
106                     continue;
107                 }
108             }
109         }
110         return criteria;
111     }
112     
113     public Criteria getCollectionCriteriaFromMapUsingPrimaryKeysOnly(Class businessObjectClass, Map formProps) {
114         BusinessObject businessObject = checkBusinessObjectClass(businessObjectClass);
115         Criteria criteria = new Criteria();
116         List pkFields = KNSServiceLocator.getBusinessObjectMetaDataService().listPrimaryKeyFieldNames(businessObjectClass);
117         Iterator pkIter = pkFields.iterator();
118         while (pkIter.hasNext()) {
119             String pkFieldName = (String) pkIter.next();
120             String pkValue = (String) formProps.get(pkFieldName);
121 
122             if (StringUtils.isBlank(pkValue)) {
123                 throw new RuntimeException("Missing pk value for field " + pkFieldName + " when a search based on PK values only is performed.");
124             }
125             else if (StringUtils.indexOfAny(pkValue, KNSConstants.QUERY_CHARACTERS) != -1) {
126                 throw new RuntimeException("Value \"" + pkValue + "\" for PK field " + pkFieldName + " contains wildcard/operator characters.");
127             }
128             boolean treatWildcardsAndOperatorsAsLiteral = KNSServiceLocator.
129             		getBusinessObjectDictionaryService().isLookupFieldTreatWildcardsAndOperatorsAsLiteral(businessObjectClass, pkFieldName);
130             createCriteria(businessObject, pkValue, pkFieldName, false, treatWildcardsAndOperatorsAsLiteral, criteria);
131         }
132         return criteria;
133     }
134     
135     private BusinessObject checkBusinessObjectClass(Class businessObjectClass) {
136         if (businessObjectClass == null) {
137             throw new IllegalArgumentException("BusinessObject class passed to LookupDaoOjb findCollectionBySearchHelper... method was null");
138         }
139         BusinessObject businessObject = null;
140         try {
141             businessObject = (BusinessObject) businessObjectClass.newInstance();
142         }
143         catch (IllegalAccessException e) {
144             throw new RuntimeException("LookupDaoOjb could not get instance of " + businessObjectClass.getName(), e);
145         }
146         catch (InstantiationException e) {
147             throw new RuntimeException("LookupDaoOjb could not get instance of " + businessObjectClass.getName(), e);
148         }
149         return businessObject;
150     }
151 
152     private Collection executeSearch(Class businessObjectClass, Criteria criteria, boolean unbounded) {
153     	Collection searchResults = new ArrayList();
154     	Long matchingResultsCount = null;
155     	try {
156     		Integer searchResultsLimit = LookupUtils.getSearchResultsLimit(businessObjectClass);
157     		if (!unbounded && (searchResultsLimit != null)) {
158     			matchingResultsCount = new Long(getPersistenceBrokerTemplate().getCount(QueryFactory.newQuery(businessObjectClass, criteria)));
159     			LookupUtils.applySearchResultsLimit(businessObjectClass, criteria, getDbPlatform());
160     		}
161     		if ((matchingResultsCount == null) || (matchingResultsCount.intValue() <= searchResultsLimit.intValue())) {
162     			matchingResultsCount = new Long(0);
163     		}
164     		searchResults = getPersistenceBrokerTemplate().getCollectionByQuery(QueryFactory.newQuery(businessObjectClass, criteria));
165     		// populate Person objects in business objects
166     		List bos = new ArrayList();
167     		bos.addAll(searchResults);
168     		searchResults = bos;
169     	}
170     	catch (OjbOperationException e) {
171     		throw new RuntimeException("LookupDaoOjb encountered exception during executeSearch", e);
172     	}
173     	catch (DataIntegrityViolationException e) {
174     		throw new RuntimeException("LookupDaoOjb encountered exception during executeSearch", e);
175     	}
176     	return new CollectionIncomplete(searchResults, matchingResultsCount);
177     }
178 
179     public boolean createCriteria(Object example, String searchValue, String propertyName, Object criteria) {
180     	return createCriteria( example, searchValue, propertyName, false, false, criteria );
181     }
182     
183     public boolean createCriteria(Object example, String searchValue, String propertyName, boolean caseInsensitive, boolean treatWildcardsAndOperatorsAsLiteral, Object criteria) {
184     	return createCriteria( example, searchValue, propertyName, false, false, criteria, null );
185     }
186 
187     public boolean createCriteria(Object example, String searchValue, String propertyName, boolean caseInsensitive, boolean treatWildcardsAndOperatorsAsLiteral, Object criteria, Map searchValues) {
188         // if searchValue is empty and the key is not a valid property ignore
189         if (!(criteria instanceof Criteria) || StringUtils.isBlank(searchValue) || !ObjectUtils.isWriteable(example, propertyName, persistenceStructureService)) {
190             return false;
191         }
192 
193         // get property type which is used to determine type of criteria
194         Class propertyType = ObjectUtils.getPropertyType(example, propertyName, persistenceStructureService);
195         if (propertyType == null) {
196             return false;
197         }
198 
199 		// build criteria
200 		if (example instanceof InactivateableFromTo) {
201 			if (KNSPropertyConstants.ACTIVE.equals(propertyName)) {
202 				addInactivateableFromToActiveCriteria(example, searchValue, (Criteria) criteria, searchValues);
203 			} else if (KNSPropertyConstants.CURRENT.equals(propertyName)) {
204 				addInactivateableFromToCurrentCriteria(example, searchValue, (Criteria) criteria, searchValues);
205 			} else if (!KNSPropertyConstants.ACTIVE_AS_OF_DATE.equals(propertyName)) {
206 				addCriteria(propertyName, searchValue, propertyType, caseInsensitive,
207 						treatWildcardsAndOperatorsAsLiteral, (Criteria) criteria);
208 			}
209 		} else {
210 			addCriteria(propertyName, searchValue, propertyType, caseInsensitive, treatWildcardsAndOperatorsAsLiteral,
211 					(Criteria) criteria);
212 		}
213         
214         return true;
215     }
216 
217     /**
218      * Find count of records meeting criteria based on the object and map.
219      */
220     public Long findCountByMap(Object example, Map formProps) {
221         Criteria criteria = new Criteria();
222         // iterate through the parameter map for key values search criteria
223         Iterator propsIter = formProps.keySet().iterator();
224         while (propsIter.hasNext()) {
225             String propertyName = (String) propsIter.next();
226             String searchValue = (String) formProps.get(propertyName);
227 
228             // if searchValue is empty and the key is not a valid property ignore
229             if (StringUtils.isBlank(searchValue) || !(PropertyUtils.isWriteable(example, propertyName))) {
230                 continue;
231             }
232 
233             // get property type which is used to determine type of criteria
234             Class propertyType = ObjectUtils.getPropertyType(example, propertyName, persistenceStructureService);
235             if (propertyType == null) {
236                 continue;
237             }
238         	Boolean caseInsensitive = Boolean.TRUE;
239         	if ( KNSServiceLocator.getDataDictionaryService().isAttributeDefined( example.getClass(), propertyName )) {
240         		caseInsensitive = !KNSServiceLocator.getDataDictionaryService().getAttributeForceUppercase( example.getClass(), propertyName );
241         	}
242         	if ( caseInsensitive == null ) { caseInsensitive = Boolean.TRUE; }
243 
244         	boolean treatWildcardsAndOperatorsAsLiteral = KNSServiceLocator
245 					.getBusinessObjectDictionaryService().isLookupFieldTreatWildcardsAndOperatorsAsLiteral(example.getClass(), propertyName);
246         	
247             // build criteria
248             addCriteria(propertyName, searchValue, propertyType, caseInsensitive, treatWildcardsAndOperatorsAsLiteral, criteria);
249         }
250 
251         // execute query and return result list
252         Query query = QueryFactory.newQuery(example.getClass(), criteria);
253 
254         return new Long(getPersistenceBrokerTemplate().getCount(query));
255     }
256 
257     /**
258      * @see org.kuali.rice.kns.dao.LookupDao#findObjectByMap(java.lang.Object, java.util.Map)
259      */
260     public Object findObjectByMap(Object example, Map formProps) {
261     	if ( persistenceStructureService.isPersistable(example.getClass())) {
262 	    	Criteria criteria = new Criteria();
263 	
264 	    	// iterate through the parameter map for key values search criteria
265 	    	Iterator propsIter = formProps.keySet().iterator();
266 	    	while (propsIter.hasNext()) {
267 	    		String propertyName = (String) propsIter.next();
268 	    		String searchValue = "";
269 	    		if (formProps.get(propertyName) != null) {
270 	    			searchValue = (formProps.get(propertyName)).toString();
271 	    		}
272 	
273 	    		if (StringUtils.isNotBlank(searchValue) & PropertyUtils.isWriteable(example, propertyName)) {
274 	    			Class propertyType = ObjectUtils.getPropertyType(example, propertyName, persistenceStructureService);
275 	    			if (TypeUtils.isIntegralClass(propertyType) || TypeUtils.isDecimalClass(propertyType) ) {
276 	    				criteria.addEqualTo(propertyName, cleanNumeric(searchValue));
277 	    			} else if (TypeUtils.isTemporalClass(propertyType)) {
278 	    				criteria.addEqualTo(propertyName, parseDate( ObjectUtils.clean(searchValue) ) );
279 	    			} else {
280 	    				criteria.addEqualTo(propertyName, searchValue);
281 	    			}
282 	    		}
283 	    	}
284 	
285 	    	// execute query and return result list
286 	    	Query query = QueryFactory.newQuery(example.getClass(), criteria);
287 	    	return getPersistenceBrokerTemplate().getObjectByQuery(query);
288     	}
289     	return null;
290     }
291 
292 
293     /**
294      * Adds to the criteria object based on the property type and any query characters given.
295      */
296     private void addCriteria(String propertyName, String propertyValue, Class propertyType, boolean caseInsensitive, boolean treatWildcardsAndOperatorsAsLiteral, Criteria criteria) {
297         if (!treatWildcardsAndOperatorsAsLiteral && StringUtils.contains(propertyValue, KNSConstants.OR_LOGICAL_OPERATOR)) {
298             addOrCriteria(propertyName, propertyValue, propertyType, caseInsensitive, criteria);
299             return;
300         }
301 
302         if (!treatWildcardsAndOperatorsAsLiteral && StringUtils.contains(propertyValue, KNSConstants.AND_LOGICAL_OPERATOR)) {
303             addAndCriteria(propertyName, propertyValue, propertyType, caseInsensitive, criteria);
304             return;
305         }
306 
307         if (StringUtils.containsIgnoreCase(propertyValue, KNSConstants.NULL_OPERATOR)) {
308         	if (StringUtils.contains(propertyValue, KNSConstants.NOT_LOGICAL_OPERATOR)) {
309         		criteria.addColumnNotNull(propertyName);
310         	}
311         	else {
312         		criteria.addColumnIsNull(propertyName);
313         	}
314         }
315         else if (TypeUtils.isStringClass(propertyType)) {
316         	// KULRICE-85 : made string searches case insensitive - used new DBPlatform function to force strings to upper case
317         	if ( caseInsensitive ) {
318         		propertyName = getDbPlatform().getUpperCaseFunction() + "(" + propertyName + ")";
319         		propertyValue = propertyValue.toUpperCase();
320         	}
321             if (!treatWildcardsAndOperatorsAsLiteral && StringUtils.contains(propertyValue, KNSConstants.NOT_LOGICAL_OPERATOR)) {
322                 addNotCriteria(propertyName, propertyValue, propertyType, caseInsensitive, criteria);
323             } else if (
324             		!treatWildcardsAndOperatorsAsLiteral && propertyValue != null && (
325             				StringUtils.contains(propertyValue, KNSConstants.BETWEEN_OPERATOR) 
326             				|| propertyValue.startsWith(">")
327             				|| propertyValue.startsWith("<") ) ) {
328                 addStringRangeCriteria(propertyName, propertyValue, criteria);
329             } else {
330             	if (treatWildcardsAndOperatorsAsLiteral) {
331             		propertyValue = StringUtils.replace(propertyValue, "*", "\\*");
332             	}
333             	criteria.addLike(propertyName, propertyValue);
334             }
335         } else if (TypeUtils.isIntegralClass(propertyType) || TypeUtils.isDecimalClass(propertyType) ) {
336             addNumericRangeCriteria(propertyName, propertyValue, treatWildcardsAndOperatorsAsLiteral, criteria);
337         } else if (TypeUtils.isTemporalClass(propertyType)) {
338             addDateRangeCriteria(propertyName, propertyValue, treatWildcardsAndOperatorsAsLiteral, criteria);
339         } else if (TypeUtils.isBooleanClass(propertyType)) {
340             criteria.addEqualTo(propertyName, ObjectUtils.clean(propertyValue));
341         } else {
342             LOG.error("not adding criterion for: " + propertyName + "," + propertyType + "," + propertyValue);
343         }
344     }
345     
346     /**
347      * Translates criteria for active status to criteria on the active from and to fields
348      * 
349      * @param example - business object being queried on
350      * @param activeSearchValue - value for the active search field, should convert to boolean
351      * @param criteria - Criteria object being built
352      * @param searchValues - Map containing all search keys and values
353      */
354     protected void addInactivateableFromToActiveCriteria(Object example, String activeSearchValue, Criteria criteria, Map searchValues) {
355 		Timestamp activeTimestamp = LookupUtils.getActiveDateTimestampForCriteria(searchValues);
356 		
357     	String activeBooleanStr = (String) (new OjbCharBooleanConversion()).javaToSql(activeSearchValue);
358     	if (OjbCharBooleanConversion.DATABASE_BOOLEAN_TRUE_STRING_REPRESENTATION.equals(activeBooleanStr)) {
359     		// (active from date <= date or active from date is null) and (date < active to date or active to date is null)
360     		Criteria criteriaBeginDate = new Criteria();
361     		criteriaBeginDate.addLessOrEqualThan(KNSPropertyConstants.ACTIVE_FROM_DATE, activeTimestamp);
362     		
363     		Criteria criteriaBeginDateNull = new Criteria();
364     		criteriaBeginDateNull.addIsNull(KNSPropertyConstants.ACTIVE_FROM_DATE);
365     		criteriaBeginDate.addOrCriteria(criteriaBeginDateNull);
366     		
367     		criteria.addAndCriteria(criteriaBeginDate);
368     		
369     		Criteria criteriaEndDate = new Criteria();
370     		criteriaEndDate.addGreaterThan(KNSPropertyConstants.ACTIVE_TO_DATE, activeTimestamp);
371     	
372     		Criteria criteriaEndDateNull = new Criteria();
373     		criteriaEndDateNull.addIsNull(KNSPropertyConstants.ACTIVE_TO_DATE);
374     		criteriaEndDate.addOrCriteria(criteriaEndDateNull);
375     		
376     		criteria.addAndCriteria(criteriaEndDate);
377     	}
378     	else if (OjbCharBooleanConversion.DATABASE_BOOLEAN_FALSE_STRING_REPRESENTATION.equals(activeBooleanStr)) {
379     		// (date < active from date) or (active from date is null) or (date >= active to date) 
380     		Criteria criteriaNonActive = new Criteria();
381     		criteriaNonActive.addGreaterThan(KNSPropertyConstants.ACTIVE_FROM_DATE, activeTimestamp);
382     		
383     		Criteria criteriaEndDate = new Criteria();
384     		criteriaEndDate.addLessOrEqualThan(KNSPropertyConstants.ACTIVE_TO_DATE, activeTimestamp);
385     		criteriaNonActive.addOrCriteria(criteriaEndDate);
386     		
387     		criteria.addAndCriteria(criteriaNonActive);
388     	}
389     }
390     
391     /**
392      * Translates criteria for current status to criteria on the active from field
393      * 
394      * @param example - business object being queried on
395      * @param currentSearchValue - value for the current search field, should convert to boolean
396      * @param criteria - Criteria object being built
397      */
398 	protected void addInactivateableFromToCurrentCriteria(Object example, String currentSearchValue, Criteria criteria, Map searchValues) {
399 		Criteria maxBeginDateCriteria = new Criteria();
400 		
401 		Timestamp activeTimestamp = LookupUtils.getActiveDateTimestampForCriteria(searchValues);
402 		
403 		maxBeginDateCriteria.addLessOrEqualThan(KNSPropertyConstants.ACTIVE_FROM_DATE, activeTimestamp);
404 
405 		List<String> groupByFieldList = businessObjectDictionaryService.getGroupByAttributesForEffectiveDating(example
406 				.getClass());
407 		if (groupByFieldList == null) {
408 			return;
409 		}
410 
411 		// join back to main query with the group by fields
412 		String[] groupBy = new String[groupByFieldList.size()];
413 		for (int i = 0; i < groupByFieldList.size(); i++) {
414 			String groupByField = groupByFieldList.get(i);
415 			groupBy[i] = groupByField;
416 
417 			maxBeginDateCriteria.addEqualToField(groupByField, Criteria.PARENT_QUERY_PREFIX + groupByField);
418 		}
419 
420 		String[] columns = new String[1];
421 		columns[0] = "max(" + KNSPropertyConstants.ACTIVE_FROM_DATE + ")";
422 
423 		QueryByCriteria query = QueryFactory.newReportQuery(example.getClass(), columns, maxBeginDateCriteria, true);
424 		query.addGroupBy(groupBy);
425 
426 		String currentBooleanStr = (String) (new OjbCharBooleanConversion()).javaToSql(currentSearchValue);
427 		if (OjbCharBooleanConversion.DATABASE_BOOLEAN_TRUE_STRING_REPRESENTATION.equals(currentBooleanStr)) {
428 			criteria.addIn(KNSPropertyConstants.ACTIVE_FROM_DATE, query);
429 		} else if (OjbCharBooleanConversion.DATABASE_BOOLEAN_FALSE_STRING_REPRESENTATION.equals(currentBooleanStr)) {
430 			criteria.addNotIn(KNSPropertyConstants.ACTIVE_FROM_DATE, query);
431 		}
432 	}
433 
434     /**
435      * @param propertyName
436      * @param propertyValue
437      * @param propertyType
438      * @param criteria
439      */
440     private void addOrCriteria(String propertyName, String propertyValue, Class propertyType, boolean caseInsensitive, Criteria criteria) {
441         addLogicalOperatorCriteria(propertyName, propertyValue, propertyType, caseInsensitive, criteria, KNSConstants.OR_LOGICAL_OPERATOR);
442     }
443        
444     /**
445      * @param propertyName
446      * @param propertyValue
447      * @param propertyType
448      * @param criteria
449      */
450     private void addAndCriteria(String propertyName, String propertyValue, Class propertyType, boolean caseInsensitive, Criteria criteria) {
451         addLogicalOperatorCriteria(propertyName, propertyValue, propertyType, caseInsensitive, criteria, KNSConstants.AND_LOGICAL_OPERATOR);
452     }
453 
454     private void addNotCriteria(String propertyName, String propertyValue, Class propertyType, boolean caseInsensitive, Criteria criteria) {
455 
456         String[] splitPropVal = StringUtils.split(propertyValue, KNSConstants.NOT_LOGICAL_OPERATOR);
457 
458         int strLength = splitPropVal.length;
459         // if more than one NOT operator assume an implicit and (i.e. !a!b = !a&!b)
460         if (strLength > 1) {
461             String expandedNot = "!" + StringUtils.join(splitPropVal, KNSConstants.AND_LOGICAL_OPERATOR + KNSConstants.NOT_LOGICAL_OPERATOR);
462             // we know that since this method was called, treatWildcardsAndOperatorsAsLiteral must be false
463             addCriteria(propertyName, expandedNot, propertyType, caseInsensitive, false, criteria);
464         }
465         else {
466             // only one so add a not like
467             criteria.addNotLike(propertyName, splitPropVal[0]);
468         }
469     }
470 
471     /**
472      * Builds a sub criteria object joined with an 'AND' or 'OR' (depending on splitValue) using the split values of propertyValue. Then joins back the
473      * sub criteria to the main criteria using an 'AND'.
474      */
475     private void addLogicalOperatorCriteria(String propertyName, String propertyValue, Class propertyType, boolean caseInsensitive, Criteria criteria, String splitValue) {
476         String[] splitPropVal = StringUtils.split(propertyValue, splitValue);
477 
478         Criteria subCriteria = new Criteria();
479         for (int i = 0; i < splitPropVal.length; i++) {
480         	Criteria predicate = new Criteria();
481 
482             addCriteria(propertyName, splitPropVal[i], propertyType, caseInsensitive, false, predicate);
483             if (splitValue == KNSConstants.OR_LOGICAL_OPERATOR) {
484             	subCriteria.addOrCriteria(predicate);
485             }
486             if (splitValue == KNSConstants.AND_LOGICAL_OPERATOR) {
487             	subCriteria.addAndCriteria(predicate);
488             }
489         }
490 
491         criteria.addAndCriteria(subCriteria);
492     }
493     
494     private java.sql.Date parseDate(String dateString) {
495 		dateString = dateString.trim();
496 		try {
497 			return dateTimeService.convertToSqlDate(dateString);
498 		} catch (ParseException ex) {
499 			return null;
500 		}
501 	}
502 
503     /**
504 	 * Adds to the criteria object based on query characters given
505 	 */
506     private void addDateRangeCriteria(String propertyName, String propertyValue, boolean treatWildcardsAndOperatorsAsLiteral, Criteria criteria) {
507 
508         if (StringUtils.contains(propertyValue, KNSConstants.BETWEEN_OPERATOR)) {
509         	if (treatWildcardsAndOperatorsAsLiteral)
510         		throw new RuntimeException("Wildcards and operators are not allowed on this date field: " + propertyName);
511             String[] rangeValues = StringUtils.split(propertyValue, KNSConstants.BETWEEN_OPERATOR);
512             criteria.addBetween(propertyName, parseDate( ObjectUtils.clean(rangeValues[0] ) ), parseDate( ObjectUtils.clean(rangeValues[1] ) ) );
513         } else if (propertyValue.startsWith(">=")) {
514         	if (treatWildcardsAndOperatorsAsLiteral)
515         		throw new RuntimeException("Wildcards and operators are not allowed on this date field: " + propertyName);
516             criteria.addGreaterOrEqualThan(propertyName, parseDate( ObjectUtils.clean(propertyValue) ) );
517         } else if (propertyValue.startsWith("<=")) {
518         	if (treatWildcardsAndOperatorsAsLiteral)
519         		throw new RuntimeException("Wildcards and operators are not allowed on this date field: " + propertyName);
520             criteria.addLessOrEqualThan(propertyName, parseDate( ObjectUtils.clean(propertyValue) ) );
521         } else if (propertyValue.startsWith(">")) {
522         	if (treatWildcardsAndOperatorsAsLiteral)
523         		throw new RuntimeException("Wildcards and operators are not allowed on this date field: " + propertyName);
524             criteria.addGreaterThan(propertyName, parseDate( ObjectUtils.clean(propertyValue) ) );
525         } else if (propertyValue.startsWith("<")) {
526         	if (treatWildcardsAndOperatorsAsLiteral)
527         		throw new RuntimeException("Wildcards and operators are not allowed on this date field: " + propertyName);
528             criteria.addLessThan(propertyName, parseDate( ObjectUtils.clean(propertyValue) ) );
529         } else {
530             criteria.addEqualTo(propertyName, parseDate( ObjectUtils.clean(propertyValue) ) );
531         }
532     }
533 
534     private BigDecimal cleanNumeric( String value ) {
535         String cleanedValue = value.replaceAll( "[^-0-9.]", "" );
536         // ensure only one "minus" at the beginning, if any
537         if ( cleanedValue.lastIndexOf( '-' ) > 0 ) {
538             if ( cleanedValue.charAt( 0 ) == '-' ) {
539                 cleanedValue = "-" + cleanedValue.replaceAll( "-", "" );
540             } else {
541                 cleanedValue = cleanedValue.replaceAll( "-", "" );
542             }
543         }
544         // ensure only one decimal in the string
545         int decimalLoc = cleanedValue.lastIndexOf( '.' );
546         if ( cleanedValue.indexOf( '.' ) != decimalLoc ) {
547             cleanedValue = cleanedValue.substring( 0, decimalLoc ).replaceAll( "\\.", "" ) + cleanedValue.substring( decimalLoc );
548         }
549         try {
550             return new BigDecimal( cleanedValue );
551         } catch ( NumberFormatException ex ) {
552             GlobalVariables.getMessageMap().putError(KNSConstants.DOCUMENT_ERRORS, RiceKeyConstants.ERROR_CUSTOM, new String[] { "Invalid Numeric Input: " + value });
553             return null;
554         }
555     }
556 
557     /**
558      * Adds to the criteria object based on query characters given
559      */
560     private void addNumericRangeCriteria(String propertyName, String propertyValue, boolean treatWildcardsAndOperatorsAsLiteral, Criteria criteria) {
561 
562         if (StringUtils.contains(propertyValue, KNSConstants.BETWEEN_OPERATOR)) {
563         	if (treatWildcardsAndOperatorsAsLiteral)
564         		throw new RuntimeException("Cannot use wildcards and operators on numeric field " + propertyName);
565             String[] rangeValues = StringUtils.split(propertyValue, KNSConstants.BETWEEN_OPERATOR);
566             criteria.addBetween(propertyName, cleanNumeric( rangeValues[0] ), cleanNumeric( rangeValues[1] ));
567         } else if (propertyValue.startsWith(">=")) {
568         	if (treatWildcardsAndOperatorsAsLiteral)
569         		throw new RuntimeException("Cannot use wildcards and operators on numeric field " + propertyName);
570             criteria.addGreaterOrEqualThan(propertyName, cleanNumeric(propertyValue));
571         } else if (propertyValue.startsWith("<=")) {
572         	if (treatWildcardsAndOperatorsAsLiteral)
573         		throw new RuntimeException("Cannot use wildcards and operators on numeric field " + propertyName);
574             criteria.addLessOrEqualThan(propertyName, cleanNumeric(propertyValue));
575         } else if (propertyValue.startsWith(">")) {
576         	if (treatWildcardsAndOperatorsAsLiteral)
577         		throw new RuntimeException("Cannot use wildcards and operators on numeric field " + propertyName);
578             criteria.addGreaterThan(propertyName, cleanNumeric( propertyValue ) );
579         } else if (propertyValue.startsWith("<")) {
580         	if (treatWildcardsAndOperatorsAsLiteral)
581         		throw new RuntimeException("Cannot use wildcards and operators on numeric field " + propertyName);
582             criteria.addLessThan(propertyName, cleanNumeric(propertyValue));
583         } else {
584             criteria.addEqualTo(propertyName, cleanNumeric(propertyValue));
585         }
586     }
587 
588     /**
589      * Adds to the criteria object based on query characters given
590      */
591     private void addStringRangeCriteria(String propertyName, String propertyValue, Criteria criteria) {
592 
593         if (StringUtils.contains(propertyValue, KNSConstants.BETWEEN_OPERATOR)) {
594             String[] rangeValues = StringUtils.split(propertyValue, KNSConstants.BETWEEN_OPERATOR);
595             criteria.addBetween(propertyName, rangeValues[0], rangeValues[1]);
596         } else if (propertyValue.startsWith(">=")) {
597             criteria.addGreaterOrEqualThan(propertyName, ObjectUtils.clean(propertyValue));
598         } else if (propertyValue.startsWith("<=")) {
599             criteria.addLessOrEqualThan(propertyName, ObjectUtils.clean(propertyValue));
600         } else if (propertyValue.startsWith(">")) {
601             criteria.addGreaterThan(propertyName, ObjectUtils.clean(propertyValue));
602         } else if (propertyValue.startsWith("<")) {
603             criteria.addLessThan(propertyName, ObjectUtils.clean(propertyValue));
604         } else {
605         	criteria.addEqualTo(propertyName, ObjectUtils.clean(propertyValue));
606         }
607     }
608 
609 	public void setDateTimeService(DateTimeService dateTimeService) {
610 		this.dateTimeService = dateTimeService;
611 	}
612 
613 	public void setBusinessObjectDictionaryService(BusinessObjectDictionaryService businessObjectDictionaryService) {
614 		this.businessObjectDictionaryService = businessObjectDictionaryService;
615 	}
616 	
617     public void setPersistenceStructureService(PersistenceStructureService persistenceStructureService) {
618         this.persistenceStructureService = persistenceStructureService;
619     }
620 
621 }