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