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