View Javadoc

1   /*
2    * Copyright 2005-2007 The Kuali Foundation
3    *
4    *
5    * Licensed under the Educational Community License, Version 2.0 (the "License");
6    * you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    * http://www.opensource.org/licenses/ecl2.php
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.kuali.rice.kew.docsearch;
18  
19  import java.util.ArrayList;
20  import java.util.HashMap;
21  import java.util.HashSet;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.Set;
26  
27  import org.apache.commons.lang.StringUtils;
28  import org.kuali.rice.core.config.ConfigContext;
29  import org.kuali.rice.core.exception.RiceRuntimeException;
30  import org.kuali.rice.kew.doctype.bo.DocumentType;
31  import org.kuali.rice.kew.doctype.service.DocumentTypeService;
32  import org.kuali.rice.kew.service.KEWServiceLocator;
33  import org.kuali.rice.kew.util.KEWConstants;
34  import org.kuali.rice.kew.util.KEWPropertyConstants;
35  import org.kuali.rice.kew.util.Utilities;
36  import org.kuali.rice.kew.web.KeyValueSort;
37  import org.kuali.rice.kns.util.KNSConstants;
38  import org.kuali.rice.kns.web.format.Formatter;
39  import org.kuali.rice.kns.web.ui.Column;
40  import org.kuali.rice.kns.web.ui.Field;
41  import org.kuali.rice.kns.web.ui.Row;
42  
43  /**
44   *
45   * @author Kuali Rice Team (rice.collab@kuali.org)
46   */
47  public class StandardDocumentSearchResultProcessor implements
48  		DocumentSearchResultProcessor {
49  	private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger
50  			.getLogger(StandardDocumentSearchResultProcessor.class);
51  
52  	private Map<String, Boolean> sortableByKey = new HashMap<String, Boolean>();
53  	private Map<String, String> labelsByKey = new HashMap<String, String>();
54  	private DocSearchCriteriaDTO searchCriteria;
55  	private String searchingUser;
56  	private boolean processFinalResults = true;
57  //FIXME: Chris get rid of all sortable references!
58  	/**
59  	 * @return the searchCriteria
60  	 */
61  	public DocSearchCriteriaDTO getSearchCriteria() {
62  		return searchCriteria;
63  	}
64  
65  	/**
66  	 * @param searchCriteria
67  	 *            the searchCriteria to set
68  	 */
69  	public void setSearchCriteria(DocSearchCriteriaDTO searchCriteria) {
70  		this.searchCriteria = searchCriteria;
71  	}
72  
73  	/**
74  	 * @return the searchingUser
75  	 */
76  	public String getSearchingUser() {
77  		return searchingUser;
78  	}
79  
80  	/**
81  	 * @param searchingUser
82  	 *            the searchingUser to set
83  	 */
84  	public void setSearchingUser(String searchingUser) {
85  		this.searchingUser = searchingUser;
86  	}
87  
88  	public List<Column> getCustomDisplayColumns() {
89  		return new ArrayList<Column>();
90  	}
91  
92  	public List<Column> setUpCustomDisplayColumns(
93  			DocSearchCriteriaDTO criteria, List<Column> columns) {
94  		for (Column column : columns) {
95  			if (column instanceof Column) {
96  				Column dsColumn = (Column) column;
97  				for (org.kuali.rice.kns.web.ui.Field field : getFields(criteria)) {
98  					if (field instanceof Field) {
99  						Field dsField = (Field) field;
100 						dsColumn.setFormatter((Formatter)dsField.getFormatter());
101 					} else {
102 						throw new RiceRuntimeException(
103 								"field must be of type org.kuali.rice.kew.docsearch.Field");
104 					}
105 				}
106 			} else {
107 				throw new RiceRuntimeException(
108 						"column must be of type org.kuali.rice.kew.docsearch.DocumentSearchColumn");
109 			}
110 		}
111 		return columns;
112 	}
113 
114 	public List<Column> getAndSetUpCustomDisplayColumns(
115 			DocSearchCriteriaDTO criteria) {
116 		List<Column> columns = getCustomDisplayColumns();
117 		return setUpCustomDisplayColumns(criteria, columns);
118 	}
119 
120 	public boolean getShowAllStandardFields() {
121 		return true;
122 	}
123 
124 	public boolean getOverrideSearchableAttributes() {
125 		return false;
126 	}
127 
128 	/**
129 	 * Convenience method to find a specific searchable attribute
130 	 *
131 	 * @param name
132 	 *            - name of search attribute savable property name
133 	 * @return the SearchAttributeCriteriaComponent object related to the given
134 	 *         key name or null if component is not found
135 	 */
136 	public SearchAttributeCriteriaComponent getSearchableAttributeByFieldName(
137 			String name) {
138 		if (StringUtils.isBlank(name)) {
139 			throw new IllegalArgumentException(
140 					"Attempted to find Searchable Attribute with blank Field name '"
141 							+ name + "'");
142 		}
143 		for (Iterator iter = getSearchCriteria().getSearchableAttributes()
144 				.iterator(); iter.hasNext();) {
145 			SearchAttributeCriteriaComponent critComponent = (SearchAttributeCriteriaComponent) iter
146 					.next();
147 			if (name.equals(critComponent.getFormKey())) {
148 				return critComponent;
149 			}
150 		}
151 		return null;
152 	}
153 
154 	/*
155 	 * (non-Javadoc)
156 	 *
157 	 * @seeorg.kuali.rice.kew.docsearch.DocumentSearchResultProcessor#
158 	 * processIntoFinalResults(java.util.List,
159 	 * org.kuali.rice.kew.docsearch.DocSearchCriteriaDTO,
160 	 * org.kuali.rice.kew.user.WorkflowUser)
161 	 */
162 	public DocumentSearchResultComponents processIntoFinalResults(
163 			List<DocSearchDTO> docSearchResultRows,
164 			DocSearchCriteriaDTO criteria, String principalId) {
165 		this.setSearchCriteria(criteria);
166 		this.setSearchingUser(principalId);
167 		List columns = constructColumnList(criteria, docSearchResultRows);
168 
169 		List<DocumentSearchResult> documentSearchResults = new ArrayList<DocumentSearchResult>();
170 		for (Iterator iter = docSearchResultRows.iterator(); iter.hasNext();) {
171 			DocSearchDTO docCriteriaDTO = (DocSearchDTO) iter.next();
172 			DocumentSearchResult docSearchResult = this.generateSearchResult(
173 					docCriteriaDTO, columns);
174 			if (docSearchResult != null) {
175 				documentSearchResults.add(docSearchResult);
176 			}
177 		}
178 		return new DocumentSearchResultComponents(columns,
179 				documentSearchResults);
180 	}
181 
182 	/**
183 	 * Method to construct a list of columns in order of how they should appear
184 	 * in the search results
185 	 *
186 	 * @return a list of columns in an ordered list that will be used to
187 	 *         generate the final search results
188 	 */
189 	public List<Column> constructColumnList(
190 			DocSearchCriteriaDTO criteria, List<DocSearchDTO> docSearchResultRows) {
191 		List<Column> tempColumns = new ArrayList<Column>();
192 		List<Column> customDisplayColumnNames = getAndSetUpCustomDisplayColumns(criteria);
193 		if ((!getShowAllStandardFields())
194 				&& (getOverrideSearchableAttributes())) {
195 			// use only what is contained in displayColumns
196 			this.addAllCustomColumns(tempColumns, criteria,
197 					customDisplayColumnNames);
198 		} else if (getShowAllStandardFields()
199 				&& (getOverrideSearchableAttributes())) {
200 			// do standard fields and use displayColumns for searchable
201 			// attributes
202 			this.addStandardSearchColumns(tempColumns, docSearchResultRows);
203 			this.addAllCustomColumns(tempColumns, criteria,
204 					customDisplayColumnNames);
205 		} else if ((!getShowAllStandardFields())
206 				&& (!getOverrideSearchableAttributes())) {
207 			// do displayColumns and then do standard searchable attributes
208 			this.addCustomStandardCriteriaColumns(tempColumns, criteria,
209 					customDisplayColumnNames);
210 			this
211 					.addSearchableAttributeColumnsNoOverrides(tempColumns,
212 							criteria);
213 		}
214 		if (tempColumns.isEmpty()) {
215 			// do default
216 			this.addStandardSearchColumns(tempColumns, docSearchResultRows);
217 			this.addSearchableAttributeColumnsNoOverrides(tempColumns,
218 							criteria);
219 		}
220 
221 		List<Column> columns = new ArrayList<Column>();
222 		this.addRouteHeaderIdColumn(columns);
223 		columns.addAll(tempColumns);
224 		this.addRouteLogColumn(columns);
225 		return columns;
226 	}
227 
228 	public void addStandardSearchColumns(List<Column> columns, List<DocSearchDTO> docSearchResultRows) {
229 		this.addColumnUsingKey(
230 						columns,
231 						KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_DOC_TYPE_LABEL);
232 		this.addColumnUsingKey(
233 						columns,
234 						KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_DOCUMENT_TITLE);
235 		this.addColumnUsingKey(
236 						columns,
237 						KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_ROUTE_STATUS_DESC);
238 		addDocStatusColumn(columns, docSearchResultRows);
239 		this.addColumnUsingKey(columns,
240 				KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_INITIATOR);
241 		this.addColumnUsingKey(
242 						columns,
243 						KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_DATE_CREATED);
244 	}
245 
246 	public void addRouteHeaderIdColumn(List<Column> columns) {
247 		this
248 				.addColumnUsingKey(
249 						columns,
250 						KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_ROUTE_HEADER_ID);
251 	}
252 
253 	public void addRouteLogColumn(List<Column> columns) {
254 		this.addColumnUsingKey(columns,
255 				KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_ROUTE_LOG);
256 	}
257 
258 	public void addDocStatusColumn(List<Column> columns, List<DocSearchDTO> docSearchResultRows) {
259 		// add this column if document status policy is defined as "both".
260 		for (DocSearchDTO myDTO : docSearchResultRows) {
261 	    	DocumentType docType = KEWServiceLocator.getDocumentTypeService().findByName(myDTO.getDocTypeName());
262 	    	if (docType.isAppDocStatusInUse()){
263 	    		this.addColumnUsingKey(columns,
264 	    				KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_DOC_STATUS);
265 	    		break;
266 	    	}
267 		}
268 		return;
269 	}
270 
271 	public void addSearchableAttributeColumnsNoOverrides(
272 			List<Column> columns, DocSearchCriteriaDTO criteria) {
273 		this
274 				.addSearchableAttributeColumnsBasedOnFields(columns, criteria,
275 						null);
276 	}
277 
278 	public void addSearchableAttributeColumnsBasedOnFields(
279 			List<Column> columns, DocSearchCriteriaDTO criteria,
280 			List<String> searchAttributeFieldNames) {
281 		Set<String> alreadyProcessedFieldKeys = new HashSet<String>();
282 		List<Field> fields = this
283 				.getFields(criteria, searchAttributeFieldNames);
284 		for (Field field : fields) {
285 			if (field instanceof Field) {
286 				Field dsField = (Field) field;
287 				if ((dsField.getPropertyName() == null)
288 						|| (!alreadyProcessedFieldKeys.contains(dsField
289 								.getPropertyName()))) {
290 					if (dsField.isColumnVisible()) {
291 						if (Field.SEARCH_RESULT_DISPLAYABLE_FIELD_TYPES
292 								.contains(dsField.getFieldType())) {
293 							String resultFieldLabel = dsField.getFieldLabel();
294 							if (dsField.isMemberOfRange()) {
295 								resultFieldLabel = dsField.getMainFieldLabel();
296 							}
297 							this.addSearchableAttributeColumnUsingKey(columns,
298 									dsField.getFormatter(), dsField.getPropertyName(),
299 									resultFieldLabel, Boolean.TRUE,
300 									Boolean.TRUE);
301 							if (dsField.getPropertyName() != null) {
302 								alreadyProcessedFieldKeys.add(dsField
303 										.getPropertyName());
304 							}
305 
306 						}
307 					}
308 				}
309 			} else {
310 				throw new RiceRuntimeException(
311 						"Fields must be of type org.kuali.rice.kew.docsearch.Field");
312 			}
313 		}
314 	}
315 
316 	public void addAllCustomColumns(List<Column> columns,
317 			DocSearchCriteriaDTO criteria,
318 			List<Column> customDisplayColumns) {
319 		for (Column customColumn : customDisplayColumns) {
320 			this.addCustomColumn(columns, customColumn);
321 		}
322 	}
323 
324 	public void addCustomStandardCriteriaColumns(
325 			List<Column> columns, DocSearchCriteriaDTO criteria,
326 			List<Column> customDisplayColumns) {
327 		for (Column customColumn : customDisplayColumns) {
328 			if (KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_SET
329 					.contains(customColumn.getPropertyName())) {
330 				this.addCustomColumn(columns, customColumn);
331 			}
332 		}
333 	}
334 
335 	public void addCustomColumn(List<Column> columns,
336 			Column customColumn) {
337 
338 		addColumnUsingKey(columns,
339 				customColumn.getPropertyName(), customColumn.getColumnTitle(), new Boolean(customColumn.getSortable()));
340 	}
341 
342 	public List<Field> getFields(DocSearchCriteriaDTO criteria) {
343 		return getFields(criteria, null);
344 	}
345 
346 	public DocumentType getDocumentType(String documentTypeName) {
347 		DocumentType documentType = null;
348 		if (StringUtils.isNotBlank(documentTypeName)) {
349 			documentType = ((DocumentTypeService) KEWServiceLocator
350 					.getService(KEWServiceLocator.DOCUMENT_TYPE_SERVICE))
351 					.findByName(documentTypeName);
352 		}
353 		return documentType;
354 	}
355 
356 	public List<Field> getFields(DocSearchCriteriaDTO criteria,
357 			List<String> searchAttributeFieldNames) {
358 		List<Field> returnFields = new ArrayList<Field>();
359 		DocumentType documentType = getDocumentType(criteria
360 				.getDocTypeFullName());
361 		if (documentType != null) {
362 			List<Field> allFields = new ArrayList<Field>();
363 			for (SearchableAttribute searchableAttribute : documentType
364 					.getSearchableAttributes()) {
365 				List<Row> searchRows = searchableAttribute
366 						.getSearchingRows(DocSearchUtils
367 								.getDocumentSearchContext("", documentType
368 										.getName(), ""));
369 				if (searchRows == null) {
370 					continue;
371 				}
372 				for (Row row : searchRows) {
373 					allFields.addAll(row.getFields());
374 				}
375 			}
376 			if (searchAttributeFieldNames == null) {
377 				returnFields = allFields;
378 			} else {
379 				for (String searchAttributeName : searchAttributeFieldNames) {
380 					for (Field field : allFields) {
381 						Field dsField = (Field) field;
382 						if (field instanceof Field) {
383 							if (searchAttributeName.equals(dsField
384 									.getPropertyName())) {
385 								returnFields.add(field);
386 							}
387 						} else {
388 							throw new RiceRuntimeException(
389 									"Fields must be of type org.kuali.rice.kns.Field");
390 						}
391 					}
392 				}
393 			}
394 		}
395 		return returnFields;
396 	}
397 
398 	public DocumentSearchResult generateSearchResult(
399 			DocSearchDTO docCriteriaDTO, List<Column> columns) {
400 		Map<String, Object> alternateSortValues = getSortValuesMap(docCriteriaDTO);
401 		DocumentSearchResult docSearchResult = null;
402 		for (Iterator iterator = columns.iterator(); iterator.hasNext();) {
403 			Column currentColumn = (Column) iterator
404 					.next();
405 			KeyValueSort kvs = generateSearchResult(docCriteriaDTO,
406 					currentColumn, alternateSortValues);
407 			if (kvs != null) {
408 				if (docSearchResult == null) {
409 					docSearchResult = new DocumentSearchResult();
410 				}
411 				docSearchResult.addResultContainer(kvs);
412 			}
413 		}
414 		return docSearchResult;
415 	}
416 
417 	public class DisplayValues {
418 		public String htmlValue;
419 		public String userDisplayValue;
420 	}
421 
422 	public KeyValueSort generateSearchResult(DocSearchDTO docCriteriaDTO,
423 			Column column,
424 			Map<String, Object> sortValuesByColumnKey) {
425 		KeyValueSort returnValue = null;
426 		DisplayValues fieldValue = null;
427 		Object sortFieldValue = null;
428 		String columnKeyName = column.getPropertyName();
429 		SearchableAttributeValue attributeValue = null;
430 
431 		if (KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_ROUTE_HEADER_ID
432 				.equals(columnKeyName)) {
433 			fieldValue = this.getRouteHeaderIdFieldDisplayValue(docCriteriaDTO
434 					.getRouteHeaderId().toString(), docCriteriaDTO
435 					.isUsingSuperUserSearch(), docCriteriaDTO.getDocTypeName());
436 			sortFieldValue = sortValuesByColumnKey.get(columnKeyName);
437 		} else if (KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_ROUTE_LOG
438 				.equals(columnKeyName)) {
439 			fieldValue = this.getRouteLogFieldDisplayValue(docCriteriaDTO
440 					.getRouteHeaderId().toString());
441 			sortFieldValue = sortValuesByColumnKey.get(columnKeyName);
442 		} else if (KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_DATE_CREATED
443 				.equals(columnKeyName)) {
444 			fieldValue = new DisplayValues();
445 			fieldValue.htmlValue = DocSearchUtils
446 					.getDisplayValueWithDateTime(docCriteriaDTO
447 							.getDateCreated());
448 			sortFieldValue = sortValuesByColumnKey.get(columnKeyName);
449 		} else if (KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_DOC_TYPE_LABEL
450 				.equals(columnKeyName)) {
451 			fieldValue = new DisplayValues();
452 			fieldValue.htmlValue = docCriteriaDTO.getDocTypeLabel();
453 			sortFieldValue = sortValuesByColumnKey.get(columnKeyName);
454 		} else if (KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_DOCUMENT_TITLE
455 				.equals(columnKeyName)) {
456 			fieldValue = new DisplayValues();
457 			fieldValue.htmlValue = docCriteriaDTO.getDocumentTitle();
458 			sortFieldValue = sortValuesByColumnKey.get(columnKeyName);
459 		} else if (KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_INITIATOR
460 				.equals(columnKeyName)) {
461 			fieldValue = this.getInitiatorFieldDisplayValue(docCriteriaDTO
462 					.getInitiatorTransposedName(), docCriteriaDTO
463 					.getInitiatorWorkflowId());
464 			sortFieldValue = sortValuesByColumnKey.get(columnKeyName);
465 		} else if (KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_ROUTE_STATUS_DESC
466 				.equals(columnKeyName)) {
467 			fieldValue = new DisplayValues();
468 			fieldValue.htmlValue = docCriteriaDTO.getDocRouteStatusCodeDesc();
469 			sortFieldValue = sortValuesByColumnKey.get(columnKeyName);
470 		} else if (KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_DOC_STATUS
471 				.equals(columnKeyName)) {
472 			fieldValue = new DisplayValues();
473 			fieldValue.htmlValue = docCriteriaDTO.getAppDocStatus();
474 			sortFieldValue = sortValuesByColumnKey.get(columnKeyName);
475 		} else {
476 			// check searchable attributes
477 			for (Iterator iter = docCriteriaDTO.getSearchableAttributes()
478 					.iterator(); iter.hasNext();) {
479 				KeyValueSort searchAttribute = (KeyValueSort) iter.next();
480 				if (searchAttribute.getKey().equals(columnKeyName)) {
481 					Object sortValue = sortValuesByColumnKey.get(columnKeyName);
482 					sortFieldValue = (sortValue != null) ? sortValue
483 							: searchAttribute.getSortValue();
484 					attributeValue = searchAttribute.getSearchableAttributeValue();
485 					fieldValue = new DisplayValues();
486 					fieldValue.htmlValue = searchAttribute.getValue();
487 					break;
488 				}
489 			}
490 		}
491 		if (fieldValue != null) {
492 			String userDisplaySortValue = fieldValue.userDisplayValue;
493 			if (StringUtils.isBlank(userDisplaySortValue)) {
494 				userDisplaySortValue = fieldValue.htmlValue;
495 			}
496 			returnValue = new KeyValueSort(columnKeyName, fieldValue.htmlValue,
497 					fieldValue.userDisplayValue,
498 					(sortFieldValue != null) ? sortFieldValue
499 							: userDisplaySortValue, attributeValue);
500 		}
501 		return returnValue;
502 	}
503 
504 	/*
505 	 * Convenience Methods to get field values for certain Workflow Standard
506 	 * Search Result columns
507 	 */
508 
509 	public DisplayValues getRouteLogFieldDisplayValue(String routeHeaderId) {
510 		DisplayValues dv = new DisplayValues();
511 		String linkPopup = "";
512 		if (this.isRouteLogPopup()) {
513 			linkPopup = " target=\"_new\"";
514 		}
515 		String imageSource = "<img alt=\"Route Log for Document\" src=\"images/my_route_log.gif\"/>";
516 		dv.htmlValue = "<a href=\"RouteLog.do?routeHeaderId=" + routeHeaderId
517 				+ "\"" + linkPopup + ">" + imageSource + "</a>";
518 		dv.userDisplayValue = imageSource;
519 		return dv;
520 	}
521 
522 	public DisplayValues getRouteHeaderIdFieldDisplayValue(
523 			String routeHeaderId, boolean isSuperUserSearch,
524 			String documentTypeName) {
525 		return this.getValueEncodedWithDocHandlerUrl(routeHeaderId,
526 				routeHeaderId, isSuperUserSearch, documentTypeName);
527 	}
528 
529 	public DisplayValues getInitiatorFieldDisplayValue(
530 			String fieldLinkTextValue, String initiatorWorkflowId) {
531 		DisplayValues dv = new DisplayValues();
532 		
533 		dv.htmlValue = "<a href=\""+ ConfigContext.getCurrentContextConfig().getKRBaseURL() +
534 		"/inquiry.do?businessObjectClassName=org.kuali.rice.kim.bo.impl.PersonImpl&" +
535 		"methodToCall=continueWithInquiry&principalId="+ initiatorWorkflowId
536 				+ "\" target=\"_blank\">"
537 				+ fieldLinkTextValue + "</a>";
538 		dv.userDisplayValue = fieldLinkTextValue;
539 		return dv;
540 	}
541 
542 	/**
543 	 * Convenience method to allow child classes to use a custom value string
544 	 * and wrap that string in the document handler URL
545 	 *
546 	 * @param value
547 	 *            - the value that will show on screen as the clickable link
548 	 * @param routeHeaderId
549 	 *            - the string value of the route header id the doc handler
550 	 *            should point to
551 	 * @param isSuperUserSearch
552 	 *            - boolean indicating whether this search is a super user
553 	 *            search or not see
554 	 *            {@link org.kuali.rice.kew.docsearch.DocSearchDTO#isUsingSuperUserSearch()}
555 	 * @return the fully encoded html for a link using the text from the input
556 	 *         parameter 'value'
557 	 */
558 	public DisplayValues getValueEncodedWithDocHandlerUrl(String value,
559 			String routeHeaderId, boolean isSuperUserSearch,
560 			String documentTypeName) {
561 		DisplayValues dv = new DisplayValues();
562 		dv.htmlValue = getDocHandlerUrlPrefix(routeHeaderId, isSuperUserSearch,
563 				documentTypeName)
564 				+ value + getDocHandlerUrlSuffix(isSuperUserSearch);
565 		dv.userDisplayValue = value;
566 		return dv;
567 	}
568 
569 	public Map<String, Object> getSortValuesMap(DocSearchDTO docCriteriaDTO) {
570 		Map<String, Object> alternateSort = new HashMap<String, Object>();
571 		alternateSort
572 				.put(
573 						KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_ROUTE_HEADER_ID,
574 						docCriteriaDTO.getRouteHeaderId());
575 		if (StringUtils.isNotBlank(docCriteriaDTO.getInitiatorTransposedName())) {
576 		    alternateSort.put(
577 				    KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_INITIATOR,
578 				    docCriteriaDTO.getInitiatorTransposedName());
579 		}
580 		else {
581 			alternateSort.put(
582 					KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_INITIATOR,
583 					docCriteriaDTO.getInitiatorWorkflowId());		
584 		}
585 		alternateSort
586 				.put(
587 						KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_DATE_CREATED,
588 						docCriteriaDTO.getDateCreated());
589 		return alternateSort;
590 	}
591 
592 	public Map<String, Boolean> getSortableByKey() {
593 		if (sortableByKey.isEmpty()) {
594 			sortableByKey = constructSortableByKey();
595 		}
596 		return sortableByKey;
597 	}
598 
599 	public Map<String, Boolean> constructSortableColumnByKey() {
600 		Map<String, Boolean> sortable = new HashMap<String, Boolean>();
601 		sortable
602 				.put(
603 						KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_ROUTE_HEADER_ID,
604 						Boolean.TRUE);
605 		sortable
606 				.put(
607 						KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_DOC_TYPE_LABEL,
608 						Boolean.TRUE);
609 		sortable
610 				.put(
611 						KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_DOCUMENT_TITLE,
612 						Boolean.TRUE);
613 		sortable
614 				.put(
615 						KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_ROUTE_STATUS_DESC,
616 						Boolean.TRUE);
617 		sortable
618 			.put(
619 				KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_DOC_STATUS,
620 				Boolean.TRUE);
621 		sortable.put(
622 				KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_INITIATOR,
623 				Boolean.TRUE);
624 		sortable
625 				.put(
626 						KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_DATE_CREATED,
627 						Boolean.TRUE);
628 		sortable.put(
629 				KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_ROUTE_LOG,
630 				Boolean.FALSE);
631 		return sortable;
632 	}
633 
634 	public Map<String, Boolean> getSortableColumnByKey() {
635 		if (sortableByKey.isEmpty()) {
636 			sortableByKey = constructSortableByKey();
637 		}
638 		return sortableByKey;
639 	}
640 
641 	public Map<String, Boolean> constructSortableByKey() {
642 		Map<String, Boolean> sortable = new HashMap<String, Boolean>();
643 		sortable
644 				.put(
645 						KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_ROUTE_HEADER_ID,
646 						Boolean.TRUE);
647 		sortable
648 				.put(
649 						KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_DOC_TYPE_LABEL,
650 						Boolean.TRUE);
651 		sortable
652 				.put(
653 						KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_DOCUMENT_TITLE,
654 						Boolean.TRUE);
655 		sortable
656 				.put(
657 						KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_ROUTE_STATUS_DESC,
658 						Boolean.TRUE);
659 		sortable.put(
660 				KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_DOC_STATUS,
661 				Boolean.TRUE);
662 		sortable.put(
663 				KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_INITIATOR,
664 				Boolean.TRUE);
665 		sortable
666 				.put(
667 						KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_DATE_CREATED,
668 						Boolean.TRUE);
669 		sortable.put(
670 				KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_ROUTE_LOG,
671 				Boolean.FALSE);
672 		return sortable;
673 	}
674 
675 	public Map<String, String> getLabelsByKey() {
676 		if (labelsByKey.isEmpty()) {
677 			labelsByKey = constructLabelsByKey();
678 		}
679 		return labelsByKey;
680 	}
681 
682 	public Map<String, String> constructLabelsByKey() {
683 		return new HashMap<String, String>();
684 	}
685 
686 	/*
687 	 * Below columns are for convenience for overriding classes
688 	 */
689 
690 	public void addColumnUsingKey(List<Column> columns, String key) {
691 		this.addColumnUsingKey(columns, key,
692 				null, null);
693 	}
694 
695 	public void addColumnUsingKey(List<Column> columns, String key, String label) {
696 		this.addColumnUsingKey(columns, key, label, null);
697 	}
698 
699 	public void addColumnUsingKey(List<Column> columns, String key, Boolean sortable) {
700 		this.addColumnUsingKey(columns, key, null, sortable);
701 	}
702 
703 	public void addColumnUsingKey(List<Column> columns, String key, String label,
704 			Boolean sortable) {
705 		columns.add(this.constructColumnUsingKey(key, label,
706 				sortable));
707 	}
708 
709 	public void addSearchableAttributeColumnUsingKey(
710 			List<Column> columns,
711 			String key, String label,
712 			Boolean sortableOverride, Boolean defaultSortable) {
713 		columns.add(this
714 				.constructColumnUsingKey(key, label,
715 						(sortableOverride != null) ? sortableOverride
716 								: defaultSortable));
717 	}
718 
719    public void addSearchableAttributeColumnUsingKey(
720         List<Column> columns,
721         Formatter formatter, String key, String label,
722         Boolean sortableOverride, Boolean defaultSortable) {
723         Column column = this.constructColumnUsingKey(key, label,
724                 (sortableOverride != null) ? sortableOverride
725                         : defaultSortable);
726         //if (formatter != null) {
727         column.setFormatter(formatter);
728         //}
729         columns.add(column);
730     }
731 
732 
733 	/*
734 	 * Below methods should probably not be overriden by overriding classes but
735 	 * could be if desired
736 	 */
737 
738 	public Column constructColumnUsingKey(String key, String label,
739 			Boolean sortable) {
740 		if (sortable == null) {
741 			sortable = getSortableByKey().get(key);
742 		}
743 		if (label == null) {
744 			label = getLabelsByKey().get(key);
745 		}
746 		Column c = new Column(
747 				label,key);
748 		return c;
749 	}
750 
751 	public boolean isDocumentHandlerPopup() {
752 	    return Utilities.getKNSParameterBooleanValue(
753 	            KEWConstants.KEW_NAMESPACE,
754 	            KNSConstants.DetailTypes.DOCUMENT_SEARCH_DETAIL_TYPE,
755 	            KEWConstants.DOCUMENT_SEARCH_DOCUMENT_POPUP_IND);
756 	}
757 
758 	public boolean isRouteLogPopup() {
759 		return Utilities.getKNSParameterBooleanValue(
760 				KEWConstants.KEW_NAMESPACE,
761 				KNSConstants.DetailTypes.DOCUMENT_SEARCH_DETAIL_TYPE,
762 				KEWConstants.DOCUMENT_SEARCH_ROUTE_LOG_POPUP_IND);
763 	}
764 
765 	public String getDocHandlerUrlPrefix(String routeHeaderId,
766 			boolean superUserSearch, String documentTypeName) {
767 		String linkPopup = "";
768 		if (this.isDocumentHandlerPopup()) {
769 			linkPopup = " target=\"_blank\"";
770 		}
771 		if (superUserSearch) {
772 			String url = "<a href=\"SuperUser.do?methodToCall=displaySuperUserDocument&routeHeaderId="
773 					+ routeHeaderId + "\"" + linkPopup + " >";
774 			if (!getDocumentType(documentTypeName)
775 					.getUseWorkflowSuperUserDocHandlerUrl().getPolicyValue()
776 					.booleanValue()) {
777 				url = "<a href=\"" + KEWConstants.DOC_HANDLER_REDIRECT_PAGE
778 						+ "?" + KEWConstants.COMMAND_PARAMETER + "="
779 						+ KEWConstants.SUPERUSER_COMMAND + "&"
780 						+ KEWConstants.ROUTEHEADER_ID_PARAMETER + "="
781 						+ routeHeaderId + "\"" + linkPopup + ">";
782 			}
783 			return url;
784 		} else {
785 			return "<a href=\"" + KEWConstants.DOC_HANDLER_REDIRECT_PAGE + "?"
786 					+ KEWConstants.COMMAND_PARAMETER + "="
787 					+ KEWConstants.DOCSEARCH_COMMAND + "&"
788 					+ KEWConstants.ROUTEHEADER_ID_PARAMETER + "="
789 					+ routeHeaderId + "\"" + linkPopup + ">";
790 		}
791 	}
792 
793 	public String getDocHandlerUrlSuffix(boolean superUserSearch) {
794 		if (superUserSearch) {
795 			return "</a>";
796 		} else {
797 			return "</a>";
798 		}
799 	}
800 
801     /**
802      * This overridden method ...
803      * 
804      * @see org.kuali.rice.kew.docsearch.DocumentSearchResultProcessor#setProcessResultSet(boolean)
805      */
806     public void setProcessFinalResults(boolean isProcessFinalResults) {
807        this.processFinalResults = isProcessFinalResults;
808     }
809 
810     /**
811      * This overridden method ...
812      * 
813      * @see org.kuali.rice.kew.docsearch.DocumentSearchResultProcessor#isProcessResultSet()
814      */
815     public boolean isProcessFinalResults() {
816         return this.processFinalResults;
817     }
818 }