001/** 002 * Copyright 2005-2016 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.kew.impl.document.search; 017 018import org.apache.commons.collections.CollectionUtils; 019import org.apache.commons.lang.StringUtils; 020import org.joda.time.DateTime; 021import org.kuali.rice.core.api.CoreApiServiceLocator; 022import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader; 023import org.kuali.rice.core.api.uif.RemotableAttributeError; 024import org.kuali.rice.core.api.uif.RemotableAttributeField; 025import org.kuali.rice.core.api.util.RiceConstants; 026import org.kuali.rice.core.framework.persistence.jdbc.sql.Criteria; 027import org.kuali.rice.core.framework.persistence.jdbc.sql.SqlBuilder; 028import org.kuali.rice.core.framework.persistence.platform.DatabasePlatform; 029import org.kuali.rice.kew.api.KewApiConstants; 030import org.kuali.rice.kew.api.KewApiServiceLocator; 031import org.kuali.rice.kew.api.document.Document; 032import org.kuali.rice.kew.api.document.DocumentStatus; 033import org.kuali.rice.kew.api.document.DocumentStatusCategory; 034import org.kuali.rice.kew.api.document.attribute.DocumentAttribute; 035import org.kuali.rice.kew.api.document.attribute.DocumentAttributeFactory; 036import org.kuali.rice.kew.api.document.search.DocumentSearchCriteria; 037import org.kuali.rice.kew.api.document.search.DocumentSearchResult; 038import org.kuali.rice.kew.api.document.search.DocumentSearchResults; 039import org.kuali.rice.kew.api.document.search.RouteNodeLookupLogic; 040import org.kuali.rice.kew.docsearch.DocumentSearchInternalUtils; 041import org.kuali.rice.kew.docsearch.QueryComponent; 042import org.kuali.rice.kew.docsearch.SearchableAttributeValue; 043import org.kuali.rice.kew.doctype.bo.DocumentType; 044import org.kuali.rice.kew.doctype.service.DocumentTypeService; 045import org.kuali.rice.kew.engine.node.RouteNode; 046import org.kuali.rice.kew.service.KEWServiceLocator; 047import org.kuali.rice.kew.util.PerformanceLogger; 048import org.kuali.rice.kim.api.identity.Person; 049import org.kuali.rice.kim.api.identity.principal.PrincipalContract; 050import org.kuali.rice.kim.api.services.KimApiServiceLocator; 051import org.kuali.rice.krad.util.KRADConstants; 052import org.kuali.rice.krad.util.MessageMap; 053 054import java.sql.ResultSet; 055import java.sql.SQLException; 056import java.sql.Statement; 057import java.sql.Timestamp; 058import java.util.ArrayList; 059import java.util.Collection; 060import java.util.Collections; 061import java.util.HashMap; 062import java.util.HashSet; 063import java.util.List; 064import java.util.Map; 065import java.util.Set; 066import java.util.TreeSet; 067 068 069/** 070 * Reference implementation of the {@code DocumentSearchGenerator}. 071 * 072 * @author Kuali Rice Team (rice.collab@kuali.org) 073 */ 074public class DocumentSearchGeneratorImpl implements DocumentSearchGenerator { 075 076 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DocumentSearchGeneratorImpl.class); 077 078 private static final String ROUTE_NODE_TABLE = "KREW_RTE_NODE_T"; 079 private static final String ROUTE_NODE_INST_TABLE = "KREW_RTE_NODE_INSTN_T"; 080 private static final String DATABASE_WILDCARD_CHARACTER_STRING = "%"; 081 private static final char DATABASE_WILDCARD_CHARACTER = DATABASE_WILDCARD_CHARACTER_STRING.toCharArray()[0]; 082 083 private org.kuali.rice.kew.api.doctype.DocumentTypeService apiDocumentTypeService; 084 085 private DatabasePlatform dbPlatform; 086 private MessageMap messageMap; 087 088 private SqlBuilder sqlBuilder = null; 089 090 @Override 091 public DocumentSearchCriteria clearSearch(DocumentSearchCriteria criteria) { 092 return DocumentSearchCriteria.Builder.create().build(); 093 } 094 095 public DocumentType getValidDocumentType(String documentTypeFullName) { 096 if (!org.apache.commons.lang.StringUtils.isEmpty(documentTypeFullName)) { 097 DocumentType documentType = KEWServiceLocator.getDocumentTypeService().findByNameCaseInsensitive(documentTypeFullName); 098 if (documentType == null) { 099 throw new RuntimeException("No Valid Document Type Found for document type name '" + documentTypeFullName + "'"); 100 } 101 return documentType; 102 } 103 return null; 104 } 105 106 @Override 107 public List<RemotableAttributeError> validateSearchableAttributes(DocumentSearchCriteria.Builder criteria) { 108 List<RemotableAttributeError> errors = new ArrayList<RemotableAttributeError>(); 109 DocumentType documentType = null; 110 try{ 111 documentType = getValidDocumentType(criteria.getDocumentTypeName()); 112 }catch(RuntimeException re){ 113 errors.add(RemotableAttributeError.Builder.create("documentTypeName", re.getMessage()).build()); 114 } 115 116 if (documentType != null) { 117 errors = KEWServiceLocator.getDocumentSearchCustomizationMediator().validateLookupFieldParameters(documentType, criteria.build()); 118 } else { 119 criteria.setDocumentAttributeValues(new HashMap<String, List<String>>()); 120 } 121 return errors == null ? Collections.<RemotableAttributeError>emptyList() : Collections.unmodifiableList(errors); 122 } 123 124 public QueryComponent getSearchableAttributeSql(Map<String, List<String>> documentAttributeValues, List<RemotableAttributeField> searchFields, String whereClausePredicatePrefix) { 125 126 StringBuilder fromSql = new StringBuilder(); 127 StringBuilder whereSql = new StringBuilder(); 128 129 //Map<String, List<SearchAttributeCriteriaComponent>> searchableAttributeRangeComponents = new HashMap<String,List<SearchAttributeCriteriaComponent>>(); 130 Criteria finalCriteria = null; 131 int tableIndex = 1; 132 SqlBuilder sqlBuilder = this.getSqlBuilder(); 133 134 for (String documentAttributeName : documentAttributeValues.keySet()) { 135 String documentAttributeNameForSQL = documentAttributeName; 136 if (documentAttributeName.contains(KewApiConstants.DOCUMENT_ATTRIBUTE_FIELD_PREFIX)) { 137 documentAttributeNameForSQL = documentAttributeName.replaceFirst(KewApiConstants.DOCUMENT_ATTRIBUTE_FIELD_PREFIX, ""); 138 } 139 List<String> searchValues = documentAttributeValues.get(documentAttributeName); 140 if (CollectionUtils.isEmpty(searchValues) || documentAttributeName.contains(KRADConstants.CHECKBOX_PRESENT_ON_FORM_ANNOTATION)) { 141 continue; 142 } 143 144 String tableAlias = "EXT" + tableIndex; 145 RemotableAttributeField searchField = getSearchFieldByName(documentAttributeName, searchFields); 146 String tableName = DocumentSearchInternalUtils.getAttributeTableName(searchField); 147 boolean caseSensitive = DocumentSearchInternalUtils.isLookupCaseSensitive(searchField); 148 149 Criteria crit = null; 150 151 Class<?> dataTypeClass = DocumentSearchInternalUtils.getDataTypeClass(searchField); 152 if (searchValues.size() > 1) { 153 // if there's more than one entry, we need to do an "in" 154 crit = new Criteria(tableName, tableAlias); 155 crit.setDbPlatform(sqlBuilder.getDbPlatform()); 156 crit.in("VAL", searchValues, dataTypeClass); 157 } else { 158 crit = sqlBuilder.createCriteria("VAL", searchValues.get(0) , tableName, tableAlias, dataTypeClass, !caseSensitive); 159 } 160 161 sqlBuilder.addCriteria("KEY_CD", documentAttributeNameForSQL, String.class, false, false, crit); // this is always of type string. 162 sqlBuilder.andCriteria("DOC_HDR_ID", tableAlias + ".DOC_HDR_ID", "KREW_DOC_HDR_T", "DOC_HDR", SqlBuilder.JoinType.class, false, false, crit); 163 164 if (finalCriteria == null ){ 165 finalCriteria = crit; 166 } else{ 167 sqlBuilder.andCriteria(finalCriteria, crit); 168 } 169 170 // - below is the old code 171 // if where clause is empty then use passed in prefix... otherwise generate one 172 String whereClausePrefix = (whereSql.length() == 0) ? whereClausePredicatePrefix : getGeneratedPredicatePrefix(whereSql.length()); 173 QueryComponent qc = generateSearchableAttributeSql(tableName, documentAttributeNameForSQL, whereClausePrefix, tableIndex); 174 fromSql.append(qc.getFromSql()); 175 tableIndex++; 176 } 177 178 if (finalCriteria == null) { 179 return new QueryComponent("", "", ""); 180 } 181 182 String whereClausePrefix = (whereSql.length() == 0) ? whereClausePredicatePrefix : getGeneratedPredicatePrefix(whereSql.length()); 183 184 return new QueryComponent("", fromSql.toString(), whereClausePrefix + " " + finalCriteria.buildWhere()); 185 } 186 187 private RemotableAttributeField getSearchFieldByName(String fieldName, List<RemotableAttributeField> searchFields) { 188 for (RemotableAttributeField searchField : searchFields) { 189 if (searchField.getName().equals(fieldName) 190 || searchField.getName().equals(KewApiConstants.DOCUMENT_ATTRIBUTE_FIELD_PREFIX + fieldName)) { 191 return searchField; 192 } 193 } 194 throw new IllegalStateException("Failed to locate a RemotableAttributeField for fieldName=" + fieldName); 195 } 196 197 public QueryComponent generateSearchableAttributeSql(String tableName, String documentAttributeName, String whereSqlStarter,int tableIndex) { 198 String tableIdentifier = "EXT" + tableIndex; 199 QueryComponent joinSqlComponent = getSearchableAttributeJoinSql(tableName, tableIdentifier, whereSqlStarter, documentAttributeName); 200 return new QueryComponent("", joinSqlComponent.getFromSql(), joinSqlComponent.getWhereSql()); 201 } 202 203 public QueryComponent getSearchableAttributeJoinSql(String tableName, String tableIdentifier, String whereSqlStarter, String attributeTableKeyColumnName) { 204 return new QueryComponent("", generateSearchableAttributeFromSql(tableName, tableIdentifier).toString(), generateSearchableAttributeWhereClauseJoin(whereSqlStarter, tableIdentifier, attributeTableKeyColumnName).toString()); 205 } 206 207 public StringBuilder generateSearchableAttributeWhereClauseJoin(String whereSqlStarter,String tableIdentifier,String attributeTableKeyColumnName) { 208 StringBuilder whereSql = new StringBuilder(constructWhereClauseElement(whereSqlStarter, "DOC_HDR.DOC_HDR_ID", "=", getDbPlatform().escapeString(tableIdentifier + ".DOC_HDR_ID"), null, null)); 209 whereSql.append(constructWhereClauseElement(" and ", tableIdentifier + ".KEY_CD", "=", 210 getDbPlatform().escapeString(attributeTableKeyColumnName), "'", "'")); 211 return whereSql; 212 } 213 214 public StringBuilder generateSearchableAttributeFromSql(String tableName, String tableIdentifier) { 215 if (StringUtils.isBlank(tableName)) { 216 throw new IllegalArgumentException("tableName was null or blank"); 217 } 218 if (StringUtils.isBlank(tableIdentifier)) { 219 throw new IllegalArgumentException("tableIdentifier was null or blank"); 220 } 221 StringBuilder fromSql = new StringBuilder(); 222 fromSql.append(" ,").append(tableName).append(" ").append(getDbPlatform().escapeString(tableIdentifier)).append(" "); 223 return fromSql; 224 } 225 226 public StringBuilder constructWhereClauseElement(String clauseStarter,String queryTableColumnName,String operand,String valueToSearch,String valuePrefix,String valueSuffix) { 227 StringBuilder whereSql = new StringBuilder(); 228 valuePrefix = (valuePrefix != null) ? valuePrefix : ""; 229 valueSuffix = (valueSuffix != null) ? valueSuffix : ""; 230 whereSql.append(" " + clauseStarter + " ").append(getDbPlatform().escapeString(queryTableColumnName)).append(" " + operand + " ").append(valuePrefix).append(valueToSearch).append(valueSuffix).append(" "); 231 return whereSql; 232 } 233 234 @Override 235 public DocumentSearchResults.Builder processResultSet(DocumentSearchCriteria criteria, boolean criteriaModified, Statement searchAttributeStatement, ResultSet resultSet, int maxResultCap, int fetchLimit) throws SQLException { 236 DocumentSearchCriteria.Builder criteriaBuilder = DocumentSearchCriteria.Builder.create(criteria); 237 DocumentSearchResults.Builder results = DocumentSearchResults.Builder.create(criteriaBuilder); 238 results.setCriteriaModified(criteriaModified); 239 240 List<DocumentSearchResult.Builder> resultList = new ArrayList<DocumentSearchResult.Builder>(); 241 results.setSearchResults(resultList); 242 Map<String, DocumentSearchResult.Builder> resultMap = new HashMap<String, DocumentSearchResult.Builder>(); 243 244 int startAt = (criteria.getStartAtIndex()==null) ? 0 : criteria.getStartAtIndex(); 245 int iteration = 0; 246 boolean resultSetHasNext = resultSet.next(); 247 248 PerformanceLogger perfLog = new PerformanceLogger(); 249 250 while (resultSetHasNext && resultMap.size() < maxResultCap && iteration < fetchLimit && startAt >= 0) { 251 if (iteration >= startAt) { 252 DocumentSearchResult.Builder resultBuilder = processRow(criteria, searchAttributeStatement, resultSet); 253 String documentId = resultBuilder.getDocument().getDocumentId(); 254 if (!resultMap.containsKey(documentId)) { 255 resultList.add(resultBuilder); 256 resultMap.put(documentId, resultBuilder); 257 } else { 258 // handle duplicate rows with different search data 259 DocumentSearchResult.Builder previousEntry = resultMap.get(documentId); 260 handleMultipleDocumentRows(previousEntry, resultBuilder); 261 } 262 } 263 264 iteration++; 265 resultSetHasNext = resultSet.next(); 266 } 267 268 perfLog.log("Time to read doc search results.", true); 269 // if we have threshold+1 results, then we have more results than we are going to display 270 results.setOverThreshold(resultSetHasNext); 271 272 LOG.debug("Processed " + resultMap.size() + " document search result rows."); 273 return results; 274 } 275 276 /** 277 * Handles multiple document rows by collapsing them into the list of document attributes on the existing row. 278 * The two rows must represent the same document. 279 * 280 * @param existingRow the existing row to combine the new row into 281 * @param newRow the new row from which to combine document attributes with the existing row 282 */ 283 private void handleMultipleDocumentRows(DocumentSearchResult.Builder existingRow, DocumentSearchResult.Builder newRow) { 284 for (DocumentAttribute.AbstractBuilder<?> newDocumentAttribute : newRow.getDocumentAttributes()) { 285 existingRow.getDocumentAttributes().add(newDocumentAttribute); 286 } 287 } 288 289 /** 290 * Processes the search result row, returning a DocumentSearchResult 291 * @param criteria the original search criteria 292 * @param searchAttributeStatement statement being used to call the database for queries 293 * @param rs the search result set 294 * @return a DocumentSearchResult representing the current ResultSet row 295 * @throws SQLException 296 */ 297 protected DocumentSearchResult.Builder processRow(DocumentSearchCriteria criteria, Statement searchAttributeStatement, ResultSet rs) throws SQLException { 298 299 String documentId = rs.getString("DOC_HDR_ID"); 300 String initiatorPrincipalId = rs.getString("INITR_PRNCPL_ID"); 301 String documentTypeName = rs.getString("DOC_TYP_NM"); 302 org.kuali.rice.kew.api.doctype.DocumentType documentType = 303 getApiDocumentTypeService().getDocumentTypeByName(documentTypeName); 304 if (documentType == null) { 305 throw new IllegalStateException("Failed to locate a document type with the given name: " + documentTypeName); 306 } 307 String documentTypeId = documentType.getId(); 308 309 Document.Builder documentBuilder = Document.Builder.create(documentId, initiatorPrincipalId, documentTypeName, documentTypeId); 310 DocumentSearchResult.Builder resultBuilder = DocumentSearchResult.Builder.create(documentBuilder); 311 312 String statusCode = rs.getString("DOC_HDR_STAT_CD"); 313 Timestamp createTimestamp = rs.getTimestamp("CRTE_DT"); 314 String title = rs.getString("TTL"); 315 String applicationDocumentStatus = rs.getString("APP_DOC_STAT"); 316 317 documentBuilder.setStatus(DocumentStatus.fromCode(statusCode)); 318 documentBuilder.setDateCreated(new DateTime(createTimestamp.getTime())); 319 documentBuilder.setTitle(title); 320 documentBuilder.setApplicationDocumentStatus(applicationDocumentStatus); 321 documentBuilder.setApplicationDocumentStatusDate(new DateTime(rs.getTimestamp("APP_DOC_STAT_MDFN_DT"))); 322 documentBuilder.setDateApproved(new DateTime(rs.getTimestamp("APRV_DT"))); 323 documentBuilder.setDateFinalized(new DateTime(rs.getTimestamp("FNL_DT"))); 324 documentBuilder.setApplicationDocumentId(rs.getString("APP_DOC_ID")); 325 documentBuilder.setDateLastModified(new DateTime(rs.getTimestamp("STAT_MDFN_DT"))); 326 documentBuilder.setRoutedByPrincipalId(rs.getString("RTE_PRNCPL_ID")); 327 328 // TODO - KULRICE-5755 - should probably set as many properties on the document as we can 329 documentBuilder.setDocumentHandlerUrl(rs.getString("DOC_HDLR_URL")); 330 331 if (isUsingAtLeastOneSearchAttribute(criteria)) { 332 populateDocumentAttributesValues(resultBuilder, searchAttributeStatement); 333 } 334 335 return resultBuilder; 336 } 337 338 /** 339 * This method performs searches against the search attribute value tables (see classes implementing 340 * {@link org.kuali.rice.kew.docsearch.SearchableAttributeValue}) to get data to fill in search attribute values on the given resultBuilder parameter 341 * 342 * @param resultBuilder - document search result object getting search attributes added to it 343 * @param searchAttributeStatement - statement being used to call the database for queries 344 * @throws SQLException 345 */ 346 public void populateDocumentAttributesValues(DocumentSearchResult.Builder resultBuilder, Statement searchAttributeStatement) throws SQLException { 347 searchAttributeStatement.setFetchSize(50); 348 String documentId = resultBuilder.getDocument().getDocumentId(); 349 List<SearchableAttributeValue> attributeValues = DocumentSearchInternalUtils 350 .getSearchableAttributeValueObjectTypes(); 351 PerformanceLogger perfLog = new PerformanceLogger(documentId); 352 for (SearchableAttributeValue searchAttValue : attributeValues) { 353 String attributeSql = "select KEY_CD, VAL from " + searchAttValue.getAttributeTableName() + " where DOC_HDR_ID = '" + documentId + "'"; 354 ResultSet attributeResultSet = null; 355 try { 356 attributeResultSet = searchAttributeStatement.executeQuery(attributeSql); 357 while (attributeResultSet.next()) { 358 searchAttValue.setSearchableAttributeKey(attributeResultSet.getString("KEY_CD")); 359 searchAttValue.setupAttributeValue(attributeResultSet, "VAL"); 360 if ( (!org.apache.commons.lang.StringUtils.isEmpty(searchAttValue.getSearchableAttributeKey())) && (searchAttValue.getSearchableAttributeValue() != null) ) { 361 DocumentAttribute documentAttribute = searchAttValue.toDocumentAttribute(); 362 resultBuilder.getDocumentAttributes().add(DocumentAttributeFactory.loadContractIntoBuilder( 363 documentAttribute)); 364 } 365 } 366 } finally { 367 if (attributeResultSet != null) { 368 try { 369 attributeResultSet.close(); 370 } catch (Exception e) { 371 LOG.warn("Could not close searchable attribute result set for class " + searchAttValue.getClass().getName(),e); 372 } 373 } 374 } 375 } 376 perfLog.log("Time to execute doc search search attribute queries.", true); 377 } 378 379 @SuppressWarnings("deprecation") 380 public String generateSearchSql(DocumentSearchCriteria criteria, List<RemotableAttributeField> searchFields) { 381 382 String docTypeTableAlias = "DOC1"; 383 String docHeaderTableAlias = "DOC_HDR"; 384 385 String sqlPrefix = "Select * from ("; 386 String sqlSuffix = ") FINAL_SEARCH order by FINAL_SEARCH.CRTE_DT desc"; 387 388 // the DISTINCT here is important as it filters out duplicate rows which could occur as the result of doc search extension values... 389 StringBuilder selectSQL = new StringBuilder("select DISTINCT("+ docHeaderTableAlias +".DOC_HDR_ID), " 390 + StringUtils.join(new String[] { 391 docHeaderTableAlias + ".INITR_PRNCPL_ID", 392 docHeaderTableAlias + ".DOC_HDR_STAT_CD", 393 docHeaderTableAlias + ".CRTE_DT", 394 docHeaderTableAlias + ".TTL", 395 docHeaderTableAlias + ".APP_DOC_STAT", 396 docHeaderTableAlias + ".STAT_MDFN_DT", 397 docHeaderTableAlias + ".APRV_DT", 398 docHeaderTableAlias + ".FNL_DT", 399 docHeaderTableAlias + ".APP_DOC_ID", 400 docHeaderTableAlias + ".RTE_PRNCPL_ID", 401 docHeaderTableAlias + ".APP_DOC_STAT_MDFN_DT", 402 docTypeTableAlias + ".DOC_TYP_NM", 403 docTypeTableAlias + ".LBL", 404 docTypeTableAlias + ".DOC_HDLR_URL", 405 docTypeTableAlias + ".ACTV_IND" 406 }, ", ")); 407 StringBuilder fromSQL = new StringBuilder(" from KREW_DOC_TYP_T "+ docTypeTableAlias +" "); 408 StringBuilder fromSQLForDocHeaderTable = new StringBuilder(", KREW_DOC_HDR_T " + docHeaderTableAlias + " "); 409 410 StringBuilder whereSQL = new StringBuilder(); 411 whereSQL.append(getDocumentIdSql(criteria.getDocumentId(), getGeneratedPredicatePrefix(whereSQL.length()), docHeaderTableAlias)); 412 // if principalId criteria exists ignore deprecated principalName search term 413 String principalInitiatorIdSql = getInitiatorIdSql(criteria.getInitiatorPrincipalId(), getGeneratedPredicatePrefix(whereSQL.length())); 414 if (StringUtils.isNotBlank(principalInitiatorIdSql)) { 415 whereSQL.append(principalInitiatorIdSql); 416 } else { 417 whereSQL.append(getInitiatorSql(criteria.getInitiatorPrincipalName(), getGeneratedPredicatePrefix(whereSQL.length()))); 418 } 419 whereSQL.append(getAppDocIdSql(criteria.getApplicationDocumentId(), getGeneratedPredicatePrefix(whereSQL.length()))); 420 whereSQL.append(getDateCreatedSql(criteria.getDateCreatedFrom(), criteria.getDateCreatedTo(), getGeneratedPredicatePrefix(whereSQL.length()))); 421 whereSQL.append(getDateLastModifiedSql(criteria.getDateLastModifiedFrom(), criteria.getDateLastModifiedTo(), getGeneratedPredicatePrefix(whereSQL.length()))); 422 whereSQL.append(getDateApprovedSql(criteria.getDateApprovedFrom(), criteria.getDateApprovedTo(), getGeneratedPredicatePrefix(whereSQL.length()))); 423 whereSQL.append(getDateFinalizedSql(criteria.getDateFinalizedFrom(), criteria.getDateFinalizedTo(), getGeneratedPredicatePrefix(whereSQL.length()))); 424 425 // flags for the table being added to the FROM class of the sql 426 String principalViewerSql = getViewerSql(criteria.getViewerPrincipalName(), getGeneratedPredicatePrefix(whereSQL.length())); 427 String principalViewerIdSql = getViewerIdSql(criteria.getViewerPrincipalId(), getGeneratedPredicatePrefix(whereSQL.length())); 428 // if principalId criteria exists ignore deprecated principalName search term 429 if (StringUtils.isNotBlank(principalViewerIdSql)){ 430 principalViewerSql = ""; 431 } 432 String groupViewerSql = getGroupViewerSql(criteria.getGroupViewerId(), getGeneratedPredicatePrefix(whereSQL.length())); 433 if (StringUtils.isNotBlank(principalViewerSql) || StringUtils.isNotBlank(groupViewerSql) || StringUtils.isNotBlank(principalViewerIdSql) ) { 434 whereSQL.append(principalViewerSql); 435 whereSQL.append(principalViewerIdSql); 436 whereSQL.append(groupViewerSql); 437 fromSQL.append(", KREW_ACTN_RQST_T "); 438 } 439 440 String principalApproverSql = getApproverSql(criteria.getApproverPrincipalName(), getGeneratedPredicatePrefix(whereSQL.length())); 441 String principalApproverIdSql = getApproverIdSql(criteria.getApproverPrincipalId(), getGeneratedPredicatePrefix(whereSQL.length())); 442 // if principalId criteria exists ignore deprecated principalName search term 443 if (StringUtils.isNotBlank(principalApproverIdSql)){ 444 principalApproverSql = ""; 445 } 446 if (StringUtils.isNotBlank(principalApproverSql) || StringUtils.isNotBlank(principalApproverIdSql)) { 447 whereSQL.append(principalApproverSql); 448 whereSQL.append(principalApproverIdSql); 449 fromSQL.append(", KREW_ACTN_TKN_T "); 450 } 451 452 453 454 String docRouteNodeSql = getDocRouteNodeSql(criteria.getDocumentTypeName(), criteria.getRouteNodeName(), criteria.getRouteNodeLookupLogic(), getGeneratedPredicatePrefix(whereSQL.length())); 455 if (StringUtils.isNotBlank(docRouteNodeSql)) { 456 whereSQL.append(docRouteNodeSql); 457 fromSQL.append(", KREW_RTE_NODE_INSTN_T "); 458 fromSQL.append(", KREW_RTE_NODE_T "); 459 } 460 461 if (!criteria.getDocumentAttributeValues().isEmpty()) { 462 QueryComponent queryComponent = getSearchableAttributeSql(criteria.getDocumentAttributeValues(), searchFields, getGeneratedPredicatePrefix( 463 whereSQL.length())); 464 selectSQL.append(queryComponent.getSelectSql()); 465 fromSQL.append(queryComponent.getFromSql()); 466 whereSQL.append(queryComponent.getWhereSql()); 467 } 468 469 whereSQL.append(getDocTypeFullNameWhereSql(criteria, getGeneratedPredicatePrefix(whereSQL.length()))); 470 whereSQL.append(getDocTitleSql(criteria.getTitle(), getGeneratedPredicatePrefix(whereSQL.length()))); 471 whereSQL.append(getDocumentStatusSql(criteria.getDocumentStatuses(), criteria.getDocumentStatusCategories(), getGeneratedPredicatePrefix(whereSQL.length()))); 472 whereSQL.append(getGeneratedPredicatePrefix(whereSQL.length())).append(" DOC_HDR.DOC_TYP_ID = DOC1.DOC_TYP_ID "); 473 fromSQL.append(fromSQLForDocHeaderTable); 474 475 // App Doc Status Value and Transition clauses 476 String statusTransitionWhereClause = getStatusTransitionDateSql(criteria.getDateApplicationDocumentStatusChangedFrom(), criteria.getDateApplicationDocumentStatusChangedTo(), getGeneratedPredicatePrefix(whereSQL.length())); 477 478 List<String> applicationDocumentStatuses = criteria.getApplicationDocumentStatuses(); 479 // deal with legacy usage of applicationDocumentStatus (which is deprecated) 480 if (!StringUtils.isBlank(criteria.getApplicationDocumentStatus())) { 481 if (!criteria.getApplicationDocumentStatuses().contains(criteria.getApplicationDocumentStatus())) { 482 applicationDocumentStatuses = new ArrayList<String>(criteria.getApplicationDocumentStatuses()); 483 applicationDocumentStatuses.add(criteria.getApplicationDocumentStatus()); 484 } 485 } 486 487 whereSQL.append(getAppDocStatusesSql(applicationDocumentStatuses, getGeneratedPredicatePrefix( 488 whereSQL.length()), statusTransitionWhereClause.length())); 489 if (statusTransitionWhereClause.length() > 0){ 490 whereSQL.append(statusTransitionWhereClause); 491 whereSQL.append(getGeneratedPredicatePrefix(whereSQL.length())).append(" DOC_HDR.DOC_HDR_ID = STAT_TRAN.DOC_HDR_ID "); 492 fromSQL.append(", KREW_APP_DOC_STAT_TRAN_T STAT_TRAN "); 493 } 494 495 String finalizedSql = sqlPrefix + " " + selectSQL.toString() + " " + fromSQL.toString() + " " + whereSQL.toString() + " " + sqlSuffix; 496 497 LOG.info("*********** SEARCH SQL ***************"); 498 LOG.info(finalizedSql); 499 LOG.info("**************************************"); 500 return finalizedSql; 501 } 502 503 public String getDocumentIdSql(String documentId, String whereClausePredicatePrefix, String tableAlias) { 504 if (StringUtils.isBlank(documentId)) { 505 return ""; 506 } else { 507 // Using true for caseInsensitive causes bad performance for MYSQL databases since function indexes cannot be added. 508 // Due to this, false is passed for caseInsensitive 509 Criteria crit = getSqlBuilder().createCriteria("DOC_HDR_ID", documentId, "KREW_DOC_HDR_T", tableAlias, String.class, false, true); 510 return new StringBuilder(whereClausePredicatePrefix + crit.buildWhere()).toString(); 511 } 512 } 513 514 public String getDocTitleSql(String docTitle, String whereClausePredicatePrefix) { 515 if (StringUtils.isBlank(docTitle)) { 516 return ""; 517 } else { 518 // quick and dirty ' replacement that isn't the best but should work for all dbs 519 docTitle = docTitle.trim().replace("\'", "\'\'"); 520 SqlBuilder sqlBuild = new SqlBuilder(); 521 Criteria crit = new Criteria("KREW_DOC_HDR_T", "DOC_HDR"); 522 sqlBuild.addCriteria("TTL", docTitle, String.class, true, true, crit); 523 return new StringBuilder(whereClausePredicatePrefix + crit.buildWhere()).toString(); 524 } 525 } 526 527 // special methods that return the sql needed to complete the search 528 // or nothing if the field was not filled in 529 public String getAppDocIdSql(String appDocId, String whereClausePredicatePrefix) { 530 if (StringUtils.isBlank(appDocId)) { 531 return ""; 532 } else { 533 String tableAlias = "DOC_HDR"; 534 Criteria crit = getSqlBuilder().createCriteria("APP_DOC_ID", appDocId, "KREW_DOC_HDR_T", tableAlias,String.class); 535 return new StringBuilder(whereClausePredicatePrefix + crit.buildWhere()).toString(); 536 } 537 } 538 539 public String getDateCreatedSql(DateTime fromDateCreated, DateTime toDateCreated, String whereClausePredicatePrefix) { 540 return establishDateString(fromDateCreated, toDateCreated, "KREW_DOC_HDR_T", "DOC_HDR", "CRTE_DT", whereClausePredicatePrefix); 541 } 542 543 public String getDateApprovedSql(DateTime fromDateApproved, DateTime toDateApproved, String whereClausePredicatePrefix) { 544 return establishDateString(fromDateApproved, toDateApproved, "KREW_DOC_HDR_T", "DOC_HDR", "APRV_DT", whereClausePredicatePrefix); 545 } 546 547 public String getDateFinalizedSql(DateTime fromDateFinalized, DateTime toDateFinalized, String whereClausePredicatePrefix) { 548 return establishDateString(fromDateFinalized, toDateFinalized, "KREW_DOC_HDR_T", "DOC_HDR", "FNL_DT", whereClausePredicatePrefix); 549 } 550 551 public String getDateLastModifiedSql(DateTime fromDateLastModified, DateTime toDateLastModified, String whereClausePredicatePrefix) { 552 return establishDateString(fromDateLastModified, toDateLastModified, "KREW_DOC_HDR_T", "DOC_HDR", "STAT_MDFN_DT", whereClausePredicatePrefix); 553 } 554 555 public String getStatusTransitionDateSql(DateTime fromStatusTransitionDate, DateTime toStatusTransitionDate, String whereClausePredicatePrefix) { 556 return establishDateString(fromStatusTransitionDate, toStatusTransitionDate, "KREW_DOC_HDR_T", "DOC_HDR", "APP_DOC_STAT_MDFN_DT", whereClausePredicatePrefix); 557 } 558 559 public String getViewerSql(String viewer, String whereClausePredicatePrefix) { 560 StringBuilder returnSql = new StringBuilder(); 561 if (StringUtils.isNotBlank(viewer)) { 562 Map<String, String> m = new HashMap<String, String>(); 563 m.put("principalName", viewer); 564 565 // This will search for people with the ability for the valid operands. 566 List<Person> personList = KimApiServiceLocator.getPersonService().findPeople(m, false); 567 List<String> principalList = new ArrayList<String>(); 568 569 if(CollectionUtils.isEmpty(personList)) { 570 // findPeople allows for wildcards, but the person must be active. If no one was found, 571 // check for an exact inactive user. 572 PrincipalContract tempPrincipal = KimApiServiceLocator.getIdentityService().getPrincipalByPrincipalName(viewer.trim()); 573 if (tempPrincipal != null) { 574 principalList.add(tempPrincipal.getPrincipalId()); 575 } else { 576 // they entered something that returned nothing... so we should return nothing 577 578 return new StringBuilder(whereClausePredicatePrefix + " 1 = 0 ").toString(); 579 } 580 } 581 582 for (Person person : personList){ 583 principalList.add(person.getPrincipalId()); 584 } 585 586 Criteria crit = new Criteria("KREW_ACTN_RQST_T", "KREW_ACTN_RQST_T"); 587 crit.in("PRNCPL_ID", principalList, String.class); 588 returnSql.append(whereClausePredicatePrefix + "( (DOC_HDR.DOC_HDR_ID = KREW_ACTN_RQST_T.DOC_HDR_ID and " + crit.buildWhere() + " )"); 589 590 Set<String> viewerGroupIds = new TreeSet<String>(); 591 592 if(CollectionUtils.isNotEmpty(principalList)) { 593 for(String principalId: principalList){ 594 viewerGroupIds.addAll(KimApiServiceLocator.getGroupService().getGroupIdsByPrincipalId(principalId)); 595 } 596 } 597 598 // Documents routed to users as part of a workgoup should be returned. 599 // Use Chad's escape stuff 600 if (viewerGroupIds != null && !viewerGroupIds.isEmpty()) { 601 602 returnSql.append(" or ( " + 603 "DOC_HDR.DOC_HDR_ID = KREW_ACTN_RQST_T.DOC_HDR_ID " + 604 "and KREW_ACTN_RQST_T.GRP_ID in ("); 605 606 boolean first = true; 607 for (String groupId : viewerGroupIds){ 608 if(!first){ 609 returnSql.append(","); 610 } 611 returnSql.append("'").append(groupId).append("'"); 612 first = false; 613 } 614 returnSql.append("))"); 615 } 616 returnSql.append(")"); 617 } 618 return returnSql.toString(); 619 } 620 621 public String getViewerIdSql(String viewerId, String whereClausePredicatePrefix) { 622 StringBuilder returnSql = new StringBuilder(); 623 if (StringUtils.isNotBlank(viewerId)) { 624 Map<String, String> m = new HashMap<String, String>(); 625 m.put("principalId", viewerId); 626 627 // This will search for people with the ability for the valid operands. 628 List<Person> personList = KimApiServiceLocator.getPersonService().findPeople(m, false); 629 List<String> principalList = new ArrayList<String>(); 630 631 if(CollectionUtils.isEmpty(personList)) { 632 // they entered something that returned nothing... so we should return nothing 633 return new StringBuilder(whereClausePredicatePrefix + " 1 = 0 ").toString(); 634 } 635 636 for (Person person : personList){ 637 principalList.add(person.getPrincipalId()); 638 } 639 640 Criteria crit = new Criteria("KREW_ACTN_RQST_T", "KREW_ACTN_RQST_T"); 641 crit.in("PRNCPL_ID", principalList, String.class); 642 returnSql.append(whereClausePredicatePrefix + "( DOC_HDR.DOC_HDR_ID = KREW_ACTN_RQST_T.DOC_HDR_ID and " + crit.buildWhere() + " )"); 643 } 644 return returnSql.toString(); 645 } 646 647 public String getGroupViewerSql(String groupId, String whereClausePredicatePrefix) { 648 String sql = ""; 649 if (StringUtils.isNotBlank(groupId)) { 650 sql = whereClausePredicatePrefix + " DOC_HDR.DOC_HDR_ID = KREW_ACTN_RQST_T.DOC_HDR_ID and KREW_ACTN_RQST_T.GRP_ID = '" + groupId + "'"; 651 } 652 return sql; 653 } 654 655 public String getInitiatorSql(String initiatorPrincipalName, String whereClausePredicatePrefix) { 656 657 if (StringUtils.isBlank(initiatorPrincipalName)) { 658 return ""; 659 } 660 661 String tableAlias = "DOC_HDR"; 662 663 Map<String, String> m = new HashMap<String, String>(); 664 m.put("principalName", initiatorPrincipalName); 665 666 // This will search for people with the ability for the valid operands. 667 List<Person> pList = KimApiServiceLocator.getPersonService().findPeople(m, false); 668 List<String> principalList = new ArrayList<String>(); 669 670 if(pList == null || pList.isEmpty() ){ 671 // findPeople allows for wildcards, but the person must be active. If no one was found, 672 // check for an exact inactive user. 673 PrincipalContract tempPrincipal = KimApiServiceLocator.getIdentityService().getPrincipalByPrincipalName(initiatorPrincipalName.trim()); 674 if (tempPrincipal != null) { 675 principalList.add(tempPrincipal.getPrincipalId()); 676 } else { 677 // they entered something that returned nothing... so we should return nothing 678 return new StringBuilder(whereClausePredicatePrefix + " 1 = 0 ").toString(); 679 } 680 } 681 682 for(Person p: pList){ 683 principalList.add(p.getPrincipalId()); 684 } 685 686 Criteria crit = new Criteria("KREW_DOC_HDR_T", tableAlias); 687 crit.in("INITR_PRNCPL_ID", principalList, String.class); 688 689 return new StringBuilder(whereClausePredicatePrefix + crit.buildWhere()).toString(); 690 } 691 692 public String getInitiatorIdSql(String initiatorPrincipalId, String whereClausePredicatePrefix) { 693 694 if (StringUtils.isBlank(initiatorPrincipalId)) { 695 return ""; 696 } 697 698 String tableAlias = "DOC_HDR"; 699 700 Map<String, String> m = new HashMap<String, String>(); 701 m.put("principalId", initiatorPrincipalId); 702 703 // This will search for people with the ability for the valid operands. 704 List<Person> pList = KimApiServiceLocator.getPersonService().findPeople(m, false); 705 List<String> principalList = new ArrayList<String>(); 706 707 if(pList == null || pList.isEmpty() ){ 708 // they entered something that returned nothing... so we should return nothing 709 return new StringBuilder(whereClausePredicatePrefix + " 1 = 0 ").toString(); 710 } 711 712 for(Person p: pList){ 713 principalList.add(p.getPrincipalId()); 714 } 715 716 Criteria crit = new Criteria("KREW_DOC_HDR_T", tableAlias); 717 crit.in("INITR_PRNCPL_ID", principalList, String.class); 718 719 return new StringBuilder(whereClausePredicatePrefix + crit.buildWhere()).toString(); 720 } 721 722 public String getApproverSql(String approver, String whereClausePredicatePrefix) { 723 String returnSql = ""; 724 if (StringUtils.isNotBlank(approver)) { 725 Map<String, String> m = new HashMap<String, String>(); 726 m.put("principalName", approver); 727 728 // This will search for people with the ability for the valid operands. 729 List<Person> pList = KimApiServiceLocator.getPersonService().findPeople(m, false); 730 List<String> principalList = new ArrayList<String>(); 731 732 if(pList == null || pList.isEmpty() ){ 733 // findPeople allows for wildcards, but the person must be active. If no one was found, 734 // check for an exact inactive user. 735 PrincipalContract tempPrincipal = KimApiServiceLocator.getIdentityService().getPrincipalByPrincipalName(approver.trim()); 736 737 if (tempPrincipal != null) { 738 principalList.add(tempPrincipal.getPrincipalId()); 739 } else { 740 // they entered something that returned nothing... so we should return nothing 741 return new StringBuilder(whereClausePredicatePrefix + " 1 = 0 ").toString(); 742 } 743 } 744 745 for(Person p: pList){ 746 principalList.add(p.getPrincipalId()); 747 } 748 749 Criteria crit = new Criteria("KREW_ACTN_TKN_T", "KREW_ACTN_TKN_T"); 750 crit.in("PRNCPL_ID", principalList, String.class); 751 752 returnSql = whereClausePredicatePrefix + 753 " DOC_HDR.DOC_HDR_ID = KREW_ACTN_TKN_T.DOC_HDR_ID and upper(KREW_ACTN_TKN_T.ACTN_CD) in ('" + 754 KewApiConstants.ACTION_TAKEN_APPROVED_CD + "','" + KewApiConstants.ACTION_TAKEN_BLANKET_APPROVE_CD + "')" + 755 " and " + crit.buildWhere(); 756 } 757 return returnSql; 758 } 759 760 public String getApproverIdSql(String approverId, String whereClausePredicatePrefix) { 761 String returnSql = ""; 762 if (StringUtils.isNotBlank(approverId)) { 763 Map<String, String> m = new HashMap<String, String>(); 764 m.put("principalId", approverId); 765 766 // This will search for people with the ability for the valid operands. 767 List<Person> pList = KimApiServiceLocator.getPersonService().findPeople(m, false); 768 List<String> principalList = new ArrayList<String>(); 769 770 if(pList == null || pList.isEmpty() ){ 771 // they entered something that returned nothing... so we should return nothing 772 return new StringBuilder(whereClausePredicatePrefix + " 1 = 0 ").toString(); 773 } 774 775 for(Person p: pList){ 776 principalList.add(p.getPrincipalId()); 777 } 778 779 Criteria crit = new Criteria("KREW_ACTN_TKN_T", "KREW_ACTN_TKN_T"); 780 crit.in("PRNCPL_ID", principalList, String.class); 781 782 returnSql = whereClausePredicatePrefix + 783 " DOC_HDR.DOC_HDR_ID = KREW_ACTN_TKN_T.DOC_HDR_ID and upper(KREW_ACTN_TKN_T.ACTN_CD) in ('" + 784 KewApiConstants.ACTION_TAKEN_APPROVED_CD + "','" + KewApiConstants.ACTION_TAKEN_BLANKET_APPROVE_CD + "')" + 785 " and " + crit.buildWhere(); 786 } 787 return returnSql; 788 } 789 790 public String getDocTypeFullNameWhereSql(DocumentSearchCriteria criteria, String whereClausePredicatePrefix) { 791 List<String> documentTypeNamesToSearch = new ArrayList<String>(); 792 String primaryDocumentTypeName = criteria.getDocumentTypeName(); 793 if (StringUtils.isNotBlank(primaryDocumentTypeName)) { 794 documentTypeNamesToSearch.add(primaryDocumentTypeName); 795 } 796 documentTypeNamesToSearch.addAll(criteria.getAdditionalDocumentTypeNames()); 797 StringBuilder returnSql = new StringBuilder(""); 798 if (CollectionUtils.isNotEmpty(documentTypeNamesToSearch)) { 799 int index = 0; 800 for (String documentTypeName : documentTypeNamesToSearch) { 801 if (StringUtils.isNotBlank(documentTypeName)) { 802 String clause = index++ == 0 ? "" : " or "; 803 DocumentTypeService docSrv = KEWServiceLocator.getDocumentTypeService(); 804 DocumentType docType = docSrv.findByNameCaseInsensitive(documentTypeName.trim()); 805 if (docType != null) { 806 if (documentTypeName.contains("*") || documentTypeName.contains("%")) { 807 addDocumentTypeLikeNameToSearchOn(returnSql, documentTypeName.trim(), clause); 808 } else { 809 addDocumentTypeNameToSearchOn(returnSql, documentTypeName.trim(), clause); 810 } 811 if (docType.getChildrenDocTypes() != null) { 812 addChildDocumentTypes(returnSql, docType.getChildrenDocTypes()); 813 } 814 } else{ 815 addDocumentTypeLikeNameToSearchOn(returnSql, documentTypeName.trim(), clause); 816 } 817 } 818 } 819 } 820 if (returnSql.length() > 0) { 821 returnSql.insert(0, "("); 822 returnSql.insert(0, whereClausePredicatePrefix); 823 returnSql.append(")"); 824 } 825 return returnSql.toString(); 826 } 827 828 public void addChildDocumentTypes(StringBuilder whereSql, Collection<DocumentType> childDocumentTypes) { 829 for (DocumentType child : childDocumentTypes) { 830 addDocumentTypeNameToSearchOn(whereSql, child.getName()); 831 addChildDocumentTypes(whereSql, child.getChildrenDocTypes()); 832 } 833 } 834 835 public void addDocumentTypeNameToSearchOn(StringBuilder whereSql, String documentTypeName) { 836 this.addDocumentTypeNameToSearchOn(whereSql, documentTypeName, " or "); 837 } 838 839 public void addDocumentTypeNameToSearchOn(StringBuilder whereSql, String documentTypeName, String clause) { 840 whereSql.append(clause).append("upper(DOC1.DOC_TYP_NM) = '" + documentTypeName.toUpperCase() + "'"); 841 } 842 public void addDocumentTypeLikeNameToSearchOn(StringBuilder whereSql, String documentTypeName, String clause) { 843 documentTypeName = documentTypeName.replace('*', '%'); 844 whereSql.append(clause).append(" upper(DOC1.DOC_TYP_NM) LIKE '" + documentTypeName.toUpperCase() + "'"); 845 } 846 847 public String getDocRouteNodeSql(String documentTypeFullName, String routeNodeName, RouteNodeLookupLogic docRouteLevelLogic, String whereClausePredicatePrefix) { 848 // -1 is the default 'blank' choice from the route node drop down a number is used because the ojb RouteNode object is used to 849 // render the node choices on the form. 850 String returnSql = ""; 851 if (StringUtils.isNotBlank(routeNodeName)) { 852 if (docRouteLevelLogic == null) { 853 docRouteLevelLogic = RouteNodeLookupLogic.EXACTLY; 854 } 855 StringBuilder routeNodeCriteria = new StringBuilder("and " + ROUTE_NODE_TABLE + ".NM "); 856 if (RouteNodeLookupLogic.EXACTLY == docRouteLevelLogic) { 857 routeNodeCriteria.append("= '" + getDbPlatform().escapeString(routeNodeName) + "' "); 858 } else { 859 routeNodeCriteria.append("in ("); 860 // below buffer used to facilitate the addition of the string ", " to separate out route node names 861 StringBuilder routeNodeInCriteria = new StringBuilder(); 862 boolean foundSpecifiedNode = false; 863 List<RouteNode> routeNodes = KEWServiceLocator.getRouteNodeService().getFlattenedNodes(getValidDocumentType(documentTypeFullName), true); 864 for (RouteNode routeNode : routeNodes) { 865 if (routeNodeName.equals(routeNode.getRouteNodeName())) { 866 // current node is specified node so we ignore it outside of the boolean below 867 foundSpecifiedNode = true; 868 continue; 869 } 870 // below logic should be to add the current node to the criteria if we haven't found the specified node 871 // and the logic qualifier is 'route nodes before specified'... or we have found the specified node and 872 // the logic qualifier is 'route nodes after specified' 873 if ( (!foundSpecifiedNode && RouteNodeLookupLogic.BEFORE == docRouteLevelLogic) || 874 (foundSpecifiedNode && RouteNodeLookupLogic.AFTER == docRouteLevelLogic) ) { 875 if (routeNodeInCriteria.length() > 0) { 876 routeNodeInCriteria.append(", "); 877 } 878 routeNodeInCriteria.append("'" + routeNode.getRouteNodeName() + "'"); 879 } 880 } 881 if (routeNodeInCriteria.length() > 0) { 882 routeNodeCriteria.append(routeNodeInCriteria); 883 } else { 884 routeNodeCriteria.append("''"); 885 } 886 routeNodeCriteria.append(") "); 887 } 888 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() + " "; 889 } 890 return returnSql; 891 } 892 893 public String getDocumentStatusSql(List<DocumentStatus> documentStatuses, List<DocumentStatusCategory> categories, String whereClausePredicatePrefix) { 894 if (CollectionUtils.isEmpty(documentStatuses) && CollectionUtils.isEmpty(categories)) { 895 return whereClausePredicatePrefix + "DOC_HDR.DOC_HDR_STAT_CD != '" + DocumentStatus.INITIATED.getCode() + "'"; 896 } else { 897 // include all given document statuses 898 Set<DocumentStatus> statusesToInclude = new HashSet<DocumentStatus>(documentStatuses); 899 900 // add all statuses from each category 901 for (DocumentStatusCategory category : categories) { 902 Set<DocumentStatus> categoryStatuses = DocumentStatus.getStatusesForCategory(category); 903 statusesToInclude.addAll(categoryStatuses); 904 } 905 906 Set<String> statusCodes = new HashSet<String>(); 907 for (DocumentStatus statusToInclude : statusesToInclude) { 908 statusCodes.add("'" + getDbPlatform().escapeString(statusToInclude.getCode()) + "'"); 909 } 910 return whereClausePredicatePrefix + " DOC_HDR.DOC_HDR_STAT_CD in (" + StringUtils.join(statusCodes, ", ") +")"; 911 } 912 } 913 914 /** 915 * This method generates the where clause fragment related to Application Document Status. 916 * If the Status values only are defined, search for the appDocStatus value in the route header. 917 * If either the transition from/to dates are defined, search agains the status transition history. 918 */ 919 public String getAppDocStatusesSql(List<String> appDocStatuses, String whereClausePredicatePrefix, int statusTransitionWhereClauseLength) { 920 if (CollectionUtils.isEmpty(appDocStatuses)) { 921 return ""; 922 } else { 923 String inList = buildAppDocStatusInList(appDocStatuses); 924 925 if (statusTransitionWhereClauseLength > 0){ 926 return whereClausePredicatePrefix + " STAT_TRAN.APP_DOC_STAT_TO" + inList; 927 } else { 928 return whereClausePredicatePrefix + " DOC_HDR.APP_DOC_STAT" + inList; 929 } 930 } 931 } 932 933 private String buildAppDocStatusInList(List<String> appDocStatuses) { 934 StringBuilder sql = new StringBuilder(" IN ("); 935 936 boolean first = true; 937 for (String appDocStatus : appDocStatuses) { 938 // commas before each element except the first one 939 if (first) { 940 first = false; 941 } else { 942 sql.append(","); 943 } 944 945 sql.append("'"); 946 sql.append(getDbPlatform().escapeString(appDocStatus.trim())); 947 sql.append("'"); 948 } 949 950 sql.append(")"); 951 952 return sql.toString(); 953 } 954 955 public String getGeneratedPredicatePrefix(int whereClauseSize) { 956 return (whereClauseSize > 0) ? " and " : " where "; 957 } 958 959 public String establishDateString(DateTime fromDate, DateTime toDate, String tableName, String tableAlias, String colName, String whereStatementClause) { 960 961 String fromDateValue = null; 962 if (fromDate != null) { 963 fromDateValue = CoreApiServiceLocator.getDateTimeService().toDateString(fromDate.toDate()); 964 } 965 966 String toDateValue = null; 967 if (toDate != null) { 968 toDateValue = CoreApiServiceLocator.getDateTimeService().toDateString(toDate.toDate()); 969 toDateValue += " 23:59:59"; 970 } 971 972 String searchValue = null; 973 if (fromDateValue != null && toDateValue != null) { 974 searchValue = fromDateValue + " .. " + toDateValue; 975 } else if (fromDateValue != null) { 976 searchValue = ">= " + fromDateValue; 977 } else if (toDateValue != null) { 978 searchValue = "<= " + toDateValue; 979 } else { 980 return ""; 981 } 982 983 Criteria crit = getSqlBuilder().createCriteria(colName, searchValue, tableName, tableAlias, java.sql.Date.class, true, true); 984 return new StringBuilder(whereStatementClause).append(crit.buildWhere()).toString(); 985 986 } 987 988 public DatabasePlatform getDbPlatform() { 989 if (dbPlatform == null) { 990 dbPlatform = (DatabasePlatform) GlobalResourceLoader.getService(RiceConstants.DB_PLATFORM); 991 } 992 return dbPlatform; 993 } 994 995 public SqlBuilder getSqlBuilder() { 996 if(sqlBuilder == null){ 997 sqlBuilder = new SqlBuilder(); 998 sqlBuilder.setDbPlatform(getDbPlatform()); 999 sqlBuilder.setDateTimeService(CoreApiServiceLocator.getDateTimeService()); 1000 } 1001 return this.sqlBuilder; 1002 } 1003 1004 public void setSqlBuilder(SqlBuilder sqlBuilder) { 1005 this.sqlBuilder = sqlBuilder; 1006 } 1007 1008 /** 1009 * A helper method for determining whether any searchable attributes are in use for the search. 1010 * 1011 * @return True if the search criteria contains at least one searchable attribute or the criteria's doc type name is 1012 * non-blank; false otherwise. 1013 */ 1014 protected boolean isUsingAtLeastOneSearchAttribute(DocumentSearchCriteria criteria) { 1015 return criteria.getDocumentAttributeValues().size() > 0 || StringUtils.isNotBlank(criteria.getDocumentTypeName()); 1016 } 1017 1018 protected org.kuali.rice.kew.api.doctype.DocumentTypeService getApiDocumentTypeService() { 1019 if (apiDocumentTypeService == null) { 1020 apiDocumentTypeService = KewApiServiceLocator.getDocumentTypeService(); 1021 } 1022 1023 return apiDocumentTypeService; 1024 } 1025 1026 protected void setApiDocumentTypeService(org.kuali.rice.kew.api.doctype.DocumentTypeService apiDocumentTypeService) { 1027 this.apiDocumentTypeService = apiDocumentTypeService; 1028 } 1029 1030}