org.kuali.rice.krad.dao.impl.LookupDaoOjb.java Source code

Java tutorial

Introduction

Here is the source code for org.kuali.rice.krad.dao.impl.LookupDaoOjb.java

Source

/**
 * Copyright 2005-2014 The Kuali Foundation
 *
 * Licensed under the Educational Community License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.opensource.org/licenses/ecl2.php
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.kuali.rice.krad.dao.impl;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.ojb.broker.query.Criteria;
import org.apache.ojb.broker.query.Query;
import org.apache.ojb.broker.query.QueryByCriteria;
import org.apache.ojb.broker.query.QueryFactory;
import org.kuali.rice.core.api.datetime.DateTimeService;
import org.kuali.rice.core.api.search.SearchOperator;
import org.kuali.rice.core.api.util.RiceKeyConstants;
import org.kuali.rice.core.api.util.type.TypeUtils;
import org.kuali.rice.core.framework.persistence.ojb.conversion.OjbCharBooleanConversion;
import org.kuali.rice.core.framework.persistence.ojb.dao.PlatformAwareDaoBaseOjb;
import org.kuali.rice.core.framework.persistence.platform.DatabasePlatform;
import org.kuali.rice.kns.service.KNSServiceLocator;
import org.kuali.rice.krad.bo.BusinessObject;
import org.kuali.rice.krad.bo.InactivatableFromTo;
import org.kuali.rice.krad.dao.LookupDao;
import org.kuali.rice.krad.lookup.CollectionIncomplete;
import org.kuali.rice.krad.lookup.LookupUtils;
import org.kuali.rice.krad.service.DataDictionaryService;
import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
import org.kuali.rice.krad.service.PersistenceStructureService;
import org.kuali.rice.krad.util.GlobalVariables;
import org.kuali.rice.krad.util.KRADConstants;
import org.kuali.rice.krad.util.KRADPropertyConstants;
import org.kuali.rice.krad.util.ObjectUtils;
import org.springframework.dao.DataIntegrityViolationException;
import org.springmodules.orm.ojb.OjbOperationException;

import java.math.BigDecimal;
import java.sql.Timestamp;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * OJB implementation of the LookupDao interface
 *
 * @deprecated use new KRAD Data framework {@link org.kuali.rice.krad.data.DataObjectService}
 */
@Deprecated
public class LookupDaoOjb extends PlatformAwareDaoBaseOjb implements LookupDao {
    private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(LookupDaoOjb.class);
    private DateTimeService dateTimeService;
    private PersistenceStructureService persistenceStructureService;
    private DataDictionaryService dataDictionaryService;

    /**
     * Since 2.3
     * This version of findCollectionBySearchHelper is needed for version compatibility.   It allows executeSearch
     * to behave the same way as it did prior to 2.3. The value for searchResultsLimit will be retrieved from the
     * KNS version of LookupUtils.
     *
     * @see org.kuali.rice.krad.dao.LookupDao#findCollectionBySearchHelper(java.lang.Class, java.util.Map, boolean,
     *      boolean)
     */
    public Collection findCollectionBySearchHelper(Class businessObjectClass, Map formProps, boolean unbounded,
            boolean usePrimaryKeyValuesOnly) {
        Integer searchResultsLimit = org.kuali.rice.kns.lookup.LookupUtils
                .getSearchResultsLimit(businessObjectClass);
        return findCollectionBySearchHelper(businessObjectClass, formProps, unbounded, usePrimaryKeyValuesOnly,
                searchResultsLimit);
    }

    /**
     * @see org.kuali.rice.krad.dao.LookupDao#findCollectionBySearchHelper(java.lang.Class, java.util.Map, boolean,
     *      boolean, Integer)
     *
     * If searchResultsLimit is null, the search results will not be limited by any other means.
     */
    public Collection findCollectionBySearchHelper(Class businessObjectClass, Map formProps, boolean unbounded,
            boolean usePrimaryKeyValuesOnly, Integer searchResultsLimit) {
        BusinessObject businessObject = checkBusinessObjectClass(businessObjectClass);
        if (usePrimaryKeyValuesOnly) {
            return executeSearch(businessObjectClass,
                    getCollectionCriteriaFromMapUsingPrimaryKeysOnly(businessObjectClass, formProps), unbounded,
                    searchResultsLimit);
        }

        Criteria crit = getCollectionCriteriaFromMap(businessObject, formProps);
        return executeSearch(businessObjectClass, crit, unbounded, searchResultsLimit);
    }

    /**
     * Builds up criteria object based on the object and map.
     */
    public Criteria getCollectionCriteriaFromMap(BusinessObject example, Map formProps) {
        Criteria criteria = new Criteria();
        Iterator propsIter = formProps.keySet().iterator();
        while (propsIter.hasNext()) {
            String propertyName = (String) propsIter.next();
            Boolean caseInsensitive = Boolean.TRUE;
            if (KRADServiceLocatorWeb.getDataDictionaryService().isAttributeDefined(example.getClass(),
                    propertyName)) {
                // 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 
                // 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 
                // could be mixed case.  Thus, caseInsensitive will be the opposite of forceUppercase. 
                caseInsensitive = !KRADServiceLocatorWeb.getDataDictionaryService()
                        .getAttributeForceUppercase(example.getClass(), propertyName);
            }
            if (caseInsensitive == null) {
                caseInsensitive = Boolean.TRUE;
            }
            boolean treatWildcardsAndOperatorsAsLiteral = KNSServiceLocator.getBusinessObjectDictionaryService()
                    .isLookupFieldTreatWildcardsAndOperatorsAsLiteral(example.getClass(), propertyName);

            if (formProps.get(propertyName) instanceof Collection) {
                Iterator iter = ((Collection) formProps.get(propertyName)).iterator();
                while (iter.hasNext()) {
                    String searchValue = (String) iter.next();
                    if (!caseInsensitive) {
                        // Verify that the searchValue is uppercased if caseInsensitive is false 
                        searchValue = searchValue.toUpperCase();
                    }
                    if (!createCriteria(example, searchValue, propertyName, caseInsensitive,
                            treatWildcardsAndOperatorsAsLiteral, criteria, formProps)) {
                        throw new RuntimeException("Invalid value in Collection");
                    }
                }
            } else {
                String searchValue = (String) formProps.get(propertyName);
                if (!caseInsensitive) {
                    // Verify that the searchValue is uppercased if caseInsensitive is false 
                    searchValue = searchValue.toUpperCase();
                }
                if (!createCriteria(example, searchValue, propertyName, caseInsensitive,
                        treatWildcardsAndOperatorsAsLiteral, criteria, formProps)) {
                    continue;
                }
            }
        }
        return criteria;
    }

    public Criteria getCollectionCriteriaFromMapUsingPrimaryKeysOnly(Class businessObjectClass, Map formProps) {
        BusinessObject businessObject = checkBusinessObjectClass(businessObjectClass);
        Criteria criteria = new Criteria();
        List pkFields = KRADServiceLocatorWeb.getLegacyDataAdapter().listPrimaryKeyFieldNames(businessObjectClass);
        Iterator pkIter = pkFields.iterator();
        while (pkIter.hasNext()) {
            String pkFieldName = (String) pkIter.next();
            String pkValue = (String) formProps.get(pkFieldName);

            if (StringUtils.isBlank(pkValue)) {
                throw new RuntimeException("Missing pk value for field " + pkFieldName
                        + " when a search based on PK values only is performed.");
            } else {
                for (SearchOperator op : SearchOperator.QUERY_CHARACTERS) {
                    if (pkValue.contains(op.op())) {
                        throw new RuntimeException("Value \"" + pkValue + "\" for PK field " + pkFieldName
                                + " contains wildcard/operator characters.");
                    }
                }
            }
            boolean treatWildcardsAndOperatorsAsLiteral = KNSServiceLocator.getBusinessObjectDictionaryService()
                    .isLookupFieldTreatWildcardsAndOperatorsAsLiteral(businessObjectClass, pkFieldName);
            createCriteria(businessObject, pkValue, pkFieldName, false, treatWildcardsAndOperatorsAsLiteral,
                    criteria);
        }
        return criteria;
    }

    private BusinessObject checkBusinessObjectClass(Class businessObjectClass) {
        if (businessObjectClass == null) {
            throw new IllegalArgumentException(
                    "BusinessObject class passed to LookupDaoOjb findCollectionBySearchHelper... method was null");
        }
        BusinessObject businessObject = null;
        try {
            businessObject = (BusinessObject) businessObjectClass.newInstance();
        } catch (IllegalAccessException e) {
            throw new RuntimeException("LookupDaoOjb could not get instance of " + businessObjectClass.getName(),
                    e);
        } catch (InstantiationException e) {
            throw new RuntimeException("LookupDaoOjb could not get instance of " + businessObjectClass.getName(),
                    e);
        }
        return businessObject;
    }

    private Collection executeSearch(Class businessObjectClass, Criteria criteria, boolean unbounded,
            Integer searchResultsLimit) {
        Collection searchResults = new ArrayList();
        Long matchingResultsCount = null;
        try {
            if (!unbounded && searchResultsLimit == null) {
                searchResultsLimit = org.kuali.rice.kns.lookup.LookupUtils
                        .getSearchResultsLimit(businessObjectClass);
            }
            // A negative number in searchResultsLimit means the search results should be unlimited.
            if (!unbounded && (searchResultsLimit != null) && searchResultsLimit >= 0) {
                matchingResultsCount = new Long(getPersistenceBrokerTemplate()
                        .getCount(QueryFactory.newQuery(businessObjectClass, criteria)));
                applySearchResultsLimit(businessObjectClass, criteria, getDbPlatform(), searchResultsLimit);
            }
            if ((matchingResultsCount == null)
                    || (matchingResultsCount.intValue() <= searchResultsLimit.intValue())) {
                matchingResultsCount = new Long(0);
            }
            searchResults = getPersistenceBrokerTemplate()
                    .getCollectionByQuery(QueryFactory.newQuery(businessObjectClass, criteria));
            // populate Person objects in business objects
            List bos = new ArrayList();
            bos.addAll(searchResults);
            searchResults = bos;

        } catch (OjbOperationException e) {
            throw new RuntimeException("LookupDaoOjb encountered exception during executeSearch", e);
        } catch (DataIntegrityViolationException e) {
            throw new RuntimeException("LookupDaoOjb encountered exception during executeSearch", e);
        }
        return new CollectionIncomplete(searchResults, matchingResultsCount);
    }

    /**
     * This method applies the search results limit to the search criteria for this BO
     *
     * @param businessObjectClass BO class to search on / get limit for
     * @param criteria search criteria
     * @param platform database platform
     * @param limit limit to use.  If limit is null, getSearchResultsLimit will be called using the businessObjectClass
     * to see if a limit can be found for this particular businessObjectClass.
     */
    protected void applySearchResultsLimit(Class businessObjectClass, Criteria criteria, DatabasePlatform platform,
            Integer limit) {
        String limitSql = null;
        if (limit != null) {
            limitSql = platform.applyLimitSql(limit);
        } else {
            limit = LookupUtils.getSearchResultsLimit(businessObjectClass, null);
            if (limit != null) {
                limitSql = platform.applyLimitSql(limit);
            }
        }
        if (StringUtils.isNotBlank(limitSql)) {
            criteria.addSql(limitSql);
        }
    }

    public boolean createCriteria(Object example, String searchValue, String propertyName, Object criteria) {
        return createCriteria(example, searchValue, propertyName, false, false, criteria);
    }

    public boolean createCriteria(Object example, String searchValue, String propertyName, boolean caseInsensitive,
            boolean treatWildcardsAndOperatorsAsLiteral, Object criteria) {
        return createCriteria(example, searchValue, propertyName, caseInsensitive,
                treatWildcardsAndOperatorsAsLiteral, criteria, null);
    }

    public boolean createCriteria(Object example, String searchValue, String propertyName, boolean caseInsensitive,
            boolean treatWildcardsAndOperatorsAsLiteral, Object criteria, Map searchValues) {
        // if searchValue is empty and the key is not a valid property ignore
        if (!(criteria instanceof Criteria) || StringUtils.isBlank(searchValue)
                || !ObjectUtils.isWriteable(example, propertyName, persistenceStructureService)) {
            return false;
        }

        // get property type which is used to determine type of criteria
        Class propertyType = ObjectUtils.getPropertyType(example, propertyName, persistenceStructureService);
        if (propertyType == null) {
            return false;
        }

        // build criteria
        if (example instanceof InactivatableFromTo) {
            if (KRADPropertyConstants.ACTIVE.equals(propertyName)) {
                addInactivateableFromToActiveCriteria(example, searchValue, (Criteria) criteria, searchValues);
            } else if (KRADPropertyConstants.CURRENT.equals(propertyName)) {
                addInactivateableFromToCurrentCriteria(example, searchValue, (Criteria) criteria, searchValues);
            } else if (!KRADPropertyConstants.ACTIVE_AS_OF_DATE.equals(propertyName)) {
                addCriteria(propertyName, searchValue, propertyType, caseInsensitive,
                        treatWildcardsAndOperatorsAsLiteral, (Criteria) criteria);
            }
        } else {
            addCriteria(propertyName, searchValue, propertyType, caseInsensitive,
                    treatWildcardsAndOperatorsAsLiteral, (Criteria) criteria);
        }

        return true;
    }

    /**
     * Find count of records meeting criteria based on the object and map.
     */
    public Long findCountByMap(Object example, Map formProps) {
        Criteria criteria = new Criteria();
        // iterate through the parameter map for key values search criteria
        Iterator propsIter = formProps.keySet().iterator();
        while (propsIter.hasNext()) {
            String propertyName = (String) propsIter.next();
            String searchValue = (String) formProps.get(propertyName);

            // if searchValue is empty and the key is not a valid property ignore
            if (StringUtils.isBlank(searchValue) || !(PropertyUtils.isWriteable(example, propertyName))) {
                continue;
            }

            // get property type which is used to determine type of criteria
            Class propertyType = ObjectUtils.getPropertyType(example, propertyName, persistenceStructureService);
            if (propertyType == null) {
                continue;
            }
            Boolean caseInsensitive = Boolean.TRUE;
            if (KRADServiceLocatorWeb.getDataDictionaryService().isAttributeDefined(example.getClass(),
                    propertyName)) {
                caseInsensitive = !KRADServiceLocatorWeb.getDataDictionaryService()
                        .getAttributeForceUppercase(example.getClass(), propertyName);
            }
            if (caseInsensitive == null) {
                caseInsensitive = Boolean.TRUE;
            }

            boolean treatWildcardsAndOperatorsAsLiteral = KNSServiceLocator.getBusinessObjectDictionaryService()
                    .isLookupFieldTreatWildcardsAndOperatorsAsLiteral(example.getClass(), propertyName);

            if (!caseInsensitive) {
                // Verify that the searchValue is uppercased if caseInsensitive is false 
                searchValue = searchValue.toUpperCase();
            }

            // build criteria
            addCriteria(propertyName, searchValue, propertyType, caseInsensitive,
                    treatWildcardsAndOperatorsAsLiteral, criteria);
        }

        // execute query and return result list
        Query query = QueryFactory.newQuery(example.getClass(), criteria);

        return new Long(getPersistenceBrokerTemplate().getCount(query));
    }

    /**
     * @see org.kuali.rice.krad.dao.LookupDao#findObjectByMap(java.lang.Object, java.util.Map)
     */
    @Override
    public <T extends Object> T findObjectByMap(Class<T> type, Map<String, String> formProps) {
        T example = null;
        try {
            example = type.newInstance();
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Cannot get new instance of " + type.getName(), e);
        } catch (InstantiationException e) {
            throw new RuntimeException("Cannot instantiate " + type.getName(), e);
        }

        if (KRADServiceLocatorWeb.getLegacyDataAdapter().isPersistable(example.getClass())) {
            Criteria criteria = new Criteria();

            // iterate through the parameter map for search criteria
            for (Map.Entry<String, String> formProp : formProps.entrySet()) {

                String propertyName = formProp.getKey();
                String searchValue = "";
                if (formProp.getValue() != null) {
                    searchValue = formProp.getValue();
                }

                if (StringUtils.isNotBlank(searchValue) & PropertyUtils.isWriteable(example, propertyName)) {
                    Class propertyType = ObjectUtils.getPropertyType(example, propertyName,
                            persistenceStructureService);
                    if (TypeUtils.isIntegralClass(propertyType) || TypeUtils.isDecimalClass(propertyType)) {
                        criteria.addEqualTo(propertyName, cleanNumeric(searchValue));
                    } else if (TypeUtils.isTemporalClass(propertyType)) {
                        criteria.addEqualTo(propertyName, parseDate(ObjectUtils.clean(searchValue)));
                    } else {
                        criteria.addEqualTo(propertyName, searchValue);
                    }
                }
            }

            // execute query and return result list
            Query query = QueryFactory.newQuery(example.getClass(), criteria);
            return (T) getPersistenceBrokerTemplate().getObjectByQuery(query);
        }
        return null;
    }

    /**
     * Adds to the criteria object based on the property type and any query characters given.
     */
    private void addCriteria(String propertyName, String propertyValue, Class propertyType, boolean caseInsensitive,
            boolean treatWildcardsAndOperatorsAsLiteral, Criteria criteria) {
        if (!treatWildcardsAndOperatorsAsLiteral && StringUtils.contains(propertyValue, SearchOperator.OR.op())) {
            addOrCriteria(propertyName, propertyValue, propertyType, caseInsensitive, criteria);
            return;
        }

        if (!treatWildcardsAndOperatorsAsLiteral && StringUtils.contains(propertyValue, SearchOperator.AND.op())) {
            addAndCriteria(propertyName, propertyValue, propertyType, caseInsensitive, criteria);
            return;
        }

        if (StringUtils.equalsIgnoreCase(propertyValue, SearchOperator.NULL.op())
                || StringUtils.equalsIgnoreCase(propertyValue, SearchOperator.NOT_NULL.op())) {
            // KULRICE-6846 null Lookup criteria causes sql exception
            if (StringUtils.contains(propertyValue, SearchOperator.NOT.op())) {
                criteria.addNotNull(propertyName);
            } else {
                criteria.addIsNull(propertyName);
            }
        } else if (TypeUtils.isStringClass(propertyType)) {
            // KULRICE-85 : made string searches case insensitive - used new DBPlatform function to force strings to upper case
            if (caseInsensitive) {
                propertyName = getDbPlatform().getUpperCaseFunction() + "(" + propertyName + ")";
                propertyValue = propertyValue.toUpperCase();
            }
            if (!treatWildcardsAndOperatorsAsLiteral
                    && StringUtils.contains(propertyValue, SearchOperator.NOT.op())) {
                addNotCriteria(propertyName, propertyValue, propertyType, caseInsensitive, criteria);
            } else if (!treatWildcardsAndOperatorsAsLiteral && propertyValue != null
                    && (StringUtils.contains(propertyValue, SearchOperator.BETWEEN.op())
                            || propertyValue.startsWith(">") || propertyValue.startsWith("<"))) {
                addStringRangeCriteria(propertyName, propertyValue, criteria);
            } else {
                if (treatWildcardsAndOperatorsAsLiteral) {
                    propertyValue = StringUtils.replace(propertyValue, "*", "\\*");
                }
                criteria.addLike(propertyName, propertyValue);
            }
        } else if (TypeUtils.isIntegralClass(propertyType) || TypeUtils.isDecimalClass(propertyType)) {
            addNumericRangeCriteria(propertyName, propertyValue, treatWildcardsAndOperatorsAsLiteral, criteria);
        } else if (TypeUtils.isTemporalClass(propertyType)) {
            addDateRangeCriteria(propertyName, propertyValue, treatWildcardsAndOperatorsAsLiteral, criteria);
        } else if (TypeUtils.isBooleanClass(propertyType)) {
            criteria.addEqualTo(propertyName, ObjectUtils.clean(propertyValue));
        } else {
            LOG.error("not adding criterion for: " + propertyName + "," + propertyType + "," + propertyValue);
        }
    }

    /**
     * Translates criteria for active status to criteria on the active from and to fields
     * 
     * @param example - business object being queried on
     * @param activeSearchValue - value for the active search field, should convert to boolean
     * @param criteria - Criteria object being built
     * @param searchValues - Map containing all search keys and values
     */
    protected void addInactivateableFromToActiveCriteria(Object example, String activeSearchValue,
            Criteria criteria, Map searchValues) {
        Timestamp activeTimestamp = LookupUtils.getActiveDateTimestampForCriteria(searchValues);

        String activeBooleanStr = (String) (new OjbCharBooleanConversion()).javaToSql(activeSearchValue);
        if (OjbCharBooleanConversion.DATABASE_BOOLEAN_TRUE_STRING_REPRESENTATION.equals(activeBooleanStr)) {
            // (active from date <= date or active from date is null) and (date < active to date or active to date is null)
            Criteria criteriaBeginDate = new Criteria();
            criteriaBeginDate.addLessOrEqualThan(KRADPropertyConstants.ACTIVE_FROM_DATE, activeTimestamp);

            Criteria criteriaBeginDateNull = new Criteria();
            criteriaBeginDateNull.addIsNull(KRADPropertyConstants.ACTIVE_FROM_DATE);
            criteriaBeginDate.addOrCriteria(criteriaBeginDateNull);

            criteria.addAndCriteria(criteriaBeginDate);

            Criteria criteriaEndDate = new Criteria();
            criteriaEndDate.addGreaterThan(KRADPropertyConstants.ACTIVE_TO_DATE, activeTimestamp);

            Criteria criteriaEndDateNull = new Criteria();
            criteriaEndDateNull.addIsNull(KRADPropertyConstants.ACTIVE_TO_DATE);
            criteriaEndDate.addOrCriteria(criteriaEndDateNull);

            criteria.addAndCriteria(criteriaEndDate);
        } else if (OjbCharBooleanConversion.DATABASE_BOOLEAN_FALSE_STRING_REPRESENTATION.equals(activeBooleanStr)) {
            // (date < active from date) or (active from date is null) or (date >= active to date) 
            Criteria criteriaNonActive = new Criteria();
            criteriaNonActive.addGreaterThan(KRADPropertyConstants.ACTIVE_FROM_DATE, activeTimestamp);

            Criteria criteriaEndDate = new Criteria();
            criteriaEndDate.addLessOrEqualThan(KRADPropertyConstants.ACTIVE_TO_DATE, activeTimestamp);
            criteriaNonActive.addOrCriteria(criteriaEndDate);

            criteria.addAndCriteria(criteriaNonActive);
        }
    }

    /**
     * Translates criteria for current status to criteria on the active from field
     * 
     * @param example - business object being queried on
     * @param currentSearchValue - value for the current search field, should convert to boolean
     * @param criteria - Criteria object being built
     */
    protected void addInactivateableFromToCurrentCriteria(Object example, String currentSearchValue,
            Criteria criteria, Map searchValues) {
        Criteria maxBeginDateCriteria = new Criteria();

        Timestamp activeTimestamp = LookupUtils.getActiveDateTimestampForCriteria(searchValues);

        maxBeginDateCriteria.addLessOrEqualThan(KRADPropertyConstants.ACTIVE_FROM_DATE, activeTimestamp);

        List<String> groupByFieldList = dataDictionaryService
                .getGroupByAttributesForEffectiveDating(example.getClass());
        if (groupByFieldList == null) {
            return;
        }

        // join back to main query with the group by fields
        String[] groupBy = new String[groupByFieldList.size()];
        for (int i = 0; i < groupByFieldList.size(); i++) {
            String groupByField = groupByFieldList.get(i);
            groupBy[i] = groupByField;

            maxBeginDateCriteria.addEqualToField(groupByField, Criteria.PARENT_QUERY_PREFIX + groupByField);
        }

        String[] columns = new String[1];
        columns[0] = "max(" + KRADPropertyConstants.ACTIVE_FROM_DATE + ")";

        QueryByCriteria query = QueryFactory.newReportQuery(example.getClass(), columns, maxBeginDateCriteria,
                true);
        query.addGroupBy(groupBy);

        String currentBooleanStr = (String) (new OjbCharBooleanConversion()).javaToSql(currentSearchValue);
        if (OjbCharBooleanConversion.DATABASE_BOOLEAN_TRUE_STRING_REPRESENTATION.equals(currentBooleanStr)) {
            criteria.addIn(KRADPropertyConstants.ACTIVE_FROM_DATE, query);
        } else if (OjbCharBooleanConversion.DATABASE_BOOLEAN_FALSE_STRING_REPRESENTATION
                .equals(currentBooleanStr)) {
            criteria.addNotIn(KRADPropertyConstants.ACTIVE_FROM_DATE, query);
        }
    }

    /**
     * @param propertyName
     * @param propertyValue
     * @param propertyType
     * @param criteria
     */
    private void addOrCriteria(String propertyName, String propertyValue, Class propertyType,
            boolean caseInsensitive, Criteria criteria) {
        addLogicalOperatorCriteria(propertyName, propertyValue, propertyType, caseInsensitive, criteria,
                SearchOperator.OR.op());
    }

    /**
     * @param propertyName
     * @param propertyValue
     * @param propertyType
     * @param criteria
     */
    private void addAndCriteria(String propertyName, String propertyValue, Class propertyType,
            boolean caseInsensitive, Criteria criteria) {
        addLogicalOperatorCriteria(propertyName, propertyValue, propertyType, caseInsensitive, criteria,
                SearchOperator.AND.op());
    }

    private void addNotCriteria(String propertyName, String propertyValue, Class propertyType,
            boolean caseInsensitive, Criteria criteria) {

        String[] splitPropVal = StringUtils.split(propertyValue, SearchOperator.NOT.op());
        try {
            int strLength = splitPropVal.length;
            // if Not'ed empty criteria
            if (strLength == 0)
                throw new IllegalArgumentException("Improper syntax of NOT operator in " + propertyName);
            // if more than one NOT operator assume an implicit and (i.e. !a!b = !a&!b)
            if (strLength > 1) {
                String expandedNot = SearchOperator.NOT
                        + StringUtils.join(splitPropVal, SearchOperator.AND.op() + SearchOperator.NOT.op());
                // we know that since this method was called, treatWildcardsAndOperatorsAsLiteral must be false
                addCriteria(propertyName, expandedNot, propertyType, caseInsensitive, false, criteria);
            } else {
                // only one so add a not like
                criteria.addNotLike(propertyName, splitPropVal[0]);
            }
        } catch (IllegalArgumentException ex) {
            GlobalVariables.getMessageMap().putError("lookupCriteria[" + propertyName + "]",
                    RiceKeyConstants.ERROR_NOT_SYNTAX, propertyName);
        }
    }

    /**
     * Builds a sub criteria object joined with an 'AND' or 'OR' (depending on splitValue) using the split values of propertyValue. Then joins back the
     * sub criteria to the main criteria using an 'AND'.
     */
    private void addLogicalOperatorCriteria(String propertyName, String propertyValue, Class propertyType,
            boolean caseInsensitive, Criteria criteria, String splitValue) {
        String[] splitPropVal = StringUtils.split(propertyValue, splitValue);

        Criteria subCriteria = new Criteria();
        for (int i = 0; i < splitPropVal.length; i++) {
            Criteria predicate = new Criteria();

            addCriteria(propertyName, splitPropVal[i], propertyType, caseInsensitive, false, predicate);
            if (splitValue.equals(SearchOperator.OR.op())) {
                subCriteria.addOrCriteria(predicate);
            }
            if (splitValue.equals(SearchOperator.AND.op())) {
                subCriteria.addAndCriteria(predicate);
            }
        }

        criteria.addAndCriteria(subCriteria);
    }

    private java.sql.Date parseDate(String dateString) {
        dateString = dateString.trim();
        try {
            return dateTimeService.convertToSqlDate(dateString);
        } catch (ParseException ex) {
            return null;
        }
    }

    /**
    * Adds to the criteria object based on query characters given
    */
    private void addDateRangeCriteria(String propertyName, String propertyValue,
            boolean treatWildcardsAndOperatorsAsLiteral, Criteria criteria) {

        try {
            if (StringUtils.contains(propertyValue, SearchOperator.BETWEEN.op())) {
                if (treatWildcardsAndOperatorsAsLiteral)
                    throw new RuntimeException(
                            "Wildcards and operators are not allowed on this date field: " + propertyName);
                String[] rangeValues = StringUtils.split(propertyValue, SearchOperator.BETWEEN.op());
                if (rangeValues.length < 2)
                    throw new IllegalArgumentException("Improper syntax of BETWEEN operator in " + propertyName);

                criteria.addBetween(propertyName, parseDate(ObjectUtils.clean(rangeValues[0])),
                        parseDate(ObjectUtils.clean(rangeValues[1])));
            } else if (propertyValue.startsWith(SearchOperator.GREATER_THAN_EQUAL.op())) {
                if (treatWildcardsAndOperatorsAsLiteral)
                    throw new RuntimeException(
                            "Wildcards and operators are not allowed on this date field: " + propertyName);
                criteria.addGreaterOrEqualThan(propertyName, parseDate(ObjectUtils.clean(propertyValue)));
            } else if (propertyValue.startsWith(SearchOperator.LESS_THAN_EQUAL.op())) {
                if (treatWildcardsAndOperatorsAsLiteral)
                    throw new RuntimeException(
                            "Wildcards and operators are not allowed on this date field: " + propertyName);
                criteria.addLessOrEqualThan(propertyName, parseDate(ObjectUtils.clean(propertyValue)));
            } else if (propertyValue.startsWith(SearchOperator.GREATER_THAN.op())) {
                if (treatWildcardsAndOperatorsAsLiteral)
                    throw new RuntimeException(
                            "Wildcards and operators are not allowed on this date field: " + propertyName);
                criteria.addGreaterThan(propertyName, parseDate(ObjectUtils.clean(propertyValue)));
            } else if (propertyValue.startsWith(SearchOperator.LESS_THAN.op())) {
                if (treatWildcardsAndOperatorsAsLiteral)
                    throw new RuntimeException(
                            "Wildcards and operators are not allowed on this date field: " + propertyName);
                criteria.addLessThan(propertyName, parseDate(ObjectUtils.clean(propertyValue)));
            } else {
                criteria.addEqualTo(propertyName, parseDate(ObjectUtils.clean(propertyValue)));
            }
        } catch (IllegalArgumentException ex) {
            GlobalVariables.getMessageMap().putError("lookupCriteria[" + propertyName + "]",
                    RiceKeyConstants.ERROR_BETWEEN_SYNTAX, propertyName);
        }
    }

    private BigDecimal cleanNumeric(String value) {
        String cleanedValue = value.replaceAll("[^-0-9.]", "");
        // ensure only one "minus" at the beginning, if any
        if (cleanedValue.lastIndexOf('-') > 0) {
            if (cleanedValue.charAt(0) == '-') {
                cleanedValue = "-" + cleanedValue.replaceAll("-", "");
            } else {
                cleanedValue = cleanedValue.replaceAll("-", "");
            }
        }
        // ensure only one decimal in the string
        int decimalLoc = cleanedValue.lastIndexOf('.');
        if (cleanedValue.indexOf('.') != decimalLoc) {
            cleanedValue = cleanedValue.substring(0, decimalLoc).replaceAll("\\.", "")
                    + cleanedValue.substring(decimalLoc);
        }
        try {
            return new BigDecimal(cleanedValue);
        } catch (NumberFormatException ex) {
            GlobalVariables.getMessageMap().putError(KRADConstants.DOCUMENT_ERRORS, RiceKeyConstants.ERROR_CUSTOM,
                    new String[] { "Invalid Numeric Input: " + value });
            return null;
        }
    }

    /**
     * Adds to the criteria object based on query characters given
     */
    private void addNumericRangeCriteria(String propertyName, String propertyValue,
            boolean treatWildcardsAndOperatorsAsLiteral, Criteria criteria) {

        try {
            if (StringUtils.contains(propertyValue, SearchOperator.BETWEEN.op())) {
                if (treatWildcardsAndOperatorsAsLiteral)
                    throw new RuntimeException(
                            "Cannot use wildcards and operators on numeric field " + propertyName);
                String[] rangeValues = StringUtils.split(propertyValue, SearchOperator.BETWEEN.op());
                if (rangeValues.length < 2)
                    throw new IllegalArgumentException("Improper syntax of BETWEEN operator in " + propertyName);

                criteria.addBetween(propertyName, cleanNumeric(rangeValues[0]), cleanNumeric(rangeValues[1]));
            } else if (propertyValue.startsWith(SearchOperator.GREATER_THAN_EQUAL.op())) {
                if (treatWildcardsAndOperatorsAsLiteral)
                    throw new RuntimeException(
                            "Cannot use wildcards and operators on numeric field " + propertyName);
                criteria.addGreaterOrEqualThan(propertyName, cleanNumeric(propertyValue));
            } else if (propertyValue.startsWith(SearchOperator.LESS_THAN_EQUAL.op())) {
                if (treatWildcardsAndOperatorsAsLiteral)
                    throw new RuntimeException(
                            "Cannot use wildcards and operators on numeric field " + propertyName);
                criteria.addLessOrEqualThan(propertyName, cleanNumeric(propertyValue));
            } else if (propertyValue.startsWith(SearchOperator.GREATER_THAN.op())) {
                if (treatWildcardsAndOperatorsAsLiteral)
                    throw new RuntimeException(
                            "Cannot use wildcards and operators on numeric field " + propertyName);
                criteria.addGreaterThan(propertyName, cleanNumeric(propertyValue));
            } else if (propertyValue.startsWith(SearchOperator.LESS_THAN.op())) {
                if (treatWildcardsAndOperatorsAsLiteral)
                    throw new RuntimeException(
                            "Cannot use wildcards and operators on numeric field " + propertyName);
                criteria.addLessThan(propertyName, cleanNumeric(propertyValue));
            } else {
                criteria.addEqualTo(propertyName, cleanNumeric(propertyValue));
            }
        } catch (IllegalArgumentException ex) {
            GlobalVariables.getMessageMap().putError("lookupCriteria[" + propertyName + "]",
                    RiceKeyConstants.ERROR_BETWEEN_SYNTAX, propertyName);
        }
    }

    /**
     * Adds to the criteria object based on query characters given
     */
    private void addStringRangeCriteria(String propertyName, String propertyValue, Criteria criteria) {

        try {
            if (StringUtils.contains(propertyValue, SearchOperator.BETWEEN.op())) {
                String[] rangeValues = StringUtils.split(propertyValue, SearchOperator.BETWEEN.op());
                if (rangeValues.length < 2)
                    throw new IllegalArgumentException("Improper syntax of BETWEEN operator in " + propertyName);

                criteria.addBetween(propertyName, rangeValues[0], rangeValues[1]);
            } else if (propertyValue.startsWith(SearchOperator.GREATER_THAN_EQUAL.op())) {
                criteria.addGreaterOrEqualThan(propertyName, ObjectUtils.clean(propertyValue));
            } else if (propertyValue.startsWith(SearchOperator.LESS_THAN_EQUAL.op())) {
                criteria.addLessOrEqualThan(propertyName, ObjectUtils.clean(propertyValue));
            } else if (propertyValue.startsWith(SearchOperator.GREATER_THAN.op())) {
                criteria.addGreaterThan(propertyName, ObjectUtils.clean(propertyValue));
            } else if (propertyValue.startsWith(SearchOperator.LESS_THAN.op())) {
                criteria.addLessThan(propertyName, ObjectUtils.clean(propertyValue));
            } else {
                criteria.addEqualTo(propertyName, ObjectUtils.clean(propertyValue));
            }
        } catch (IllegalArgumentException ex) {
            GlobalVariables.getMessageMap().putError("lookupCriteria[" + propertyName + "]",
                    RiceKeyConstants.ERROR_BETWEEN_SYNTAX, propertyName);
        }
    }

    public void setDateTimeService(DateTimeService dateTimeService) {
        this.dateTimeService = dateTimeService;
    }

    public void setPersistenceStructureService(PersistenceStructureService persistenceStructureService) {
        this.persistenceStructureService = persistenceStructureService;
    }

    public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
        this.dataDictionaryService = dataDictionaryService;
    }
}