Coverage Report - org.kuali.rice.kew.docsearch.StandardDocumentSearchGenerator
 
Classes in this File Line Coverage Branch Coverage Complexity
StandardDocumentSearchGenerator
0%
0/868
0%
0/522
4.922
 
 1  
 /*
 2  
  * Copyright 2005-2007 The Kuali Foundation
 3  
  *
 4  
  *
 5  
  * Licensed under the Educational Community License, Version 2.0 (the "License");
 6  
  * you may not use this file except in compliance with the License.
 7  
  * You may obtain a copy of the License at
 8  
  *
 9  
  * http://www.opensource.org/licenses/ecl2.php
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 package org.kuali.rice.kew.docsearch;
 18  
 
 19  
 
 20  
 import java.math.BigDecimal;
 21  
 import java.sql.ResultSet;
 22  
 import java.sql.SQLException;
 23  
 import java.sql.Statement;
 24  
 import java.sql.Timestamp;
 25  
 import java.text.SimpleDateFormat;
 26  
 import java.util.ArrayList;
 27  
 import java.util.Arrays;
 28  
 import java.util.Calendar;
 29  
 import java.util.Collection;
 30  
 import java.util.HashMap;
 31  
 import java.util.HashSet;
 32  
 import java.util.Iterator;
 33  
 import java.util.List;
 34  
 import java.util.Map;
 35  
 import java.util.Set;
 36  
 import java.util.TreeSet;
 37  
 
 38  
 import org.apache.commons.lang.StringUtils;
 39  
 import org.kuali.rice.core.database.platform.DatabasePlatform;
 40  
 import org.kuali.rice.core.exception.RiceRuntimeException;
 41  
 import org.kuali.rice.core.jdbc.SqlBuilder;
 42  
 import org.kuali.rice.core.jdbc.criteria.Criteria;
 43  
 import org.kuali.rice.core.resourceloader.GlobalResourceLoader;
 44  
 import org.kuali.rice.core.util.RiceConstants;
 45  
 import org.kuali.rice.kew.doctype.SecuritySession;
 46  
 import org.kuali.rice.kew.doctype.bo.DocumentType;
 47  
 import org.kuali.rice.kew.doctype.service.DocumentTypeService;
 48  
 import org.kuali.rice.kew.engine.node.RouteNode;
 49  
 import org.kuali.rice.kew.exception.WorkflowRuntimeException;
 50  
 import org.kuali.rice.kew.exception.WorkflowServiceError;
 51  
 import org.kuali.rice.kew.exception.WorkflowServiceErrorImpl;
 52  
 import org.kuali.rice.kew.rule.WorkflowAttributeValidationError;
 53  
 import org.kuali.rice.kew.service.KEWServiceLocator;
 54  
 import org.kuali.rice.kew.util.KEWConstants;
 55  
 import org.kuali.rice.kew.util.PerformanceLogger;
 56  
 import org.kuali.rice.kew.util.Utilities;
 57  
 import org.kuali.rice.kew.web.KeyValueSort;
 58  
 import org.kuali.rice.kew.web.session.UserSession;
 59  
 import org.kuali.rice.kim.bo.Group;
 60  
 import org.kuali.rice.kim.bo.Person;
 61  
 import org.kuali.rice.kim.bo.entity.dto.KimEntityNamePrincipalNameInfo;
 62  
 import org.kuali.rice.kim.service.KIMServiceLocator;
 63  
 import org.kuali.rice.kns.service.KNSServiceLocator;
 64  
 import org.kuali.rice.kns.util.GlobalVariables;
 65  
 import org.kuali.rice.kns.util.KNSConstants;
 66  
 import org.kuali.rice.kns.util.MessageMap;
 67  
 import org.kuali.rice.kns.util.ObjectUtils;
 68  
 import org.kuali.rice.kns.util.RiceKeyConstants;
 69  
 import org.kuali.rice.kns.util.TypeUtils;
 70  
 
 71  
 
 72  
 /**
 73  
  *
 74  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 75  
  */
 76  
 public class StandardDocumentSearchGenerator implements DocumentSearchGenerator {
 77  0
     private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(StandardDocumentSearchGenerator.class);
 78  
 
 79  
     private static final String ROUTE_NODE_TABLE = "KREW_RTE_NODE_T";
 80  
     private static final String ROUTE_NODE_INST_TABLE = "KREW_RTE_NODE_INSTN_T";
 81  
     private static final String DATABASE_WILDCARD_CHARACTER_STRING = "%";
 82  0
     private static final char DATABASE_WILDCARD_CHARACTER = DATABASE_WILDCARD_CHARACTER_STRING.toCharArray()[0];
 83  
 
 84  
     private static final String CREATE_DATE_FIELD_STRING = " DOC_HDR.CRTE_DT ";
 85  
     private static final String APPROVE_DATE_FIELD_STRING = " DOC_HDR.APRV_DT ";
 86  
     private static final String FINALIZATION_DATE_FIELD_STRING = " DOC_HDR.FNL_DT ";
 87  
     private static final String LAST_STATUS_UPDATE_DATE = " DOC_HDR.STAT_MDFN_DT ";
 88  
     private static final String STATUS_TRANSITION_DATE_FIELD_STRING = " STAT_TRAN.STAT_TRANS_DATE ";
 89  
 
 90  
     private static List<SearchableAttribute> searchableAttributes;
 91  
     private static DocSearchCriteriaDTO criteria;
 92  
     private static String searchingUser;
 93  
 
 94  0
     private boolean isProcessResultSet = true;
 95  
 
 96  
     private DatabasePlatform dbPlatform;
 97  
     private MessageMap messageMap;
 98  
 
 99  0
     private SqlBuilder sqlBuilder = null;
 100  
 
 101  
     public StandardDocumentSearchGenerator() {
 102  0
         super();
 103  0
         searchableAttributes = new ArrayList<SearchableAttribute>();
 104  0
     }
 105  
 
 106  
     /**
 107  
      * @param searchableAttributes in a list
 108  
      */
 109  
     public StandardDocumentSearchGenerator(List<SearchableAttribute> searchableAttributes) {
 110  0
         this();
 111  0
         StandardDocumentSearchGenerator.searchableAttributes = searchableAttributes;
 112  0
     }
 113  
 
 114  
     public DocSearchCriteriaDTO getCriteria() {
 115  0
         return criteria;
 116  
     }
 117  
 
 118  
     public void setCriteria(DocSearchCriteriaDTO criteria) {
 119  0
         StandardDocumentSearchGenerator.criteria = criteria;
 120  0
     }
 121  
 
 122  
     public List<SearchableAttribute> getSearchableAttributes() {
 123  0
         return searchableAttributes;
 124  
     }
 125  
 
 126  
     public void setSearchableAttributes(List<SearchableAttribute> searchableAttributes) {
 127  0
         this.searchableAttributes = searchableAttributes;
 128  0
     }
 129  
 
 130  
     public String getSearchingUser() {
 131  0
         return searchingUser;
 132  
     }
 133  
 
 134  
     public void setSearchingUser(String searchingUser) {
 135  0
         StandardDocumentSearchGenerator.searchingUser = searchingUser;
 136  0
     }
 137  
 
 138  
     public DocSearchCriteriaDTO clearSearch(DocSearchCriteriaDTO searchCriteria) {
 139  0
         return new DocSearchCriteriaDTO();
 140  
     }
 141  
 
 142  
     public List<WorkflowServiceError> performPreSearchConditions(String principalId, DocSearchCriteriaDTO searchCriteria) {
 143  0
         setCriteria(searchCriteria);
 144  0
         return new ArrayList<WorkflowServiceError>();
 145  
     }
 146  
 
 147  
     public SearchAttributeCriteriaComponent getSearchableAttributeByFieldName(String name) {
 148  0
         if (StringUtils.isBlank(name)) {
 149  0
             throw new IllegalArgumentException("Attempted to find Searchable Attribute with blank Field name '" + name + "'");
 150  
         }
 151  0
         for (SearchAttributeCriteriaComponent critComponent : getCriteria().getSearchableAttributes())
 152  
         {
 153  
 
 154  0
             if (name.equals(critComponent.getFormKey()))
 155  
             {
 156  0
                 return critComponent;
 157  
             }
 158  
         }
 159  0
         return null;
 160  
     }
 161  
 
 162  
     public void addErrorMessageToList(List<WorkflowServiceError> errors, String message) {
 163  0
         errors.add(new WorkflowServiceErrorImpl(message,"general.message",message));
 164  0
     }
 165  
 
 166  
     /* (non-Javadoc)
 167  
      * @see org.kuali.rice.kew.docsearch.DocumentSearchGenerator#executeSearch(org.kuali.rice.kew.docsearch.DocSearchCriteriaDTO, org.kuali.rice.core.database.platform.DatabasePlatform)
 168  
      */
 169  
     public String generateSearchSql(DocSearchCriteriaDTO searchCriteria) {
 170  0
         setCriteria(searchCriteria);
 171  0
         return getDocSearchSQL();
 172  
     }
 173  
 
 174  
     public DocumentType getValidDocumentType(String documentTypeFullName) {
 175  0
         if (!Utilities.isEmpty(documentTypeFullName)) {
 176  0
             DocumentType documentType = KEWServiceLocator.getDocumentTypeService().findByName(documentTypeFullName);
 177  0
             if (documentType == null) {
 178  0
                 throw new RuntimeException("No Valid Document Type Found for document type name '" + documentTypeFullName + "'");
 179  
             }
 180  0
             return documentType;
 181  
         }
 182  0
         return null;
 183  
     }
 184  
 
 185  
     /* (non-Javadoc)
 186  
      * @see org.kuali.rice.kew.docsearch.DocumentSearchGenerator#validateSearchableAttributes()
 187  
      */
 188  
     public List<WorkflowServiceError> validateSearchableAttributes(DocSearchCriteriaDTO searchCriteria) {
 189  0
         setCriteria(searchCriteria);
 190  0
         List<WorkflowServiceError> errors = new ArrayList<WorkflowServiceError>();
 191  0
         List<SearchAttributeCriteriaComponent> searchableAttributes = criteria.getSearchableAttributes();
 192  0
         if (searchableAttributes != null && !searchableAttributes.isEmpty()) {
 193  0
             Map<String, Object> paramMap = new HashMap<String, Object>();
 194  0
             for (SearchAttributeCriteriaComponent component : searchableAttributes) {
 195  0
                 if (!Utilities.isEmpty(component.getValues())) {
 196  0
                     paramMap.put(component.getFormKey(),component.getValues());
 197  
                 } else {
 198  0
                     paramMap.put(component.getFormKey(),component.getValue());
 199  
                 }
 200  
             }
 201  0
             DocumentType documentType = getValidDocumentType(criteria.getDocTypeFullName());
 202  
             try {
 203  0
                 for (SearchableAttribute searchableAttribute : documentType.getSearchableAttributes()) {
 204  0
                     List<WorkflowAttributeValidationError> searchableErrors = validateSearchableAttribute(
 205  
                             searchableAttribute, paramMap, DocSearchUtils.getDocumentSearchContext("", documentType.getName(), ""));
 206  0
                     if(!Utilities.isEmpty(searchableErrors)){
 207  0
                         for (WorkflowAttributeValidationError error : searchableErrors) {
 208  0
                                     if (error.getMessageMap() != null && error.getMessageMap().hasErrors()) {
 209  
                                             // In order to pass the map along we've added a member to the WorkflowServiceErrorImpl
 210  0
                                             errors.add(new WorkflowServiceErrorImpl(error.getKey(), "routetemplate.xmlattribute.error", 
 211  
                                                             error.getMessage(), null, error.getMessageMap()));
 212  
                                     } else {
 213  0
                             errors.add(new WorkflowServiceErrorImpl(error.getKey(), "routetemplate.xmlattribute.error", error.getMessage()));
 214  
                         }
 215  
                     }
 216  
                 }
 217  0
                     }
 218  0
             } catch (Exception e) {
 219  0
                 LOG.error("error finding searchable attribute in when validating document search criteria.", e);
 220  0
             }
 221  
         }
 222  0
         return errors;
 223  
     }
 224  
 
 225  
     public List<WorkflowAttributeValidationError> validateSearchableAttribute(
 226  
             SearchableAttribute searchableAttribute, Map searchAttributesParameterMap, DocumentSearchContext documentSearchContext) {
 227  0
         return searchableAttribute.validateUserSearchInputs(searchAttributesParameterMap, documentSearchContext);
 228  
     }
 229  
 
 230  
     private Class getSearchableAttributeClass(SearchableAttributeValue sav){
 231  0
         if(sav instanceof SearchableAttributeDateTimeValue){
 232  0
             return Timestamp.class;
 233  0
         }else if(sav instanceof SearchableAttributeFloatValue){
 234  0
             return Float.TYPE;
 235  0
         }else if(sav instanceof SearchableAttributeLongValue){
 236  0
             return Long.TYPE;
 237  0
         }else if(sav instanceof SearchableAttributeStringValue){
 238  0
             return String.class;
 239  
         }else{
 240  0
             return null;
 241  
         }
 242  
     }
 243  
 
 244  
     /**
 245  
      * Cleans upper bounds on an entire list of values.
 246  
      * @param stringDates list
 247  
      * @return list of dates
 248  
      */
 249  
     private static List<String> cleanUpperBounds(List<String> stringDates) {
 250  0
         List<String> lRet = null;
 251  0
         if(stringDates != null && !stringDates.isEmpty()){
 252  0
             lRet = new ArrayList<String>();
 253  0
             for(String stringDate:stringDates){
 254  0
                 lRet.add(cleanUpperBound(stringDate));
 255  
             }
 256  
         }
 257  0
         return lRet;
 258  
     }
 259  
     
 260  
     /**
 261  
      * When dealing with upperbound dates, it is a business requirement that if a timestamp isn't already
 262  
      * stated append 23:59:59 to the end of the date.  This ensures that you are searching for the entire
 263  
      * day.
 264  
      * @param stringDate
 265  
      * @return upper bound date
 266  
      */
 267  
     private static String cleanUpperBound(String stringDate){
 268  
         try{
 269  0
             java.sql.Timestamp dt = KNSServiceLocator.getDateTimeService().convertToSqlTimestamp(stringDate);
 270  0
             SimpleDateFormat sdfTime = new SimpleDateFormat("HH:mm:ss");
 271  
 
 272  0
             if("00:00:00".equals(sdfTime.format(dt))){
 273  0
                 stringDate = stringDate + " 23:59:59";
 274  
             }
 275  0
         } catch (Exception ex){
 276  0
             GlobalVariables.getMessageMap().putError(KNSConstants.DOCUMENT_ERRORS, RiceKeyConstants.ERROR_CUSTOM, new String[] { "Invalid Date Input: " + stringDate });
 277  0
         }
 278  0
         return stringDate;
 279  
     }
 280  
 
 281  
 
 282  
     /**
 283  
      *
 284  
      * This method takes in any valid date string, like <12/30/09 and convert
 285  
      * it into <12/30/09 23:59:59, but only for upper bound type values.
 286  
      *
 287  
      *  This method only really cares about .., <, <=. other operators
 288  
      *  are not evaluated.
 289  
      *
 290  
      * In order to do this it has to parse the inline date string
 291  
      *
 292  
      * @param propertyValue
 293  
      * @return
 294  
      */
 295  
     private static String parseAndConvertDateToRange(String propertyValue) {
 296  
 
 297  0
         String sRet = propertyValue;
 298  
 
 299  0
         if (StringUtils.contains(propertyValue, KNSConstants.BETWEEN_OPERATOR)) {
 300  0
             String[] rangeValues = propertyValue.split("\\.\\."); // this translate to the .. operator
 301  0
             sRet = ObjectUtils.clean(rangeValues[0].trim())+ " .. " + cleanUpperBound(ObjectUtils.clean(rangeValues[1].trim()));
 302  0
         }  else if (propertyValue.startsWith("<=")) {
 303  0
             sRet = "<=" + cleanUpperBound(ObjectUtils.clean(propertyValue));
 304  0
         }  else if (propertyValue.startsWith("<")) {
 305  0
             sRet = "<" + cleanUpperBound(ObjectUtils.clean(propertyValue));
 306  
         }
 307  
 
 308  0
         return sRet;
 309  
     }
 310  
 
 311  
 
 312  
 
 313  
     /**
 314  
      *
 315  
      * This method is intended to validate that the lower bound value is <= the upper bound value.  Although having a lower
 316  
      * value would not hurt the actual db query.  Only relevant to be called when you are dealing with Range Searchable Attributes
 317  
      *
 318  
      * If an error is found, it will throw a RuntimeException.
 319  
      *
 320  
      * @param lowerBound
 321  
      * @param upperBound
 322  
      */
 323  
     private void validateBounds(SearchAttributeCriteriaComponent lowerBound, SearchAttributeCriteriaComponent upperBound){
 324  
 
 325  0
         Class type = getSearchableAttributeClass(lowerBound.getSearchableAttributeValue());
 326  0
         Class upperType = getSearchableAttributeClass(upperBound.getSearchableAttributeValue());
 327  
 
 328  
         // Make sure they are of the same data type
 329  0
         if(type.getName().compareTo(upperType.getName()) != 0){
 330  0
             String err = "Type Mismatch. Must compare two like types";
 331  0
             LOG.error("validateBounds() " + err);
 332  0
             throw new RuntimeException(err);
 333  
         }
 334  
 
 335  0
         String errorMsg = "The search attribute range values are out of order. Lower bound must be <= Upper Bound. ["
 336  
             + lowerBound.getValue() + ", " + upperBound.getValue() + "] for type " + type.getName();
 337  
 
 338  0
         if(TypeUtils.isIntegralClass(type) || TypeUtils.isDecimalClass(type)){
 339  
             // The clean numeric will work with both integer and float values
 340  0
             BigDecimal lVal = SqlBuilder.stringToBigDecimal(lowerBound.getValue());
 341  0
             BigDecimal uVal = SqlBuilder.stringToBigDecimal(upperBound.getValue());
 342  
 
 343  0
             if(lVal.compareTo(uVal) > 0){
 344  0
                 LOG.error("validateBounds() " + errorMsg);
 345  0
                 throw new RuntimeException(errorMsg);
 346  
             }
 347  
 
 348  0
         }else if(TypeUtils.isTemporalClass(type)){
 349  0
             java.sql.Timestamp lVal = null;
 350  0
             java.sql.Timestamp uVal = null;
 351  
             try{
 352  0
                 lVal = KNSServiceLocator.getDateTimeService().convertToSqlTimestamp(lowerBound.getValue());
 353  0
                 uVal = KNSServiceLocator.getDateTimeService().convertToSqlTimestamp(upperBound.getValue());
 354  0
             }catch(Exception ex){
 355  0
                 LOG.error("validateBounds() " + errorMsg);
 356  0
                 throw new RuntimeException(errorMsg, ex);
 357  0
             }
 358  
 
 359  0
             if(lVal.compareTo(uVal) > 0){
 360  0
                 LOG.error("validateBounds() " + errorMsg);
 361  0
                 throw new RuntimeException(errorMsg);
 362  
             }
 363  
 
 364  0
         }else if(TypeUtils.isStringClass(type)){
 365  
             // this is a complete edge case that should not be allowed to happen but
 366  
             // the XSD states that it's valid.
 367  0
             if(lowerBound.isCaseSensitive() != upperBound.isCaseSensitive()){
 368  0
                 LOG.warn("validateBounds(): Cannot Validate because mismatch case sensitivity ["
 369  
                         + lowerBound.getValue() + ", " + upperBound.getValue() + "] for type " + type.getName());
 370  0
             }else if(lowerBound.isCaseSensitive()){
 371  0
                 if(lowerBound.getValue().compareTo(upperBound.getValue()) > 0){
 372  0
                     LOG.error("validateBounds() " + errorMsg);
 373  0
                     throw new RuntimeException(errorMsg);
 374  
                 }
 375  
             }else{
 376  0
                 if(lowerBound.getValue().compareToIgnoreCase(upperBound.getValue()) > 0){
 377  0
                     LOG.error("validateBounds() " + errorMsg);
 378  0
                     throw new RuntimeException(errorMsg);
 379  
                 }
 380  
             }
 381  
         }
 382  0
     }
 383  
 
 384  
     /**
 385  
      *
 386  
      * This method takes in a list of searchable attributes and pulls out range componets, combines them
 387  
      * and adds them back into the original list.
 388  
      *
 389  
      *  for any upperbound that does not have a timestamp, add one that's 23:59:59
 390  
      *
 391  
      *  for non range attributes it checks to see if it's a standard no time date and converts
 392  
      *  it to a "between".
 393  
      *
 394  
      * so sa list: a, b, cR1, d, e, cR2
 395  
      * returns: a, b, cR,
 396  
      * @param searchableAttributes the contents of this list can be altered
 397  
      *
 398  
      */
 399  
     private void combineAndFormatSearchableComponents(
 400  
             List<SearchAttributeCriteriaComponent> searchableAttributes) {
 401  
 
 402  0
         Map<String, List<SearchAttributeCriteriaComponent>> searchableAttributeRangeComponents = new HashMap<String, List<SearchAttributeCriteriaComponent>>();
 403  
 
 404  0
         for (SearchAttributeCriteriaComponent criteriaComponent : searchableAttributes)
 405  
         {
 406  0
             if (!criteriaComponent.isSearchable())
 407  
             {
 408  0
                 continue;
 409  
             }
 410  
 
 411  0
             SearchableAttributeValue searchAttribute = criteriaComponent.getSearchableAttributeValue();
 412  0
             if (searchAttribute == null)
 413  
             {
 414  
                 // key given for propertyField must not be on document
 415  0
                 String errorMsg = "The search attribute value associated with key '"
 416  
                         + criteriaComponent.getSavedKey() + "' cannot be found";
 417  0
                 LOG.error("getSearchableAttributeSql() " + errorMsg);
 418  0
                 throw new RuntimeException(errorMsg);
 419  
             }
 420  
 
 421  0
             Class clazz = getSearchableAttributeClass(searchAttribute);
 422  
 
 423  0
             if (criteriaComponent.isRangeSearch())
 424  
             {
 425  
 
 426  0
                 if (searchableAttributeRangeComponents.containsKey(criteriaComponent.getSavedKey()))
 427  
                 {
 428  0
                     List<SearchAttributeCriteriaComponent> criteriaComponents = searchableAttributeRangeComponents.get(criteriaComponent.getSavedKey());
 429  0
                     List<SearchAttributeCriteriaComponent> newCriteriaComponents = new ArrayList<SearchAttributeCriteriaComponent>();
 430  0
                     newCriteriaComponents.addAll(criteriaComponents);
 431  0
                     newCriteriaComponents.add(criteriaComponent);
 432  0
                     searchableAttributeRangeComponents.put(criteriaComponent.getSavedKey(), newCriteriaComponents);
 433  0
                 } else
 434  
                 {
 435  0
                     searchableAttributeRangeComponents.put(criteriaComponent.getSavedKey(),
 436  
                             Arrays.asList(criteriaComponent)
 437  
                     );
 438  
                 }
 439  
                 // we need to make sure the dates are converted based on case.
 440  
                 // for upperbound
 441  0
                 if (TypeUtils.isTemporalClass(clazz) && criteriaComponent.isComponentUpperBoundValue())
 442  
                 {
 443  0
                     criteriaComponent.setValue(cleanUpperBound(criteriaComponent.getValue()));
 444  0
                     criteriaComponent.setValues(cleanUpperBounds(criteriaComponent.getValues()));
 445  
                 }
 446  
 
 447  
             } else
 448  
             {
 449  0
                 if (TypeUtils.isTemporalClass(clazz))
 450  
                 {
 451  0
                     criteriaComponent.setValue(criteriaComponent.getValue());
 452  
                 }
 453  
             }
 454  0
         }
 455  
 
 456  
         // we combined the attributes above into a map of lists. Now for each
 457  
         // key, make one SA.
 458  0
         for (String keyName : searchableAttributeRangeComponents.keySet()) {
 459  0
             List<SearchAttributeCriteriaComponent> criteriaComponents = searchableAttributeRangeComponents
 460  
                     .get(keyName);
 461  
 
 462  0
             SearchAttributeCriteriaComponent newComp = null;
 463  0
             SearchAttributeCriteriaComponent lowerBound = null;
 464  0
             SearchAttributeCriteriaComponent upperBound = null;
 465  
 
 466  0
             for (SearchAttributeCriteriaComponent component : criteriaComponents) {
 467  0
                 if (component.isComponentLowerBoundValue()) {
 468  0
                     lowerBound = component;
 469  0
                 } else if (component.isComponentUpperBoundValue()) {
 470  0
                     upperBound = component;
 471  
                 } else {
 472  0
                     String errorMsg = "The search attribute value associated with key '"
 473  
                             + component.getSavedKey()
 474  
                             + "' is not upper or lower bound";
 475  0
                     LOG.error("getSearchableAttributeSql() " + errorMsg);
 476  0
                     throw new RuntimeException(errorMsg);
 477  
                 }
 478  
             }
 479  
 
 480  
             // now we have both the upper and lower if they exist. lets make a
 481  
             // new single component.
 482  0
             if (lowerBound != null && upperBound != null) { // between case
 483  
 
 484  
                 // we need to check and make sure a < b for range values
 485  0
                 validateBounds(lowerBound, upperBound);
 486  
 
 487  
                 // we need to do this if the search is NOT inclusive. if
 488  
                 // that's the case then
 489  
                 // the between operator does not work.
 490  
 
 491  0
                 lowerBound.setRangeSearch(false);
 492  0
                 upperBound.setRangeSearch(false);
 493  0
                 if (lowerBound.isSearchInclusive()) {
 494  0
                     lowerBound.setValue(">=" + lowerBound.getValue());
 495  
                 } else {
 496  0
                     lowerBound.setValue(">" + lowerBound.getValue());
 497  
                 }
 498  0
                 if (upperBound.isSearchInclusive()) {
 499  0
                     upperBound.setValue("<=" + upperBound.getValue());
 500  
                 } else {
 501  0
                     upperBound.setValue("<" + upperBound.getValue());
 502  
                 }
 503  
 
 504  0
             } else if (lowerBound != null) {
 505  0
                 newComp = new SearchAttributeCriteriaComponent(lowerBound
 506  
                         .getFormKey(), null, false);
 507  0
                 if (lowerBound.isSearchInclusive()) {
 508  0
                     newComp.setValue(">=" + lowerBound.getValue());
 509  
                 } else {
 510  0
                     newComp.setValue(">" + lowerBound.getValue());
 511  
                 }
 512  0
                 newComp.setSearchInclusive(lowerBound.isSearchInclusive());
 513  0
                 newComp.setCaseSensitive(lowerBound.isCaseSensitive());
 514  0
                 newComp.setAllowInlineRange(lowerBound.isAllowInlineRange());
 515  0
                 newComp.setCanHoldMultipleValues(lowerBound
 516  
                         .isCanHoldMultipleValues());
 517  0
                 newComp.setLookupableFieldType(lowerBound
 518  
                         .getLookupableFieldType());
 519  0
                 newComp.setSearchable(true);
 520  0
                 newComp.setSearchableAttributeValue(lowerBound
 521  
                         .getSearchableAttributeValue());
 522  0
                 newComp.setSavedKey(lowerBound.getSavedKey());
 523  0
                 searchableAttributes.add(newComp);
 524  0
             } else if (upperBound != null) {
 525  0
                 newComp = new SearchAttributeCriteriaComponent(upperBound
 526  
                         .getFormKey(), null, false);
 527  0
                 if (upperBound.isSearchInclusive()) {
 528  0
                     newComp.setValue("<=" + upperBound.getValue());
 529  
                 } else {
 530  0
                     newComp.setValue("<" + upperBound.getValue());
 531  
                 }
 532  0
                 newComp.setSearchInclusive(upperBound.isSearchInclusive());
 533  0
                 newComp.setCaseSensitive(upperBound.isCaseSensitive());
 534  0
                 newComp.setAllowInlineRange(upperBound.isAllowInlineRange());
 535  0
                 newComp.setCanHoldMultipleValues(upperBound.isCanHoldMultipleValues());
 536  0
                 newComp.setLookupableFieldType(upperBound.getLookupableFieldType());
 537  0
                 newComp.setSearchable(true);
 538  0
                 newComp.setSearchableAttributeValue(upperBound.getSearchableAttributeValue());
 539  0
                 newComp.setSavedKey(upperBound.getSavedKey());
 540  0
                 searchableAttributes.add(newComp);
 541  
             }
 542  
 
 543  0
         }
 544  
 
 545  
         // last step is to remove all range items from the list because we have
 546  
         // just combined them into single elements
 547  0
         for (Iterator<SearchAttributeCriteriaComponent> iterator = searchableAttributes.iterator(); iterator
 548  0
                 .hasNext();) {
 549  0
             SearchAttributeCriteriaComponent criteriaComponent = (SearchAttributeCriteriaComponent) iterator
 550  
                     .next();
 551  0
             if (!criteriaComponent.isSearchable()) {
 552  0
                 continue;
 553  
             }
 554  
 
 555  0
             if (criteriaComponent.isRangeSearch()) {
 556  0
                 iterator.remove();
 557  
             }
 558  
 
 559  0
         }
 560  0
     }
 561  
 
 562  
     public QueryComponent getSearchableAttributeSql(List<SearchAttributeCriteriaComponent> searchableAttributes, String whereClausePredicatePrefix) {
 563  
         /*
 564  
          * This method still isn't complete. It now is a hybrid of the old and new way of generating the sql.
 565  
          * It's still using the old way to build the select and from parts of the statement, with the new part generating
 566  
          * the where.  The new way allows for operators and should clear up a lot of the date issues.
 567  
          */
 568  
 
 569  
         // this will massage the data and change all range attributes into std ones.
 570  0
         combineAndFormatSearchableComponents(searchableAttributes);
 571  
 
 572  0
         StringBuffer fromSql = new StringBuffer();
 573  0
         StringBuffer whereSql = new StringBuffer();
 574  
 
 575  0
         int tableIndex = 1;
 576  0
         String tableAlias = "EXT" + tableIndex;
 577  
 
 578  0
         Map<String, List<SearchAttributeCriteriaComponent>> searchableAttributeRangeComponents = new HashMap<String,List<SearchAttributeCriteriaComponent>>();
 579  0
         org.kuali.rice.core.jdbc.criteria.Criteria finalCriteria = null;
 580  
 
 581  0
         for (Iterator<SearchAttributeCriteriaComponent> iterator = searchableAttributes.iterator(); iterator.hasNext(); tableIndex++) {
 582  0
             SearchAttributeCriteriaComponent criteriaComponent = iterator.next();
 583  0
             if (!criteriaComponent.isSearchable()) {
 584  0
                 continue;
 585  
             }
 586  
 
 587  0
             SqlBuilder sqlBuild = this.getSqlBuilder();
 588  
 
 589  0
             SearchableAttributeValue searchAttribute = criteriaComponent.getSearchableAttributeValue();
 590  0
             if (searchAttribute == null) {
 591  
                 // key given for propertyField must not be on document
 592  0
                 String errorMsg = "The search attribute value associated with key '" + criteriaComponent.getSavedKey() + "' cannot be found";
 593  0
                 LOG.error("getSearchableAttributeSql() " + errorMsg);
 594  0
                 throw new RuntimeException(errorMsg);
 595  
             }
 596  
 
 597  0
             tableAlias = "EXT" + tableIndex;
 598  
 
 599  0
             Class c = getSearchableAttributeClass(searchAttribute);
 600  
 
 601  0
             boolean addCaseInsensitivityForValue = (!criteriaComponent.isCaseSensitive()) && criteriaComponent.getSearchableAttributeValue().allowsCaseInsensitivity();
 602  
 
 603  0
             Criteria crit = null;
 604  0
             List<String> searchValues = criteriaComponent.getValues();
 605  0
             if (searchValues != null && !searchValues.isEmpty()) {
 606  0
                 crit = new Criteria(searchAttribute.getAttributeTableName(), tableAlias);
 607  0
                 crit.setDbPlatform(sqlBuild.getDbPlatform());
 608  0
                 crit.in("VAL", criteriaComponent.getValues(), c);
 609  
             } else {
 610  0
                 crit = sqlBuild.createCriteria("VAL", criteriaComponent.getValue() , searchAttribute.getAttributeTableName(), tableAlias, c, addCaseInsensitivityForValue, searchAttribute.allowsWildcards());
 611  
             }
 612  0
             sqlBuild.addCriteria("KEY_CD", criteriaComponent.getSavedKey(), String.class, false, false, crit); // this is always of type string.
 613  0
             sqlBuild.andCriteria("DOC_HDR_ID", tableAlias + ".DOC_HDR_ID", "KREW_DOC_HDR_T", "DOC_HDR", TypeUtils.JoinType.class, false, false, crit);
 614  
 
 615  0
             if(finalCriteria == null ){
 616  0
                 finalCriteria = crit;
 617  
             }else{
 618  0
                 sqlBuild.andCriteria(finalCriteria, crit);
 619  
             }
 620  
 
 621  
             // - below is the old code
 622  
             // if where clause is empty then use passed in prefix... otherwise generate one
 623  0
             String whereClausePrefix = (whereSql.length() == 0) ? whereClausePredicatePrefix : getGeneratedPredicatePrefix(whereSql.length());
 624  0
             QueryComponent qc = generateSearchableAttributeSql(criteriaComponent, whereClausePrefix, tableIndex);
 625  0
             fromSql.append(qc.getFromSql());
 626  
 
 627  
         }
 628  
 
 629  0
         for (String keyName : searchableAttributeRangeComponents.keySet()) {
 630  0
             List<SearchAttributeCriteriaComponent> criteriaComponents = searchableAttributeRangeComponents.get(keyName);
 631  
             // if where clause is empty then use passed in prefix... otherwise generate one
 632  0
             String whereClausePrefix = (whereSql.length() == 0) ? whereClausePredicatePrefix : getGeneratedPredicatePrefix(whereSql.length());
 633  0
             QueryComponent qc = generateSearchableAttributeRangeSql(keyName, criteriaComponents, whereClausePrefix, tableIndex);
 634  0
             fromSql.append(qc.getFromSql());
 635  0
         }
 636  
         
 637  0
         if (finalCriteria == null) {
 638  0
             return new QueryComponent("", "", "");
 639  
         }
 640  
 
 641  0
         String whereClausePrefix = (whereSql.length() == 0) ? whereClausePredicatePrefix : getGeneratedPredicatePrefix(whereSql.length());
 642  
 
 643  0
         return new QueryComponent("",fromSql.toString(),whereClausePrefix + " "+ finalCriteria.buildWhere());
 644  
     }
 645  
 
 646  
     public QueryComponent generateSearchableAttributeSql(SearchAttributeCriteriaComponent criteriaComponent,String whereSqlStarter,int tableIndex) {
 647  0
         String tableIdentifier = "EXT" + tableIndex;
 648  0
         String queryTableColumnName = tableIdentifier + ".VAL";
 649  0
         QueryComponent joinSqlComponent = getSearchableAttributeJoinSql(criteriaComponent.getSearchableAttributeValue(), tableIdentifier, whereSqlStarter, criteriaComponent.getSavedKey());
 650  0
         StringBuffer fromSql = new StringBuffer(joinSqlComponent.getFromSql());
 651  0
         StringBuffer whereSql = new StringBuffer(joinSqlComponent.getWhereSql());
 652  
 
 653  
         // removed because we pull the where from somewhere else now.
 654  
         //whereSql.append(generateSearchableAttributeDefaultWhereSql(criteriaComponent, queryTableColumnName));
 655  
 
 656  0
         return new QueryComponent("",fromSql.toString(),whereSql.toString());
 657  
     }
 658  
 
 659  
     public QueryComponent generateSearchableAttributeRangeSql(String searchAttributeKeyName, List<SearchAttributeCriteriaComponent> criteriaComponents,String whereSqlStarter,int tableIndex) {
 660  0
         StringBuffer fromSql = new StringBuffer();
 661  0
         StringBuffer whereSql = new StringBuffer();
 662  0
         boolean joinAlreadyPerformed = false;
 663  0
         String tableIdentifier = "EXT" + tableIndex;
 664  0
         String queryTableColumnName = tableIdentifier + ".VAL";
 665  
 
 666  0
         for (SearchAttributeCriteriaComponent criteriaComponent : criteriaComponents) {
 667  0
             if (!searchAttributeKeyName.equals(criteriaComponent.getSavedKey())) {
 668  0
                 String errorMsg = "Key value of searchable attribute component with savedKey '" + criteriaComponent.getSavedKey() + "' does not match specified savedKey value '" + searchAttributeKeyName + "'";
 669  0
                 LOG.error("generateSearchableAttributeRangeSql() " + errorMsg);
 670  0
                 throw new RuntimeException(errorMsg);
 671  
             }
 672  0
             if (!joinAlreadyPerformed) {
 673  0
                 QueryComponent joinSqlComponent = getSearchableAttributeJoinSql(criteriaComponent.getSearchableAttributeValue(), tableIdentifier, whereSqlStarter, searchAttributeKeyName);
 674  0
                 fromSql.append(joinSqlComponent.getFromSql());
 675  0
                 whereSql.append(joinSqlComponent.getWhereSql());
 676  0
                 joinAlreadyPerformed = true;
 677  
             }
 678  0
             whereSql.append(generateSearchableAttributeDefaultWhereSql(criteriaComponent, queryTableColumnName));
 679  
         }
 680  
 
 681  0
         return new QueryComponent("",fromSql.toString(),whereSql.toString());
 682  
     }
 683  
 
 684  
     public StringBuilder generateSearchableAttributeDefaultWhereSql(SearchAttributeCriteriaComponent criteriaComponent,String queryTableColumnName) {
 685  0
         StringBuilder whereSql = new StringBuilder();
 686  0
         String initialClauseStarter = "and";
 687  
 //        whereSql.append(" " + initialClauseStarter + " ");
 688  
 
 689  0
         boolean valueIsDate = (criteriaComponent.getSearchableAttributeValue() instanceof SearchableAttributeDateTimeValue);
 690  0
         boolean valueIsString = (criteriaComponent.getSearchableAttributeValue() instanceof SearchableAttributeStringValue);
 691  0
         boolean valueIsLong = (criteriaComponent.getSearchableAttributeValue() instanceof SearchableAttributeLongValue);
 692  0
         boolean valueIsFloat = (criteriaComponent.getSearchableAttributeValue() instanceof SearchableAttributeFloatValue);
 693  0
         boolean addCaseInsensitivityForValue = (!criteriaComponent.isCaseSensitive()) && criteriaComponent.getSearchableAttributeValue().allowsCaseInsensitivity();
 694  0
         String attributeValueSearched = criteriaComponent.getValue();
 695  0
         List<String> attributeValuesSearched = criteriaComponent.getValues();
 696  
 
 697  0
         StringBuilder whereSqlTemp = new StringBuilder();
 698  0
         if (valueIsDate) {
 699  0
             if (criteriaComponent.isRangeSearch()) {
 700  
                 // for a range search just add the criteria
 701  0
                 whereSqlTemp.append(constructWhereClauseDateElement(initialClauseStarter, queryTableColumnName, criteriaComponent.isSearchInclusive(), criteriaComponent.isComponentLowerBoundValue(), attributeValueSearched));
 702  0
             } else if(criteriaComponent.isAllowInlineRange()) {
 703  0
                 whereSqlTemp.append(constructWhereClauseDateElement(initialClauseStarter, queryTableColumnName, criteriaComponent.isSearchInclusive(), false, attributeValueSearched, criteriaComponent.isAllowInlineRange()));
 704  
             } else {
 705  0
                 if (!Utilities.isEmpty(attributeValuesSearched)) {
 706  
                     // for a multivalue date search we need multiple ranges entered
 707  0
                     whereSqlTemp.append(initialClauseStarter).append(" (");
 708  0
                     boolean firstValue = true;
 709  0
                     for (String attributeValueEntered : attributeValuesSearched) {
 710  0
                         whereSqlTemp.append(" ( ");
 711  0
                         whereSqlTemp.append(constructWhereClauseDateElement("", queryTableColumnName, criteriaComponent.isSearchInclusive(), true, attributeValueEntered));
 712  0
                         whereSqlTemp.append(constructWhereClauseDateElement("and", queryTableColumnName, criteriaComponent.isSearchInclusive(), false, attributeValueEntered));
 713  0
                         whereSqlTemp.append(" ) ");
 714  0
                         String separator = " or ";
 715  0
                         if (firstValue) {
 716  0
                             firstValue = false;
 717  0
                             separator = "";
 718  
                         }
 719  0
                         whereSqlTemp.append(separator);
 720  0
                     }
 721  0
                     whereSqlTemp.append(") ");
 722  0
                 } else {
 723  
                     // below is a search for a single date field.... must do a range of 'time' so we can find any value regardless of the time associated with it
 724  0
                     whereSqlTemp.append(constructWhereClauseDateElement(initialClauseStarter, queryTableColumnName, criteriaComponent.isSearchInclusive(), true, attributeValueSearched));
 725  0
                     whereSqlTemp.append(constructWhereClauseDateElement(initialClauseStarter, queryTableColumnName, criteriaComponent.isSearchInclusive(), false, attributeValueSearched));
 726  
                 }
 727  
             }
 728  
         } else {
 729  0
             boolean usingWildcards = false;
 730  0
             StringBuffer prefix = new StringBuffer("");
 731  0
             StringBuffer suffix = new StringBuffer("");
 732  0
             if (valueIsString) {
 733  0
                 prefix.append("'");
 734  0
                 suffix.insert(0,"'");
 735  
             }
 736  
             // apply wildcarding if wildcard character is specified
 737  
             // after conversion of doc search to lookup, wildcards are always allowed
 738  0
             if (!Utilities.isEmpty(attributeValuesSearched)) {
 739  0
                 List<String> newList = new ArrayList<String>();
 740  0
                 for (String attributeValueEntered : attributeValuesSearched) {
 741  0
                     newList.add(attributeValueEntered.trim().replace('*', DATABASE_WILDCARD_CHARACTER));
 742  0
                     usingWildcards |= (attributeValueEntered.contains(DATABASE_WILDCARD_CHARACTER_STRING));
 743  
                 }
 744  0
                 attributeValuesSearched = newList;
 745  0
             } else {
 746  0
                 attributeValueSearched = attributeValueSearched.trim().replace('*', DATABASE_WILDCARD_CHARACTER);
 747  0
                 usingWildcards |= (attributeValueSearched.indexOf(DATABASE_WILDCARD_CHARACTER_STRING) != -1);
 748  
             }
 749  0
             String prefixToUse = prefix.toString();
 750  0
             String suffixToUse = suffix.toString();
 751  
 
 752  0
             if (addCaseInsensitivityForValue) {
 753  0
                 queryTableColumnName = "upper(" + queryTableColumnName + ")";
 754  0
                 prefixToUse = "upper(" + prefix.toString();
 755  0
                 suffixToUse = suffix.toString() + ")";
 756  
             }
 757  
 
 758  0
             if (!Utilities.isEmpty(attributeValuesSearched)) {
 759  
                 // for a multivalue search we need multiple 'or' clause statements entered
 760  0
                 whereSqlTemp.append(initialClauseStarter).append(" (");
 761  0
                 boolean firstValue = true;
 762  0
                 for (String attributeValueEntered : attributeValuesSearched) {
 763  0
                     checkNumberFormattingIfNumeric(attributeValueEntered, valueIsLong, valueIsFloat);
 764  
 
 765  0
                     String separator = " or ";
 766  0
                     if (firstValue) {
 767  0
                         firstValue = false;
 768  0
                         separator = "";
 769  
                     }
 770  0
                     String sqlOperand = getSqlOperand(criteriaComponent.isRangeSearch(), criteriaComponent.isSearchInclusive(), (criteriaComponent.isRangeSearch() && criteriaComponent.isComponentLowerBoundValue()), usingWildcards);
 771  0
                     whereSqlTemp.append(constructWhereClauseElement(separator, queryTableColumnName, sqlOperand, getDbPlatform().escapeString(attributeValueEntered), prefixToUse, suffixToUse));
 772  0
                 }
 773  0
                 whereSqlTemp.append(") ");
 774  0
             } else {
 775  0
                 String sqlOperand = getSqlOperand(criteriaComponent.isRangeSearch(), criteriaComponent.isSearchInclusive(), (criteriaComponent.isRangeSearch() && criteriaComponent.isComponentLowerBoundValue()), usingWildcards);
 776  0
               if(criteriaComponent.isAllowInlineRange()) {
 777  0
                 for (String range : KNSConstants.RANGE_CHARACTERS) {
 778  0
                         int index = StringUtils.indexOf(attributeValueSearched, range);
 779  0
                     if(index != -1) {
 780  0
                             sqlOperand=range;
 781  0
                             if(!StringUtils.equals(sqlOperand, "..")) {
 782  0
                                 attributeValueSearched = StringUtils.remove(attributeValueSearched, range);
 783  
 
 784  
                             } else {
 785  0
                                 String[] rangeValues = StringUtils.split(attributeValueSearched, "..");
 786  0
                                 if(rangeValues!=null && rangeValues.length>1) {
 787  0
                                     checkNumberFormattingIfNumeric(rangeValues[0], valueIsLong, valueIsFloat);
 788  
 
 789  
                                     //append first one here and then set the second one and break
 790  0
                                     whereSqlTemp.append(constructWhereClauseElement(initialClauseStarter, queryTableColumnName, ">=", getDbPlatform().escapeString(rangeValues[0]), prefixToUse, suffixToUse));
 791  0
                                     attributeValueSearched = rangeValues[1];
 792  0
                                     sqlOperand = "<=";
 793  
                                 } else {
 794  0
                                     throw new RuntimeException("What to do here...Range search \"..\" without one element");
 795  
                                 }
 796  
                             }
 797  0
                             break;
 798  
                         }
 799  
                     }
 800  
               }
 801  0
                 checkNumberFormattingIfNumeric(attributeValueSearched, valueIsLong, valueIsFloat);
 802  0
                 whereSqlTemp.append(constructWhereClauseElement(initialClauseStarter, queryTableColumnName, sqlOperand, getDbPlatform().escapeString(attributeValueSearched), prefixToUse, suffixToUse));
 803  
             }
 804  
         }
 805  0
         whereSqlTemp.append(" ");
 806  0
         return whereSql.append(whereSqlTemp);
 807  
     }
 808  
 
 809  
     /**
 810  
      *  Checks if a particular String value is supposed to be numeric, and, if so, will throw an exception if the String is not numeric.
 811  
      *
 812  
      * @param testValue The String to test.
 813  
      * @param valueIsLong Indicates if the input value should be a Long.
 814  
      * @param valueIsFloat Indicates if the input value should be a BigDecimal.
 815  
      * @throws RiceRuntimeException if the value is supposed to be numeric, but is not.
 816  
      */
 817  
     private void checkNumberFormattingIfNumeric(String testValue, boolean valueIsLong, boolean valueIsFloat) {
 818  0
         if (valueIsLong) {
 819  0
             try { Long.parseLong(testValue.trim()); }
 820  0
             catch (Exception exc) { throw new RiceRuntimeException("Invalid number format", exc); }
 821  
         }
 822  0
         if (valueIsFloat) {
 823  0
             try { new BigDecimal(testValue.trim()); }
 824  0
             catch (Exception exc) { throw new RiceRuntimeException("Invalid number format", exc); }
 825  
         }
 826  0
     }
 827  
 
 828  
     public QueryComponent getSearchableAttributeJoinSql(SearchableAttributeValue attributeValue,String tableIdentifier,String whereSqlStarter,String attributeTableKeyColumnName) {
 829  0
         return new QueryComponent("",generateSearchableAttributeFromSql(attributeValue, tableIdentifier).toString(),generateSearchableAttributeWhereClauseJoin(whereSqlStarter, tableIdentifier, attributeTableKeyColumnName).toString());
 830  
     }
 831  
 
 832  
     public StringBuffer generateSearchableAttributeWhereClauseJoin(String whereSqlStarter,String tableIdentifier,String attributeTableKeyColumnName) {
 833  0
         StringBuffer whereSql = new StringBuffer(constructWhereClauseElement(whereSqlStarter, "DOC_HDR.DOC_HDR_ID", "=", getDbPlatform().escapeString(tableIdentifier + ".DOC_HDR_ID"), null, null));
 834  0
         whereSql.append(constructWhereClauseElement(" and ", tableIdentifier + ".KEY_CD", "=", getDbPlatform().escapeString(attributeTableKeyColumnName), "'", "'"));
 835  0
         return whereSql;
 836  
     }
 837  
 
 838  
     public StringBuffer generateSearchableAttributeFromSql(SearchableAttributeValue attributeValue,String tableIdentifier) {
 839  0
         StringBuffer fromSql = new StringBuffer();
 840  0
         String tableName = getDbPlatform().escapeString(attributeValue.getAttributeTableName());
 841  0
         if (StringUtils.isBlank(tableName)) {
 842  0
             String errorMsg = "The table name associated with Searchable Attribute with class '" + attributeValue.getClass() + "' returns as '" + tableName + "'";
 843  0
             LOG.error("getSearchableAttributeSql() " + errorMsg);
 844  0
             throw new RuntimeException(errorMsg);
 845  
         }
 846  0
         fromSql.append(" ," + tableName + " " + getDbPlatform().escapeString(tableIdentifier) + " ");
 847  0
         return fromSql;
 848  
     }
 849  
 
 850  
     public StringBuffer constructWhereClauseDateElement(String clauseStarter,String queryTableColumnName,boolean inclusive,boolean valueIsLowerBound,String dateValueToSearch) {
 851  0
         return constructWhereClauseDateElement(clauseStarter, queryTableColumnName, inclusive, valueIsLowerBound, dateValueToSearch,false);
 852  
     }
 853  
 
 854  
     public StringBuffer constructWhereClauseDateElement(String clauseStarter,String queryTableColumnName,boolean inclusive,boolean valueIsLowerBound,String dateValueToSearch, boolean isAllowInlineRange) {
 855  0
         StringBuffer whereSQLBuffer = new StringBuffer();
 856  0
         StringBuffer sqlOperand = new StringBuffer(getSqlOperand(true, inclusive, valueIsLowerBound, false));
 857  0
         String lowerTimeBound = "00:00:00";
 858  0
         String upperTimeBound = "23:59:59";
 859  
 
 860  0
         String timeValueToSearch = null;
 861  0
         if (valueIsLowerBound) {
 862  0
             timeValueToSearch = lowerTimeBound;
 863  
         } else {
 864  0
             timeValueToSearch = upperTimeBound;
 865  
         }
 866  
 
 867  0
         if(isAllowInlineRange) {
 868  0
             for (String range : KNSConstants.RANGE_CHARACTERS) {
 869  0
                 int index = StringUtils.indexOf(dateValueToSearch, range);
 870  0
                 if(index != -1) {
 871  0
                     sqlOperand=new StringBuffer(range);
 872  0
                     if(!StringUtils.equals(sqlOperand.toString(), "..")) {
 873  0
                         dateValueToSearch = StringUtils.remove(dateValueToSearch,range);
 874  0
                         if(StringUtils.equals(range, ">")) {
 875  0
                             timeValueToSearch = upperTimeBound;
 876  0
                         } else if(StringUtils.equals(range, "<")){
 877  0
                             timeValueToSearch = lowerTimeBound;
 878  
                         }
 879  
                     }  else {
 880  0
                         String[] rangeValues = StringUtils.split(dateValueToSearch, "..");
 881  0
                         if(rangeValues!=null && rangeValues.length>1) {
 882  
                             //Enhancement Idea - Could possibly use recursion here (would have to set the lower bound and inclusive variables
 883  
                             //append first one here and then set the second one and break
 884  0
                             timeValueToSearch = lowerTimeBound;
 885  0
                                                         whereSQLBuffer.append(constructWhereClauseElement(clauseStarter, queryTableColumnName, ">=", getDbPlatform().getDateSQL(getDbPlatform().escapeString(DocSearchUtils.getSqlFormattedDate(rangeValues[0].trim())), timeValueToSearch.trim()), "", ""));
 886  
 
 887  0
                             dateValueToSearch = rangeValues[1];
 888  0
                             sqlOperand = new StringBuffer("<=");
 889  0
                             timeValueToSearch = upperTimeBound;
 890  
                         } else {
 891  0
                             throw new RuntimeException("What to do here...Range search \"..\" without one element");
 892  
                         }
 893  
                     }
 894  0
                     break;
 895  
                 }
 896  
             }
 897  
         }
 898  0
                 return whereSQLBuffer.append(constructWhereClauseElement(clauseStarter, queryTableColumnName, sqlOperand.toString(), getDbPlatform().getDateSQL(getDbPlatform().escapeString(DocSearchUtils.getSqlFormattedDate(dateValueToSearch.trim())), timeValueToSearch.trim()), "", ""));
 899  
     }
 900  
 
 901  
     public StringBuffer constructWhereClauseElement(String clauseStarter,String queryTableColumnName,String operand,String valueToSearch,String valuePrefix,String valueSuffix) {
 902  0
         StringBuffer whereSql = new StringBuffer();
 903  0
         valuePrefix = (valuePrefix != null) ? valuePrefix : "";
 904  0
         valueSuffix = (valueSuffix != null) ? valueSuffix : "";
 905  0
         whereSql.append(" " + clauseStarter + " ").append(getDbPlatform().escapeString(queryTableColumnName)).append(" " + operand + " ").append(valuePrefix).append(valueToSearch).append(valueSuffix).append(" ");
 906  0
         return whereSql;
 907  
     }
 908  
 
 909  
     /**
 910  
      * For the following we first check for a ranged search because a ranged search
 911  
      * does not allow for wildcards
 912  
      */
 913  
     public String getSqlOperand(boolean rangeSearch, boolean inclusive, boolean valueIsLowerBound, boolean usingWildcards) {
 914  0
         StringBuffer sqlOperand = new StringBuffer("=");
 915  0
         if (rangeSearch) {
 916  0
             if (valueIsLowerBound) {
 917  0
                 sqlOperand = new StringBuffer(">");
 918  
             } else {
 919  0
                 sqlOperand = new StringBuffer("<");
 920  
             }
 921  0
             if (inclusive) {
 922  0
                 sqlOperand.append("=");
 923  
             }
 924  
 
 925  0
         } else if (usingWildcards) {
 926  0
             sqlOperand = new StringBuffer("like");
 927  
         }
 928  0
         return sqlOperand.toString();
 929  
     }
 930  
 
 931  
     /**
 932  
      * @deprecated Removed as of version 0.9.3.  Use {@link #processResultSet(Statement, ResultSet, DocSearchCriteriaDTO, String)} instead.
 933  
      */
 934  
     public List<DocSearchDTO> processResultSet(Statement searchAttributeStatement, ResultSet resultSet,DocSearchCriteriaDTO searchCriteria) throws SQLException {
 935  0
         String principalId = null;
 936  0
         return processResultSet(searchAttributeStatement, resultSet, searchCriteria, principalId);
 937  
     }
 938  
 
 939  
 
 940  
     /**
 941  
      * @param resultSet
 942  
      * @param criteria
 943  
      * @return
 944  
      * @throws SQLException
 945  
      */
 946  
     public List<DocSearchDTO> processResultSet(Statement searchAttributeStatement, ResultSet resultSet,DocSearchCriteriaDTO searchCriteria, String principalId) throws SQLException {
 947  0
         setCriteria(searchCriteria);
 948  0
         int size = 0;
 949  0
         List<DocSearchDTO> docList = new ArrayList<DocSearchDTO>();
 950  0
         Map<Long, DocSearchDTO> resultMap = new HashMap<Long, DocSearchDTO>();
 951  0
         PerformanceLogger perfLog = new PerformanceLogger();
 952  0
         int iteration = 0;
 953  0
         boolean resultSetHasNext = resultSet.next();
 954  
         while ( resultSetHasNext &&
 955  0
                 ( (searchCriteria.getThreshold() == null) || (resultMap.size() < searchCriteria.getThreshold().intValue()) ) &&
 956  
                 ( (searchCriteria.getFetchLimit() == null) || (iteration < searchCriteria.getFetchLimit().intValue()) ) ) {
 957  0
             iteration++;
 958  0
             DocSearchDTO docCriteriaDTO = processRow(searchAttributeStatement, resultSet);
 959  0
             docCriteriaDTO.setSuperUserSearch(getCriteria().getSuperUserSearch());
 960  0
             if (!resultMap.containsKey(docCriteriaDTO.getRouteHeaderId())) {
 961  0
                 docList.add(docCriteriaDTO);
 962  0
                 resultMap.put(docCriteriaDTO.getRouteHeaderId(), docCriteriaDTO);
 963  0
                 size++;
 964  
             } else {
 965  
                 // handle duplicate rows with different search data
 966  0
                 DocSearchDTO previousEntry = (DocSearchDTO)resultMap.get(docCriteriaDTO.getRouteHeaderId());
 967  0
                 handleMultipleDocumentRows(previousEntry, docCriteriaDTO);
 968  
             }
 969  0
             resultSetHasNext = resultSet.next();
 970  0
         }
 971  
         /**
 972  
          * Begin IU Customization
 973  
          * 05/01/2010 - Eric Westfall
 974  
          * EN-1792
 975  
          * 
 976  
          * Go through all doc search rows after they have been generated to fetch all names.  Attempting to
 977  
          * address some significance performance issues with doc search whenever none of the initiators on
 978  
          * the returned documents are cached.
 979  
          */
 980  0
         Set<String> initiatorPrincipalIdSet = new HashSet<String>();
 981  0
         for (DocSearchDTO docSearchRow : docList) {
 982  0
                 initiatorPrincipalIdSet.add(docSearchRow.getInitiatorWorkflowId());
 983  
         }
 984  0
         List<String> initiatorPrincipalIds = new ArrayList<String>();
 985  0
         initiatorPrincipalIds.addAll(initiatorPrincipalIdSet);
 986  0
         if(initiatorPrincipalIds != null && !initiatorPrincipalIds.isEmpty()){ // don't call the service if the search returned nothing.
 987  0
                 Map<String, KimEntityNamePrincipalNameInfo> entityNames = KIMServiceLocator.getIdentityService().getDefaultNamesForPrincipalIds(initiatorPrincipalIds);
 988  0
                 for (DocSearchDTO docSearchRow : docList) {
 989  0
                         KimEntityNamePrincipalNameInfo name = entityNames.get(docSearchRow.getInitiatorWorkflowId());
 990  0
                         if (name != null) {
 991  0
                                 docSearchRow.setInitiatorFirstName(name.getDefaultEntityName().getFirstName());
 992  0
                                 docSearchRow.setInitiatorLastName(name.getDefaultEntityName().getLastName());
 993  0
                                 docSearchRow.setInitiatorName(name.getDefaultEntityName().getFormattedName());
 994  0
                                 docSearchRow.setInitiatorNetworkId(name.getPrincipalName());
 995  0
                                         if (StringUtils.isNotBlank(name.getDefaultEntityName().getFormattedName())) {
 996  0
                                                 docSearchRow.setInitiatorTransposedName(name.getDefaultEntityName().getFormattedName());
 997  0
                                         } else if (StringUtils.isNotBlank(name.getPrincipalName())) {
 998  0
                                                 docSearchRow.setInitiatorTransposedName(name.getPrincipalName());
 999  
                                         } else {
 1000  0
                                                 docSearchRow.setInitiatorTransposedName(docSearchRow.getInitiatorWorkflowId());
 1001  
                                         }
 1002  
                                 // it doesn't look like the doc search code even uses the initiator email address for anything
 1003  0
                                 docSearchRow.setInitiatorEmailAddress("");
 1004  
                         }
 1005  0
                 }
 1006  
         }
 1007  
         /**
 1008  
          * End IU Customization
 1009  
          */
 1010  0
         perfLog.log("Time to read doc search results.", true);
 1011  
         // if we have threshold+1 results, then we have more results than we are going to display
 1012  0
         criteria.setOverThreshold(resultSetHasNext);
 1013  
 
 1014  0
         UserSession userSession = UserSession.getAuthenticatedUser();
 1015  0
         if ( (userSession == null) && (principalId != null && !"".equals(principalId)) ) {
 1016  0
             LOG.info("Authenticated User Session is null... using parameter user: " + principalId);
 1017  0
             userSession = new UserSession(principalId);
 1018  0
         } else if (searchCriteria.isOverridingUserSession()) {
 1019  0
             if (principalId == null) {
 1020  0
                 LOG.error("Search Criteria specified UserSession override but given user paramter is null");
 1021  0
                 throw new WorkflowRuntimeException("Search criteria specified UserSession override but given user is null.");
 1022  
             }
 1023  0
             LOG.info("Search Criteria specified UserSession override.  Using user: " + principalId);
 1024  0
             userSession = new UserSession(principalId);
 1025  
         }
 1026  0
         if (userSession != null) {
 1027  
             // TODO do we really want to allow the document search if there is no User Session?
 1028  
             // This is mainly to allow for the unit tests to run but I wonder if we need to push
 1029  
             // the concept of the "executing user" into the doc search api in some way...
 1030  0
             perfLog = new PerformanceLogger();
 1031  0
             SecuritySession securitySession = new SecuritySession(userSession);
 1032  0
             for (Iterator<DocSearchDTO> iterator = docList.iterator(); iterator.hasNext();) {
 1033  0
                 DocSearchDTO docCriteriaDTO = (DocSearchDTO) iterator.next();
 1034  0
                 if (!KEWServiceLocator.getDocumentSecurityService().docSearchAuthorized(userSession, docCriteriaDTO, securitySession)) {
 1035  0
                     iterator.remove();
 1036  0
                     criteria.setSecurityFilteredRows(criteria.getSecurityFilteredRows() + 1);
 1037  
                 }
 1038  0
             }
 1039  0
             perfLog.log("Time to filter document search results for security.", true);
 1040  
         }
 1041  
 
 1042  0
         LOG.debug("Processed "+size+" document search result rows.");
 1043  0
         return docList;
 1044  
     }
 1045  
 
 1046  
     /**
 1047  
      * Handles multiple document rows by collapsing them and their data into the searchable attribute columns.
 1048  
      *
 1049  
      * TODO this is currently concatenating strings together with HTML elements, this seems bad in this location,
 1050  
      * perhaps we should move this to the web layer (and perhaps enhance the searchable attributes
 1051  
      * portion of the DocSearchDTO data structure?)
 1052  
      */
 1053  
     public void handleMultipleDocumentRows(DocSearchDTO existingRow, DocSearchDTO newRow) {
 1054  
 
 1055  0
         for (KeyValueSort newData : newRow.getSearchableAttributes()) {
 1056  0
             String newRowValue = newData.getValue();
 1057  0
             boolean foundMatch = false;
 1058  0
             for (KeyValueSort existingData : existingRow.getSearchableAttributes()) {
 1059  0
                 if (existingData.getKey().equals(newData.getKey())) {
 1060  0
                     String existingRowValue = existingData.getValue();
 1061  0
                     if (!Utilities.isEmpty(newRowValue)) {
 1062  0
                         String valueToSet = "";
 1063  0
                         if (Utilities.isEmpty(existingRowValue)) {
 1064  0
                             valueToSet = newRowValue;
 1065  
                         } else {
 1066  0
                             valueToSet = existingRowValue + "<br>" + newRowValue;
 1067  
                         }
 1068  0
                         existingData.setvalue(valueToSet);
 1069  0
                         if ( (existingData.getSortValue() == null) && (newData.getSortValue() != null) ) {
 1070  0
                             existingData.setSortValue(newData.getSortValue());
 1071  
                         }
 1072  
                     }
 1073  0
                     foundMatch = true;
 1074  0
                 }
 1075  
             }
 1076  0
             if (!foundMatch) {
 1077  0
                 existingRow.addSearchableAttribute(new KeyValueSort(newData));
 1078  
             }
 1079  0
         }
 1080  0
     }
 1081  
 
 1082  
     public DocSearchDTO processRow(Statement searchAttributeStatement, ResultSet rs) throws SQLException {
 1083  0
         DocSearchDTO docCriteriaDTO = new DocSearchDTO();
 1084  
 
 1085  0
         docCriteriaDTO.setRouteHeaderId(new Long(rs.getLong("DOC_HDR_ID")));
 1086  
 
 1087  0
         String docTypeLabel = rs.getString("LBL");
 1088  0
         String activeIndicatorCode = rs.getString("ACTV_IND");
 1089  
 
 1090  0
         docCriteriaDTO.setDocRouteStatusCode(rs.getString("DOC_HDR_STAT_CD"));
 1091  0
         docCriteriaDTO.setDateCreated(rs.getTimestamp("CRTE_DT"));
 1092  0
         docCriteriaDTO.setDocumentTitle(rs.getString("TTL"));
 1093  0
         docCriteriaDTO.setDocTypeName(rs.getString("DOC_TYP_NM"));
 1094  0
         docCriteriaDTO.setDocTypeLabel(docTypeLabel);
 1095  0
         docCriteriaDTO.setAppDocStatus(rs.getString("APP_DOC_STAT"));
 1096  
 
 1097  0
         if ((activeIndicatorCode == null) || (activeIndicatorCode.trim().length() == 0)) {
 1098  0
             docCriteriaDTO.setActiveIndicatorCode(KEWConstants.ACTIVE_CD);
 1099  
         } else {
 1100  0
             docCriteriaDTO.setActiveIndicatorCode(activeIndicatorCode);
 1101  
         }
 1102  
 
 1103  0
         if ((docTypeLabel == null) || (docTypeLabel.trim().length() == 0)) {
 1104  0
             docCriteriaDTO.setDocTypeHandlerUrl("");
 1105  
         } else {
 1106  0
             docCriteriaDTO.setDocTypeHandlerUrl(rs.getString("DOC_HDLR_URL"));
 1107  
         }
 1108  
 
 1109  0
         docCriteriaDTO.setInitiatorWorkflowId(rs.getString("INITR_PRNCPL_ID"));
 1110  
 
 1111  
         /**
 1112  
          * Begin IU Customization
 1113  
          * 05/01/2010 - Eric Westfall
 1114  
          * EN-1792
 1115  
          * 
 1116  
          * Remove the code to fetch the person and principal from their services.  After all rows
 1117  
          * have been fetched, we will process the names in one big bunch in the method that calls processRow.
 1118  
          * So we will basically comment out all of the following code.
 1119  
          */
 1120  
 
 1121  
         /*
 1122  
         Person user = KIMServiceLocator.getPersonService().getPerson(docCriteriaDTO.getInitiatorWorkflowId());
 1123  
 
 1124  
         if (user != null) {
 1125  
             KimPrincipal principal = KIMServiceLocator.getIdentityManagementService().getPrincipal(docCriteriaDTO.getInitiatorWorkflowId());
 1126  
 
 1127  
             docCriteriaDTO.setInitiatorNetworkId(user.getPrincipalName());
 1128  
             docCriteriaDTO.setInitiatorName(user.getName());
 1129  
             docCriteriaDTO.setInitiatorFirstName(user.getFirstName());
 1130  
             docCriteriaDTO.setInitiatorLastName(user.getLastName());
 1131  
             docCriteriaDTO.setInitiatorTransposedName(UserUtils.getTransposedName(UserSession.getAuthenticatedUser(), principal));
 1132  
             docCriteriaDTO.setInitiatorEmailAddress(user.getEmailAddress());
 1133  
         }
 1134  
 
 1135  
         */
 1136  
 
 1137  
         /**
 1138  
          * End IU Customization
 1139  
          */
 1140  
         
 1141  0
         if (isUsingAtLeastOneSearchAttribute()) {
 1142  0
             populateRowSearchableAttributes(docCriteriaDTO,searchAttributeStatement);
 1143  
         }
 1144  0
         return docCriteriaDTO;
 1145  
     }
 1146  
 
 1147  
     /**
 1148  
      * This method performs searches against the search attribute value tables (see classes implementing
 1149  
      * {@link SearchableAttributeValue}) to get data to fill in search attribute values on the given docCriteriaDTO parameter
 1150  
      *
 1151  
      * @param docCriteriaDTO - document search result object getting search attributes added to it
 1152  
      * @param searchAttributeStatement - statement being used to call the database for queries
 1153  
      * @throws SQLException
 1154  
      */
 1155  
     public void populateRowSearchableAttributes(DocSearchDTO docCriteriaDTO, Statement searchAttributeStatement) throws SQLException {
 1156  0
         searchAttributeStatement.setFetchSize(50);
 1157  0
         Long documentId = docCriteriaDTO.getRouteHeaderId();
 1158  0
         List<SearchableAttributeValue> attributeValues = DocSearchUtils.getSearchableAttributeValueObjectTypes();
 1159  0
         PerformanceLogger perfLog = new PerformanceLogger(documentId);
 1160  0
         for (SearchableAttributeValue searchAttValue : attributeValues) {
 1161  0
             String attributeSql = "select KEY_CD, VAL from " + searchAttValue.getAttributeTableName() + " where DOC_HDR_ID = " + documentId;
 1162  0
             ResultSet attributeResultSet = null;
 1163  
             try {
 1164  0
                 attributeResultSet = searchAttributeStatement.executeQuery(attributeSql);
 1165  0
                 while (attributeResultSet.next()) {
 1166  0
                     searchAttValue.setSearchableAttributeKey(attributeResultSet.getString("KEY_CD"));
 1167  0
                     searchAttValue.setupAttributeValue(attributeResultSet, "VAL");
 1168  0
                     if ( (!Utilities.isEmpty(searchAttValue.getSearchableAttributeKey())) && (searchAttValue.getSearchableAttributeValue() != null) ) {
 1169  0
                         docCriteriaDTO.addSearchableAttribute(new KeyValueSort(searchAttValue.getSearchableAttributeKey(),searchAttValue.getSearchableAttributeDisplayValue(),searchAttValue.getSearchableAttributeValue(),searchAttValue));
 1170  
                     }
 1171  
                 }
 1172  0
             } finally {
 1173  0
                 if (attributeResultSet != null) {
 1174  
                     try {
 1175  0
                         attributeResultSet.close();
 1176  0
                     } catch (Exception e) {
 1177  0
                         LOG.warn("Could not close searchable attribute result set for class " + searchAttValue.getClass().getName(),e);
 1178  0
                     }
 1179  
                 }
 1180  0
             }
 1181  0
         }
 1182  0
         perfLog.log("Time to execute doc search search attribute queries.", true);
 1183  0
     }
 1184  
 
 1185  
     /**
 1186  
      * @deprecated As of version 0.9.3 this method is no longer used. Method
 1187  
      *             {@link #populateRowSearchableAttributes(DocSearchDTO, Statement)} is being used instead.
 1188  
      */
 1189  
     @Deprecated
 1190  
     public void populateRowSearchableAttributes(DocSearchDTO docCriteriaDTO, Statement searchAttributeStatement, ResultSet rs) throws SQLException {
 1191  0
         List<SearchableAttributeValue> searchAttributeValues = DocSearchUtils.getSearchableAttributeValueObjectTypes();
 1192  0
         for (SearchableAttributeValue searchAttValue : searchAttributeValues) {
 1193  0
             String prefixName = searchAttValue.getAttributeDataType().toUpperCase();
 1194  0
             searchAttValue.setSearchableAttributeKey(rs.getString(prefixName + "_KEY"));
 1195  0
             searchAttValue.setupAttributeValue(rs, prefixName + "_VALUE");
 1196  0
             if ( (!Utilities.isEmpty(searchAttValue.getSearchableAttributeKey())) && (searchAttValue.getSearchableAttributeValue() != null) ) {
 1197  0
             docCriteriaDTO.addSearchableAttribute(new KeyValueSort(searchAttValue.getSearchableAttributeKey(),searchAttValue.getSearchableAttributeDisplayValue(),searchAttValue.getSearchableAttributeValue(),searchAttValue));
 1198  
             }
 1199  0
         }
 1200  0
     }
 1201  
 
 1202  
     public String getDocSearchSQL() {
 1203  
 
 1204  0
         String docTypeTableAlias   = "DOC1";
 1205  0
         String docHeaderTableAlias = "DOC_HDR";
 1206  
 
 1207  0
         String sqlPrefix = "Select * from (";
 1208  0
         String sqlSuffix = ") FINAL_SEARCH order by FINAL_SEARCH.DOC_HDR_ID desc";
 1209  
         // the DISTINCT here is important as it filters out duplicate rows which could occur as the result of doc search extension values...
 1210  0
         StringBuffer selectSQL = new StringBuffer("select DISTINCT("+ docHeaderTableAlias +".DOC_HDR_ID), "+ docHeaderTableAlias +".INITR_PRNCPL_ID, "
 1211  
                 + docHeaderTableAlias +".DOC_HDR_STAT_CD, "+ docHeaderTableAlias +".CRTE_DT, "+ docHeaderTableAlias +".TTL, "+ docHeaderTableAlias +".APP_DOC_STAT, "+ docTypeTableAlias +".DOC_TYP_NM, "
 1212  
                 + docTypeTableAlias +".LBL, "+ docTypeTableAlias +".DOC_HDLR_URL, "+ docTypeTableAlias +".ACTV_IND");
 1213  0
         StringBuffer fromSQL = new StringBuffer(" from KREW_DOC_TYP_T "+ docTypeTableAlias +" ");
 1214  0
         StringBuffer fromSQLForDocHeaderTable = new StringBuffer(", KREW_DOC_HDR_T " + docHeaderTableAlias + " ");
 1215  
 
 1216  0
         StringBuffer whereSQL = new StringBuffer();
 1217  0
         whereSQL.append(getRouteHeaderIdSql(criteria.getRouteHeaderId(), getGeneratedPredicatePrefix(whereSQL.length()), docHeaderTableAlias));
 1218  0
         whereSQL.append(getInitiatorSql(criteria.getInitiator(), getGeneratedPredicatePrefix(whereSQL.length())));
 1219  0
         whereSQL.append(getAppDocIdSql(criteria.getAppDocId(), getGeneratedPredicatePrefix(whereSQL.length())));
 1220  0
         whereSQL.append(getDateCreatedSql(criteria.getFromDateCreated(), criteria.getToDateCreated(), getGeneratedPredicatePrefix(whereSQL.length())));
 1221  0
         whereSQL.append(getDateLastModifiedSql(criteria.getFromDateLastModified(), criteria.getToDateLastModified(), getGeneratedPredicatePrefix(whereSQL.length())));
 1222  0
         whereSQL.append(getDateApprovedSql(criteria.getFromDateApproved(), criteria.getToDateApproved(), getGeneratedPredicatePrefix(whereSQL.length())));
 1223  0
         whereSQL.append(getDateFinalizedSql(criteria.getFromDateFinalized(), criteria.getToDateFinalized(), getGeneratedPredicatePrefix(whereSQL.length())));
 1224  
         // flags for the table being added to the FROM class of the sql
 1225  0
         if ((!"".equals(getViewerSql(criteria.getViewer(), getGeneratedPredicatePrefix(whereSQL.length())))) || (!"".equals(getWorkgroupViewerSql(criteria.getWorkgroupViewerId(), criteria.getWorkgroupViewerName(), getGeneratedPredicatePrefix(whereSQL.length()))))) {
 1226  0
             whereSQL.append(getViewerSql(criteria.getViewer(), getGeneratedPredicatePrefix(whereSQL.length())));
 1227  0
             whereSQL.append(getWorkgroupViewerSql(criteria.getWorkgroupViewerId(), criteria.getWorkgroupViewerName(), getGeneratedPredicatePrefix(whereSQL.length())));
 1228  0
             fromSQL.append(", KREW_ACTN_RQST_T ");
 1229  
         }
 1230  
 
 1231  0
         if (!("".equals(getApproverSql(criteria.getApprover(), getGeneratedPredicatePrefix(whereSQL.length()))))) {
 1232  0
             whereSQL.append(getApproverSql(criteria.getApprover(), getGeneratedPredicatePrefix(whereSQL.length())));
 1233  0
             fromSQL.append(", KREW_ACTN_TKN_T ");
 1234  
         }
 1235  
 
 1236  
 
 1237  
 
 1238  0
         String docRouteNodeSql = getDocRouteNodeSql(criteria.getDocTypeFullName(), criteria.getDocRouteNodeId(), criteria.getDocRouteNodeLogic(), getGeneratedPredicatePrefix(whereSQL.length()));
 1239  0
         if (!"".equals(docRouteNodeSql)) {
 1240  0
             whereSQL.append(docRouteNodeSql);
 1241  0
             fromSQL.append(", KREW_RTE_NODE_INSTN_T ");
 1242  0
             fromSQL.append(", KREW_RTE_NODE_T ");
 1243  
         }
 1244  
 
 1245  0
         filterOutNonQueryAttributes();
 1246  0
         if ((criteria.getSearchableAttributes() != null) && (criteria.getSearchableAttributes().size() > 0)) {
 1247  0
             QueryComponent queryComponent = getSearchableAttributeSql(criteria.getSearchableAttributes(), getGeneratedPredicatePrefix(whereSQL.length()));
 1248  0
             selectSQL.append(queryComponent.getSelectSql());
 1249  0
             fromSQL.append(queryComponent.getFromSql());
 1250  0
             whereSQL.append(queryComponent.getWhereSql());
 1251  
         }
 1252  
 
 1253  
         // at this point we haven't appended doc title to the query, if the document title is the only field
 1254  
         // which was entered, we want to set the "from" date to be X days ago.  This will allow for a
 1255  
         // more efficient query
 1256  0
         Integer defaultCreateDateDaysAgoValue = null;
 1257  
 //        whereSQL.append(getDocTitleSql(criteria.getDocTitle(), getGeneratedPredicatePrefix(whereSQL.length())));
 1258  0
         String tempWhereSql = getDocTitleSql(criteria.getDocTitle(), getGeneratedPredicatePrefix(whereSQL.length()));
 1259  0
         if ( ((whereSQL == null) || (StringUtils.isBlank(whereSQL.toString()))) && (StringUtils.isNotBlank(tempWhereSql)) ) {
 1260  
             // doc title is not blank
 1261  0
             defaultCreateDateDaysAgoValue = KEWConstants.DOCUMENT_SEARCH_DOC_TITLE_CREATE_DATE_DAYS_AGO;
 1262  
         }
 1263  0
         whereSQL.append(tempWhereSql);
 1264  0
         if ( ((whereSQL == null) || (StringUtils.isBlank(whereSQL.toString()))) && (StringUtils.isBlank(criteria.getDocRouteStatus())) ) {
 1265  
             // if they haven't set any criteria, default the from created date to today minus days from constant variable
 1266  0
             defaultCreateDateDaysAgoValue = KEWConstants.DOCUMENT_SEARCH_NO_CRITERIA_CREATE_DATE_DAYS_AGO;
 1267  
         }
 1268  0
         if (defaultCreateDateDaysAgoValue != null) {
 1269  
             // add a default create date
 1270  0
             Calendar calendar = Calendar.getInstance();
 1271  0
             calendar.add(Calendar.DATE, defaultCreateDateDaysAgoValue.intValue());
 1272  0
             criteria.setFromDateCreated(RiceConstants.getDefaultDateFormat().format(calendar.getTime()));
 1273  0
             whereSQL.append(getDateCreatedSql(criteria.getFromDateCreated(), criteria.getToDateCreated(), getGeneratedPredicatePrefix(whereSQL.length())));
 1274  
         }
 1275  
 
 1276  0
         String docTypeFullNameSql = getDocTypeFullNameWhereSql(criteria.getDocTypeFullName(), getGeneratedPredicatePrefix(whereSQL.length()));
 1277  0
         if (!("".equals(docTypeFullNameSql))) {
 1278  0
             whereSQL.append(docTypeFullNameSql);
 1279  
         }
 1280  0
         whereSQL.append(getDocRouteStatusSql(criteria.getDocRouteStatus(), getGeneratedPredicatePrefix(whereSQL.length())));
 1281  0
         whereSQL.append(getGeneratedPredicatePrefix(whereSQL.length())).append(" DOC_HDR.DOC_TYP_ID = DOC1.DOC_TYP_ID ");
 1282  0
         fromSQL.append(fromSQLForDocHeaderTable);
 1283  
         
 1284  
         // App Doc Status Value and Transition clauses
 1285  0
         String statusTransitionWhereClause = getStatusTransitionDateSql(criteria.getFromStatusTransitionDate(), criteria.getToStatusTransitionDate(), getGeneratedPredicatePrefix(whereSQL.length()));
 1286  0
         whereSQL.append(getAppDocStatusSql(criteria.getAppDocStatus(), getGeneratedPredicatePrefix(whereSQL.length()), statusTransitionWhereClause.length() ));                
 1287  0
         if (statusTransitionWhereClause.length() > 0){
 1288  0
                 whereSQL.append(statusTransitionWhereClause);
 1289  0
             whereSQL.append(getGeneratedPredicatePrefix(whereSQL.length())).append(" DOC_HDR.DOC_HDR_ID = STAT_TRAN.DOC_HDR_ID ");
 1290  0
                 fromSQL.append(", KREW_APP_DOC_STAT_TRAN_T STAT_TRAN ");
 1291  
         } 
 1292  
         
 1293  0
         String finalizedSql = sqlPrefix + " " + selectSQL.toString() + " " + fromSQL.toString() + " " + whereSQL.toString() + " " + sqlSuffix;
 1294  
 
 1295  0
         LOG.info("*********** SEARCH SQL ***************");
 1296  0
         LOG.info(finalizedSql);
 1297  0
         LOG.info("**************************************");
 1298  0
         return finalizedSql;
 1299  
     }
 1300  
 
 1301  
     /**
 1302  
      * @deprecated As of version 0.9.3 this method is no longer used. This method had been used to create multiple SQL queries if using searchable attributes
 1303  
      *             and use the sql UNION function to join the queries. The replacement method
 1304  
      *             {@link #generateFinalSQL(QueryComponent, String, String)} is now used instead.
 1305  
      */
 1306  
     @Deprecated
 1307  
     public String generateFinalSQL(QueryComponent searchSQL,String docHeaderTableAlias, String standardSqlPrefix, String standardSqlSuffix) {
 1308  0
         StringBuffer finalSql = new StringBuffer();
 1309  0
         List<SearchableAttributeValue> searchableAttributeValues = DocSearchUtils.getSearchableAttributeValueObjectTypes();
 1310  0
         List<String> tableAliasComponentNames = new ArrayList<String>(searchableAttributeValues.size());
 1311  0
         for (SearchableAttributeValue attValue : searchableAttributeValues) {
 1312  0
             tableAliasComponentNames.add(attValue.getAttributeDataType().toUpperCase());
 1313  
         }
 1314  0
         for (SearchableAttributeValue attributeValue : searchableAttributeValues) {
 1315  0
             QueryComponent qc = generateSqlForSearchableAttributeValue(attributeValue, tableAliasComponentNames, docHeaderTableAlias);
 1316  0
             StringBuffer currentSql = new StringBuffer();
 1317  0
             currentSql.append(searchSQL.getSelectSql() + qc.getSelectSql() + searchSQL.getFromSql() + qc.getFromSql() + searchSQL.getWhereSql() + qc.getWhereSql());
 1318  0
             if (finalSql.length() == 0) {
 1319  0
                 finalSql.append(standardSqlPrefix).append(" ( ").append(currentSql);
 1320  
             } else {
 1321  0
                 finalSql.append(" ) UNION ( " + currentSql.toString());
 1322  
             }
 1323  0
         }
 1324  0
         finalSql.append(" ) " + standardSqlSuffix);
 1325  0
         return finalSql.toString();
 1326  
     }
 1327  
 
 1328  
     /**
 1329  
      * @deprecated As of version 0.9.3 this method is no longer used. This method had been used to generate SQL to return searchable attributes using left
 1330  
      *             outer joins. The new mechanism to get search attributes from the database is to call each search attribute
 1331  
      *             table individually in the {@link #populateRowSearchableAttributes(DocSearchDTO, Statement, ResultSet)}
 1332  
      *             method.
 1333  
      */
 1334  
     @Deprecated
 1335  
     public QueryComponent generateSqlForSearchableAttributeValue(SearchableAttributeValue attributeValue, List<String> tableAliasComponentNames, String docHeaderTableAlias) {
 1336  0
         StringBuffer selectSql = new StringBuffer();
 1337  0
         StringBuffer fromSql = new StringBuffer();
 1338  0
         String currentAttributeTableAlias = "SA_" + attributeValue.getAttributeDataType().toUpperCase();
 1339  0
         fromSql.append(" LEFT OUTER JOIN " + attributeValue.getAttributeTableName() + " " + currentAttributeTableAlias + " ON (" + docHeaderTableAlias + ".DOC_HDR_ID = " + currentAttributeTableAlias + ".DOC_HDR_ID)");
 1340  0
         for (Iterator<String> iter = tableAliasComponentNames.iterator(); iter.hasNext();) {
 1341  0
             String aliasComponentName = (String) iter.next();
 1342  0
             if (aliasComponentName.equalsIgnoreCase(attributeValue.getAttributeDataType())) {
 1343  0
                 selectSql.append(", " + currentAttributeTableAlias + ".KEY_CD as " + aliasComponentName + "_KEY, " + currentAttributeTableAlias + ".VAL as " + aliasComponentName + "_VALUE");
 1344  
             } else {
 1345  0
                 selectSql.append(", NULL as " + aliasComponentName + "_KEY, NULL as " + aliasComponentName + "_VALUE");
 1346  
             }
 1347  0
         }
 1348  0
         return new QueryComponent(selectSql.toString(),fromSql.toString(),"");
 1349  
     }
 1350  
 
 1351  
     public String getRouteHeaderIdSql(String routeHeaderId, String whereClausePredicatePrefix, String tableAlias) {
 1352  
 
 1353  0
         if ((routeHeaderId == null) || "".equals(routeHeaderId.trim())) {
 1354  0
             return "";
 1355  
         } else {
 1356  0
             Criteria crit = getSqlBuilder().createCriteria("DOC_HDR_ID", routeHeaderId, "KREW_DOC_HDR_T", tableAlias,Long.TYPE);
 1357  0
             return new StringBuffer(whereClausePredicatePrefix + crit.buildWhere()).toString();
 1358  
         }
 1359  
 
 1360  
     }
 1361  
 
 1362  
     public String getInitiatorSql(String initiator, String whereClausePredicatePrefix) {
 1363  0
         String tableAlias = "DOC_HDR";
 1364  
 
 1365  0
         if ((initiator == null) || "".equals(initiator.trim())) {
 1366  0
             return "";
 1367  
         }
 1368  
 
 1369  0
         Map<String, String> m = new HashMap<String, String>();
 1370  0
         m.put("principalName", initiator);
 1371  
 
 1372  
         // This will search for people with the ability for the valid operands.
 1373  0
         List<Person> pList = KIMServiceLocator.getPersonService().findPeople(m, false);
 1374  
 
 1375  0
         if(pList == null || pList.isEmpty() ){
 1376  
             // they entered something that returned nothing... so we should return nothing
 1377  0
              return new StringBuffer(whereClausePredicatePrefix + " 1 = 0 ").toString();
 1378  
         }
 1379  
 
 1380  0
         List<String> principalList = new ArrayList<String>();
 1381  
 
 1382  0
         for(Person p: pList){
 1383  0
             principalList.add(p.getPrincipalId());
 1384  
         }
 1385  
 
 1386  0
         Criteria crit = new org.kuali.rice.core.jdbc.criteria.Criteria("KREW_DOC_HDR_T", tableAlias);
 1387  0
         crit.in("INITR_PRNCPL_ID", principalList, String.class);
 1388  
 
 1389  
         //sqlBuild.addCriteria("INITR_PRNCPL_ID", userWorkflowId, tableAlias, String.class, true, false, crit);
 1390  0
         return new StringBuffer(whereClausePredicatePrefix + crit.buildWhere()).toString();
 1391  
 
 1392  
         //return new StringBuffer(whereClausePredicatePrefix + " DOC_HDR.INITR_PRNCPL_ID = '").append(userWorkflowId).append("'").toString();
 1393  
     }
 1394  
 
 1395  
     public String getDocTitleSql(String docTitle, String whereClausePredicatePrefix) {
 1396  0
         if (StringUtils.isBlank(docTitle)) {
 1397  0
             return "";
 1398  
         } else {
 1399  
             /*
 1400  
             if (!docTitle.trim().endsWith("*")) {
 1401  
                 docTitle = docTitle.trim().concat("*").replace('*', '%');
 1402  
             } else {
 1403  
                 docTitle = docTitle.trim().replace('*', '%');
 1404  
             }
 1405  
             */
 1406  
             // quick and dirty ' replacement that isn't the best but should work for all dbs
 1407  0
             docTitle = docTitle.trim().replace("\'", "\'\'");
 1408  
 
 1409  
 
 1410  0
             SqlBuilder sqlBuild = new SqlBuilder();
 1411  0
             org.kuali.rice.core.jdbc.criteria.Criteria crit = new org.kuali.rice.core.jdbc.criteria.Criteria("KREW_DOC_HDR_T", "DOC_HDR");
 1412  
 
 1413  0
             sqlBuild.addCriteria("TTL", docTitle, String.class, true, true, crit);
 1414  0
             return new StringBuffer(whereClausePredicatePrefix + crit.buildWhere()).toString();
 1415  
 
 1416  
 
 1417  
 
 1418  
             //return new StringBuffer(whereClausePredicatePrefix + " upper(DOC_HDR.TTL) like '%").append(getDbPlatform().escapeString(docTitle.toUpperCase())).append("'").toString();
 1419  
         }
 1420  
     }
 1421  
 
 1422  
     // special methods that return the sql needed to complete the search
 1423  
     // or nothing if the field was not filled in
 1424  
     public String getAppDocIdSql(String appDocId, String whereClausePredicatePrefix) {
 1425  0
         String tableAlias = "DOC_HDR";
 1426  
 
 1427  0
         if ((appDocId == null) || "".equals(appDocId.trim())) {
 1428  0
             return "";
 1429  
         } else {
 1430  0
             Criteria crit = getSqlBuilder().createCriteria("APP_DOC_ID", appDocId, "KREW_DOC_HDR_T", tableAlias,String.class);
 1431  0
             return new StringBuffer(whereClausePredicatePrefix + crit.buildWhere()).toString();
 1432  
         }
 1433  
     }
 1434  
 
 1435  
     public String getDateCreatedSql(String fromDateCreated, String toDateCreated, String whereClausePredicatePrefix) {
 1436  0
         return establishDateString(fromDateCreated, toDateCreated, "KREW_DOC_HDR_T", "DOC_HDR", "CRTE_DT", whereClausePredicatePrefix);
 1437  
     }
 1438  
 
 1439  
     public String getDateApprovedSql(String fromDateApproved, String toDateApproved, String whereClausePredicatePrefix) {
 1440  0
         return establishDateString(fromDateApproved, toDateApproved, "KREW_DOC_HDR_T", "DOC_HDR", "APRV_DT", whereClausePredicatePrefix);
 1441  
     }
 1442  
 
 1443  
     public String getDateFinalizedSql(String fromDateFinalized, String toDateFinalized, String whereClausePredicatePrefix) {
 1444  0
         return establishDateString(fromDateFinalized, toDateFinalized, "KREW_DOC_HDR_T", "DOC_HDR", "FNL_DT", whereClausePredicatePrefix);
 1445  
 
 1446  
     }
 1447  
 
 1448  
     public String getDateLastModifiedSql(String fromDateLastModified, String toDateLastModified, String whereClausePredicatePrefix) {
 1449  0
         return establishDateString(fromDateLastModified, toDateLastModified, "KREW_DOC_HDR_T", "DOC_HDR", "STAT_MDFN_DT", whereClausePredicatePrefix);
 1450  
     }
 1451  
 
 1452  
         public String getStatusTransitionDateSql(String fromStatusTransitionDate, String toStatusTransitionDate, String whereClausePredicatePrefix) {
 1453  0
         return establishDateString(fromStatusTransitionDate, toStatusTransitionDate, "KREW_DOC_HDR_T", "DOC_HDR", "APP_DOC_STAT_MDFN_DT", whereClausePredicatePrefix);
 1454  
     }
 1455  
     
 1456  
     public String getViewerSql(String viewer, String whereClausePredicatePrefix) {
 1457  0
         String returnSql = "";
 1458  0
         if ((viewer != null) && (!"".equals(viewer.trim()))) {
 1459  0
             Map<String, String> m = new HashMap<String, String>();
 1460  0
             m.put("principalName", viewer);
 1461  
 
 1462  
             // This will search for people with the ability for the valid operands.
 1463  0
             List<Person> pList = KIMServiceLocator.getPersonService().findPeople(m, false);
 1464  
 
 1465  0
             if(pList == null || pList.isEmpty() ){
 1466  
                 // they entered something that returned nothing... so we should return nothing
 1467  0
                 return new StringBuffer(whereClausePredicatePrefix + " 1 = 0 ").toString();
 1468  
             }
 1469  
 
 1470  0
             List<String> principalList = new ArrayList<String>();
 1471  
 
 1472  0
             for(Person p: pList){
 1473  0
                 principalList.add(p.getPrincipalId());
 1474  
             }
 1475  
 
 1476  0
             Criteria crit = new org.kuali.rice.core.jdbc.criteria.Criteria("KREW_ACTN_RQST_T", "KREW_ACTN_RQST_T");
 1477  
 
 1478  0
             crit.in("PRNCPL_ID", principalList, String.class);
 1479  
 
 1480  
             //Person person = KIMServiceLocator.getPersonService().getPersonByPrincipalName(viewer.trim());
 1481  
             //String principalId = person.getPrincipalId();
 1482  0
             returnSql = whereClausePredicatePrefix + "( (DOC_HDR.DOC_HDR_ID = KREW_ACTN_RQST_T.DOC_HDR_ID and " + crit.buildWhere() + " )";
 1483  
 
 1484  0
             Set<String> viewerGroupIds = new TreeSet<String>();
 1485  
 
 1486  0
             if(principalList != null && !principalList.isEmpty()){
 1487  0
                 for(String principalId: principalList){
 1488  0
                         viewerGroupIds.addAll(KIMServiceLocator.getGroupService().getGroupIdsForPrincipal(principalId));
 1489  
                 }
 1490  
             }
 1491  
 
 1492  
             // Documents routed to users as part of a workgoup should be returned.
 1493  
 
 1494  
             // Use Chad's escape stuff
 1495  0
             if (viewerGroupIds != null && !viewerGroupIds.isEmpty()) {
 1496  
 
 1497  0
                 returnSql += " or ( " +
 1498  
                     "DOC_HDR.DOC_HDR_ID = KREW_ACTN_RQST_T.DOC_HDR_ID " +
 1499  
                     "and KREW_ACTN_RQST_T.GRP_ID in (";
 1500  
 
 1501  0
                 boolean first = true;
 1502  0
                 for(String groupId: viewerGroupIds){
 1503  0
                     if(!first){
 1504  0
                         returnSql += ",";
 1505  
                     }
 1506  0
                     returnSql += "'" + groupId + "'";
 1507  0
                     first = false;
 1508  
                 }
 1509  0
                 returnSql += "))";
 1510  
             }
 1511  0
             returnSql += ")";
 1512  
         }
 1513  0
         return returnSql;
 1514  
     }
 1515  
 
 1516  
     public String getWorkgroupViewerSql(String id, String workgroupName, String whereClausePredicatePrefix) {
 1517  0
         String sql = "";
 1518  0
         if (!Utilities.isEmpty(workgroupName)) {
 1519  0
             Group group = KIMServiceLocator.getIdentityManagementService().getGroup(id);
 1520  0
             sql = whereClausePredicatePrefix + " DOC_HDR.DOC_HDR_ID = KREW_ACTN_RQST_T.DOC_HDR_ID and KREW_ACTN_RQST_T.GRP_ID = " + group.getGroupId();
 1521  
         }
 1522  0
         return sql;
 1523  
     }
 1524  
 
 1525  
     public String getApproverSql(String approver, String whereClausePredicatePrefix) {
 1526  0
         String returnSql = "";
 1527  0
         if ((approver != null) && (!"".equals(approver.trim()))) {
 1528  0
             Map<String, String> m = new HashMap<String, String>();
 1529  0
             m.put("principalName", approver);
 1530  
 
 1531  
             // This will search for people with the ability for the valid operands.
 1532  0
             List<Person> pList = KIMServiceLocator.getPersonService().findPeople(m, false);
 1533  
 
 1534  0
             if(pList == null || pList.isEmpty() ){
 1535  0
                  return "";
 1536  
             }
 1537  
 
 1538  0
             List<String> principalList = new ArrayList<String>();
 1539  
 
 1540  0
             for(Person p: pList){
 1541  0
                 principalList.add(p.getPrincipalId());
 1542  
             }
 1543  
 
 1544  0
             Criteria crit = new org.kuali.rice.core.jdbc.criteria.Criteria("KREW_ACTN_TKN_T", "KREW_ACTN_TKN_T");
 1545  0
             crit.in("PRNCPL_ID", principalList, String.class);
 1546  
 
 1547  0
             returnSql = whereClausePredicatePrefix +
 1548  
             " DOC_HDR.DOC_HDR_ID = KREW_ACTN_TKN_T.DOC_HDR_ID and upper(KREW_ACTN_TKN_T.ACTN_CD) in ('" +
 1549  
             KEWConstants.ACTION_TAKEN_APPROVED_CD + "','" + KEWConstants.ACTION_TAKEN_BLANKET_APPROVE_CD + "')" +
 1550  
             " and " + crit.buildWhere();
 1551  
         }
 1552  0
         return returnSql;
 1553  
     }
 1554  
 
 1555  
     public String getDocTypeFullNameWhereSql(String docTypeFullName, String whereClausePredicatePrefix) {
 1556  0
         StringBuffer returnSql = new StringBuffer("");
 1557  0
         if ((docTypeFullName != null) && (!"".equals(docTypeFullName.trim()))) {
 1558  
             /*
 1559  
             DocumentTypeDAOOjbImpl
 1560  
 
 1561  
 
 1562  
             Map<String, String> m = new HashMap<String, String>();
 1563  
             m.put("name", docTypeFullName);
 1564  
 
 1565  
             Collection c = KNSServiceLocator.getBusinessObjectDao().findMatching(DocumentType.class, m);
 1566  
 */
 1567  0
             DocumentTypeService docSrv = (DocumentTypeService) KEWServiceLocator.getDocumentTypeService();
 1568  0
             DocumentType docType = docSrv.findByName(docTypeFullName.trim());
 1569  0
             if (docType != null) {
 1570  0
                 returnSql.append(whereClausePredicatePrefix).append("(");
 1571  0
                 addDocumentTypeNameToSearchOn(returnSql,docType.getName(), "");
 1572  0
                 if (docType.getChildrenDocTypes() != null) {
 1573  0
                     addChildDocumentTypes(returnSql, docType.getChildrenDocTypes());
 1574  
                 }
 1575  0
                 addExtraDocumentTypesToSearch(returnSql,docType);
 1576  0
                 returnSql.append(")");
 1577  
             }else{
 1578  0
                 returnSql.append(whereClausePredicatePrefix).append("(");
 1579  0
                 addDocumentTypeLikeNameToSearchOn(returnSql,docTypeFullName.trim(), "");
 1580  0
                 returnSql.append(")");
 1581  
             }
 1582  
         }
 1583  0
         return returnSql.toString();
 1584  
     }
 1585  
 
 1586  
     public void addChildDocumentTypes(StringBuffer whereSql, Collection<DocumentType> childDocumentTypes) {
 1587  0
         for (DocumentType child : childDocumentTypes) {
 1588  0
             addDocumentTypeNameToSearchOn(whereSql, child.getName());
 1589  0
             addChildDocumentTypes(whereSql, child.getChildrenDocTypes());
 1590  
         }
 1591  0
     }
 1592  
 
 1593  0
     public void addExtraDocumentTypesToSearch(StringBuffer whereSql,DocumentType docType) {}
 1594  
 
 1595  
     public void addDocumentTypeNameToSearchOn(StringBuffer whereSql,String documentTypeName) {
 1596  0
         this.addDocumentTypeNameToSearchOn(whereSql, documentTypeName, " or ");
 1597  0
     }
 1598  
 
 1599  
     public void addDocumentTypeNameToSearchOn(StringBuffer whereSql,String documentTypeName, String clause) {
 1600  0
         whereSql.append(clause).append(" DOC1.DOC_TYP_NM = '" + documentTypeName + "'");
 1601  0
     }
 1602  
     public void addDocumentTypeLikeNameToSearchOn(StringBuffer whereSql,String documentTypeName, String clause) {
 1603  0
         documentTypeName = documentTypeName.replace('*', '%');
 1604  0
         whereSql.append(clause).append(" DOC1.DOC_TYP_NM LIKE '" + documentTypeName + "'");
 1605  0
     }
 1606  
 
 1607  
     public String getDocRouteNodeSql(String documentTypeFullName, String docRouteLevel, String docRouteLevelLogic, String whereClausePredicatePrefix) {
 1608  
         // -1 is the default 'blank' choice from the route node drop down a number is used because the ojb RouteNode object is used to
 1609  
         // render the node choices on the form.
 1610  0
         String returnSql = "";
 1611  0
         if ((docRouteLevel != null) && (!"".equals(docRouteLevel.trim())) && (!docRouteLevel.equals("-1"))) {
 1612  
                 
 1613  
             /**
 1614  
                  * Begin IU Customization
 1615  
                  * 04-14-2010 - Shannon Hess
 1616  
                  * 
 1617  
                  * Using the docRouteLevel, get the corresponding route node name and use that for the comparison.  EN-1698.
 1618  
                  * 
 1619  
                  */
 1620  
                     
 1621  0
                 String searchCriteriaRouteNodeName = "";
 1622  
                 try {
 1623  0
                         long docRouteLevelLong = Long.parseLong(docRouteLevel);
 1624  0
                         RouteNode searchCriteriaRouteNode = KEWServiceLocator.getRouteNodeService().findRouteNodeById(docRouteLevelLong);
 1625  
                         
 1626  0
                         if (searchCriteriaRouteNode != null) {
 1627  0
                                 searchCriteriaRouteNodeName = searchCriteriaRouteNode.getRouteNodeName();
 1628  
                         }
 1629  0
                 } catch (java.lang.NumberFormatException e) {
 1630  0
                         searchCriteriaRouteNodeName = docRouteLevel;
 1631  0
                 }
 1632  
                                     
 1633  0
             StringBuffer routeNodeCriteria = new StringBuffer("and " + ROUTE_NODE_TABLE + ".NM ");
 1634  0
             if (KEWConstants.DOC_SEARCH_ROUTE_STATUS_QUALIFIER_EXACT.equalsIgnoreCase(docRouteLevelLogic.trim())) {
 1635  0
                         routeNodeCriteria.append("= '" + getDbPlatform().escapeString(searchCriteriaRouteNodeName) + "' ");
 1636  
             } else {
 1637  0
                 routeNodeCriteria.append("in (");
 1638  
                 // below buffer used to facilitate the addition of the string ", " to separate out route node names
 1639  0
                 StringBuffer routeNodeInCriteria = new StringBuffer();
 1640  0
                 boolean foundSpecifiedNode = false;
 1641  0
                 List<RouteNode> routeNodes = KEWServiceLocator.getRouteNodeService().getFlattenedNodes(getValidDocumentType(documentTypeFullName), true);
 1642  0
                 for (RouteNode routeNode : routeNodes) {
 1643  0
                     if (searchCriteriaRouteNodeName.equals(routeNode.getRouteNodeName())) {
 1644  
               /**
 1645  
                * End IU Customization
 1646  
                */
 1647  
                         // current node is specified node so we ignore it outside of the boolean below
 1648  0
                         foundSpecifiedNode = true;
 1649  0
                         continue;
 1650  
                     }
 1651  
                     // below logic should be to add the current node to the criteria if we haven't found the specified node
 1652  
                     // and the logic qualifier is 'route nodes before specified'... or we have found the specified node and
 1653  
                     // the logic qualifier is 'route nodes after specified'
 1654  0
                     if ( (!foundSpecifiedNode && (KEWConstants.DOC_SEARCH_ROUTE_STATUS_QUALIFIER_BEFORE.equalsIgnoreCase(docRouteLevelLogic.trim()))) ||
 1655  
                          (foundSpecifiedNode && (KEWConstants.DOC_SEARCH_ROUTE_STATUS_QUALIFIER_AFTER.equalsIgnoreCase(docRouteLevelLogic.trim()))) ) {
 1656  0
                         if (routeNodeInCriteria.length() > 0) {
 1657  0
                             routeNodeInCriteria.append(", ");
 1658  
                         }
 1659  0
                         routeNodeInCriteria.append("'" + routeNode.getRouteNodeName() + "'");
 1660  
                     }
 1661  
                 }
 1662  0
                 if (routeNodeInCriteria.length() > 0) {
 1663  0
                     routeNodeCriteria.append(routeNodeInCriteria);
 1664  
                 } else {
 1665  0
                     routeNodeCriteria.append("''");
 1666  
                 }
 1667  0
                 routeNodeCriteria.append(") ");
 1668  
             }
 1669  0
             returnSql = whereClausePredicatePrefix + "DOC_HDR.DOC_HDR_ID = " + ROUTE_NODE_INST_TABLE + ".DOC_HDR_ID and " + ROUTE_NODE_INST_TABLE + ".RTE_NODE_ID = " + ROUTE_NODE_TABLE + ".RTE_NODE_ID and " + ROUTE_NODE_INST_TABLE + ".ACTV_IND = 1 " + routeNodeCriteria.toString() + " ";
 1670  
         }
 1671  0
         return returnSql;
 1672  
     }
 1673  
 
 1674  
     public String getDocRouteStatusSql(String docRouteStatuses, String whereClausePredicatePrefix) {
 1675  0
         if ((docRouteStatuses == null) || "".equals(docRouteStatuses.trim())) {
 1676  0
             return whereClausePredicatePrefix + "DOC_HDR.DOC_HDR_STAT_CD != '" + KEWConstants.ROUTE_HEADER_INITIATED_CD + "'";
 1677  
         } else {
 1678  
 
 1679  
             // doc status can now be a comma deliminated list
 1680  0
             List<String> docRouteStatusList = Arrays.asList(docRouteStatuses.split(","));
 1681  0
             String inList = "";
 1682  
 
 1683  0
             for(String docRouteStatus : docRouteStatusList){
 1684  0
                 if(KEWConstants.DOCUMENT_STATUS_PARENT_TYPES.containsKey(docRouteStatus)){
 1685  
                     // build the sql
 1686  0
                     for(String docStatusCd : KEWConstants.DOCUMENT_STATUS_PARENT_TYPES.get(docRouteStatus)){
 1687  0
                         inList += "'" + getDbPlatform().escapeString(docStatusCd.trim()) + "',";
 1688  
                     }
 1689  
                 } else{
 1690  0
                     inList += "'" + getDbPlatform().escapeString(docRouteStatus.trim()) + "',";
 1691  
                 }
 1692  
             }
 1693  0
             inList = inList.substring(0,inList.length()-1); // remove trailing ','
 1694  
 
 1695  0
             return whereClausePredicatePrefix + " DOC_HDR.DOC_HDR_STAT_CD in (" + inList +")";
 1696  
         }
 1697  
     }
 1698  
 
 1699  
     /**
 1700  
      * 
 1701  
      * This method generates the where clause fragment related to Application Document Status.
 1702  
      * If the Status value only is defined, search for the appDocStatus value in the route header.
 1703  
      * If either the transition from/to dates are defined, search agains the status transition history.
 1704  
      * 
 1705  
      * @param appDocStatus
 1706  
      * @param whereClausePredicatePrefix
 1707  
      * @param statusTransitionWhereClauseLength
 1708  
      * @return
 1709  
      */
 1710  
     public String getAppDocStatusSql(String appDocStatus, String whereClausePredicatePrefix, int statusTransitionWhereClauseLength) {
 1711  0
         if ((appDocStatus == null) || "".equals(appDocStatus.trim())) {
 1712  0
             return "";
 1713  
         } else {
 1714  0
                 if (statusTransitionWhereClauseLength > 0){
 1715  0
                         return whereClausePredicatePrefix + " STAT_TRAN.APP_DOC_STAT_TO = '" + getDbPlatform().escapeString(appDocStatus.trim()) + "'";
 1716  
                 }else{
 1717  0
                         return whereClausePredicatePrefix + " DOC_HDR.APP_DOC_STAT = '" + getDbPlatform().escapeString(appDocStatus.trim()) + "'";
 1718  
                 }
 1719  
         }
 1720  
     }
 1721  
 
 1722  
 
 1723  
     // ---- utility methods
 1724  
 
 1725  
     /**
 1726  
      * TODO we should probably clean this up some, but we are going to exclude those KeyLabelPairs
 1727  
      * that have a null label.  This will happen in the case of Quickfinders which don't really
 1728  
      * represent criteria anyway.  Note however, that it is legal for the label to be the empty string.
 1729  
      * At some point we will probably need to do some more work to untangle this mess
 1730  
      */
 1731  
     public void filterOutNonQueryAttributes() {
 1732  0
         List<SearchAttributeCriteriaComponent> newAttributes = new ArrayList<SearchAttributeCriteriaComponent>();
 1733  0
         for (SearchAttributeCriteriaComponent component : criteria.getSearchableAttributes()) {
 1734  0
             if (component != null) {
 1735  0
                 if ( (StringUtils.isNotBlank(component.getValue())) || (!Utilities.isEmpty(component.getValues())) ) {
 1736  0
                     newAttributes.add(component);
 1737  
                 }
 1738  
             }
 1739  
         }
 1740  0
         criteria.setSearchableAttributes(newAttributes);
 1741  0
     }
 1742  
 
 1743  
     public String getGeneratedPredicatePrefix(int whereClauseSize) {
 1744  0
         return (whereClauseSize > 0) ? " and " : " where ";
 1745  
     }
 1746  
 
 1747  
     public String establishDateString(String fromDate, String toDate, String tableName, String tableAlias, String colName, String whereStatementClause) {
 1748  
   /*
 1749  
         String[] splitPropVal = StringUtils.split(columnDbName, "\\.");
 1750  
         String tableAlias = splitPropVal[0];
 1751  
         String colName = splitPropVal[1];
 1752  
 */
 1753  
 
 1754  0
         String searchVal = "";
 1755  
 
 1756  0
             if(fromDate != null && !"".equals(fromDate)) {
 1757  
                     try {                           
 1758  0
                             KNSServiceLocator.getDateTimeService().convertToSqlTimestamp(fromDate);                          
 1759  0
                     } catch (Exception exc) { throw new RiceRuntimeException("Invalid date format", exc); }
 1760  
             }
 1761  
             
 1762  0
         if(toDate != null && !"".equals(toDate)){
 1763  
             try{
 1764  0
                 java.sql.Timestamp dt = KNSServiceLocator.getDateTimeService().convertToSqlTimestamp(toDate);
 1765  0
                 SimpleDateFormat sdfTime = new SimpleDateFormat("HH:mm:ss");
 1766  
 
 1767  0
                 if("00:00:00".equals(sdfTime.format(dt))){
 1768  0
                     toDate += " 23:59:59";
 1769  
                 }
 1770  
             }
 1771  0
             catch (Exception exc) { throw new RiceRuntimeException("Invalid date format", exc); }
 1772  
         }
 1773  
 
 1774  
 
 1775  0
         if(fromDate != null && toDate != null && !"".equals(fromDate) && !"".equals(toDate)){
 1776  0
             searchVal = fromDate + " .. " + toDate;
 1777  
         }else{
 1778  0
             if(fromDate != null && !"".equals(fromDate)){
 1779  0
                 searchVal = ">= " + fromDate;
 1780  0
             }else if(toDate != null && !"".equals(toDate)){
 1781  0
                 searchVal = "<= " + toDate;
 1782  0
             }else searchVal =  "";
 1783  
         }
 1784  
 
 1785  0
         if(searchVal == null || "".equals(searchVal))
 1786  0
             return "";
 1787  
 
 1788  0
         Criteria crit = getSqlBuilder().createCriteria(colName, searchVal, tableName, tableAlias, java.sql.Date.class, true, true);
 1789  0
         return new StringBuffer(whereStatementClause + crit.buildWhere()).toString();
 1790  
 
 1791  
 /*
 1792  
 
 1793  
 
 1794  
         DatabasePlatform platform = getDbPlatform();
 1795  
         StringBuffer dateSqlString = new StringBuffer(whereStatementClause).append(" " + platform.escapeString(columnDbName) + " ");
 1796  
         if (fromDate != null && DocSearchUtils.getSqlFormattedDate(fromDate) != null && toDate != null && DocSearchUtils.getSqlFormattedDate(toDate) != null) {
 1797  
             return dateSqlString.append(" >= " + DocSearchUtils.getDateSQL(platform.escapeString(DocSearchUtils.getSqlFormattedDate(fromDate.trim())), null) + " and " + platform.escapeString(columnDbName) + " <= " + DocSearchUtils.getDateSQL(platform.escapeString(DocSearchUtils.getSqlFormattedDate(toDate.trim())), "23:59:59")).toString();
 1798  
         } else {
 1799  
             if (fromDate != null && DocSearchUtils.getSqlFormattedDate(fromDate) != null) {
 1800  
                 return dateSqlString.append(" >= " + DocSearchUtils.getDateSQL(platform.escapeString(DocSearchUtils.getSqlFormattedDate(fromDate.trim())), null)).toString();
 1801  
             } else if (toDate != null && DocSearchUtils.getSqlFormattedDate(toDate) != null) {
 1802  
                 return dateSqlString.append(" <= " + DocSearchUtils.getDateSQL(platform.escapeString(DocSearchUtils.getSqlFormattedDate(toDate.trim())), "23:59:59")).toString();
 1803  
             } else {
 1804  
                 return "";
 1805  
             }
 1806  
         }
 1807  
 */
 1808  
     }
 1809  
 
 1810  
     public int getDocumentSearchResultSetLimit() {
 1811  0
         return DEFAULT_SEARCH_RESULT_CAP;
 1812  
     }
 1813  
 
 1814  
     public boolean isProcessResultSet(){
 1815  0
         return this.isProcessResultSet;
 1816  
     }
 1817  
     public void setProcessResultSet(boolean isProcessResultSet){
 1818  0
         this.isProcessResultSet = isProcessResultSet;
 1819  0
     }
 1820  
 
 1821  
     public DatabasePlatform getDbPlatform() {
 1822  0
         if (dbPlatform == null) {
 1823  0
             dbPlatform = (DatabasePlatform) GlobalResourceLoader.getService(RiceConstants.DB_PLATFORM);
 1824  
         }
 1825  0
         return dbPlatform;
 1826  
     }
 1827  
 
 1828  
     public MessageMap getMessageMap(DocSearchCriteriaDTO searchCriteria) {
 1829  0
         setCriteria(searchCriteria);
 1830  0
         return this.messageMap;
 1831  
     }
 1832  
 
 1833  
     private List<String> tokenizeCriteria(String input){
 1834  0
         List<String> lRet = null;
 1835  
 
 1836  0
         lRet = Arrays.asList(input.split("\\|"));
 1837  
 
 1838  0
         return lRet;
 1839  
     }
 1840  
     private boolean validateNumber(List<String> integers) {
 1841  0
         for(String integer: integers){
 1842  
         //  if(!this.validateNumber(integer)){
 1843  
         //      return false;
 1844  
         //  }
 1845  
         }
 1846  0
         return true;
 1847  
     }
 1848  
 
 1849  
     /**
 1850  
      * @return the sqlBuilder
 1851  
      */
 1852  
     public SqlBuilder getSqlBuilder() {
 1853  0
         if(sqlBuilder == null){
 1854  0
             sqlBuilder = new SqlBuilder();
 1855  0
             sqlBuilder.setDbPlatform(getDbPlatform());
 1856  0
             sqlBuilder.setDateTimeService(KNSServiceLocator.getDateTimeService());
 1857  
         }
 1858  0
         return this.sqlBuilder;
 1859  
     }
 1860  
 
 1861  
     /**
 1862  
      * @param sqlBuilder the sqlBuilder to set
 1863  
      */
 1864  
     public void setSqlBuilder(SqlBuilder sqlBuilder) {
 1865  0
         this.sqlBuilder = sqlBuilder;
 1866  0
     }
 1867  
     
 1868  
     /**
 1869  
      * A helper method for determining whether any searchable attributes are in use for the search. Subclasses can override this method to add their
 1870  
      * own logic for checking searchable attribute existence.
 1871  
      * 
 1872  
      * @return True if the search criteria contains at least one searchable attribute or the criteria's doc type name is non-blank; false otherwise.
 1873  
      */
 1874  
     protected boolean isUsingAtLeastOneSearchAttribute() {
 1875  0
         return ( (criteria.getSearchableAttributes() != null && criteria.getSearchableAttributes().size() > 0) ||
 1876  
                 StringUtils.isNotBlank(criteria.getDocTypeFullName()) );
 1877  
     }
 1878  
 
 1879  
 }