View Javadoc

1   /**
2    * Copyright 2005-2012 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kew.rule.bo;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.kuali.rice.core.api.uif.RemotableAttributeField;
20  import org.kuali.rice.core.api.uif.RemotableCheckbox;
21  import org.kuali.rice.core.api.uif.RemotableCheckboxGroup;
22  import org.kuali.rice.core.api.uif.RemotableRadioButtonGroup;
23  import org.kuali.rice.core.api.uif.RemotableSelect;
24  import org.kuali.rice.core.api.uif.RemotableTextInput;
25  import org.kuali.rice.core.api.util.ConcreteKeyValue;
26  import org.kuali.rice.core.api.util.KeyValue;
27  import org.kuali.rice.core.api.util.RiceKeyConstants;
28  import org.kuali.rice.core.web.format.BooleanFormatter;
29  import org.kuali.rice.core.web.format.CollectionFormatter;
30  import org.kuali.rice.core.web.format.DateFormatter;
31  import org.kuali.rice.core.web.format.Formatter;
32  import org.kuali.rice.kew.api.KewApiServiceLocator;
33  import org.kuali.rice.kew.api.rule.RuleTemplate;
34  import org.kuali.rice.kew.api.rule.RuleTemplateAttribute;
35  import org.kuali.rice.kew.api.validation.ValidationResults;
36  import org.kuali.rice.kew.framework.KewFrameworkServiceLocator;
37  import org.kuali.rice.kew.framework.rule.attribute.WorkflowRuleAttributeHandlerService;
38  import org.kuali.rice.kew.lookupable.MyColumns;
39  import org.kuali.rice.kew.rule.RuleBaseValues;
40  import org.kuali.rice.kew.rule.service.RuleServiceInternal;
41  import org.kuali.rice.kew.rule.service.RuleTemplateService;
42  import org.kuali.rice.kew.service.KEWServiceLocator;
43  import org.kuali.rice.kim.api.KimConstants;
44  import org.kuali.rice.kim.api.group.Group;
45  import org.kuali.rice.kim.api.group.GroupService;
46  import org.kuali.rice.kim.api.identity.Person;
47  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
48  import org.kuali.rice.kns.document.authorization.BusinessObjectRestrictions;
49  import org.kuali.rice.kns.lookup.HtmlData;
50  import org.kuali.rice.kns.lookup.KualiLookupableHelperServiceImpl;
51  import org.kuali.rice.kns.lookup.LookupableHelperService;
52  import org.kuali.rice.kns.service.KNSServiceLocator;
53  import org.kuali.rice.kns.util.FieldUtils;
54  import org.kuali.rice.kns.web.comparator.CellComparatorHelper;
55  import org.kuali.rice.kns.web.struts.form.LookupForm;
56  import org.kuali.rice.kns.web.ui.Column;
57  import org.kuali.rice.kns.web.ui.Field;
58  import org.kuali.rice.kns.web.ui.ResultRow;
59  import org.kuali.rice.kns.web.ui.Row;
60  import org.kuali.rice.krad.bo.BusinessObject;
61  import org.kuali.rice.krad.bo.PersistableBusinessObject;
62  import org.kuali.rice.krad.exception.ValidationException;
63  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
64  import org.kuali.rice.krad.util.GlobalVariables;
65  import org.kuali.rice.krad.util.KRADConstants;
66  import org.kuali.rice.krad.util.ObjectUtils;
67  
68  import java.sql.Date;
69  import java.util.ArrayList;
70  import java.util.Collection;
71  import java.util.HashMap;
72  import java.util.Iterator;
73  import java.util.List;
74  import java.util.Map;
75  
76  /**
77   * This is a description of what this class does - jjhanso don't forget to fill this in.
78   *
79   * @author Kuali Rice Team (rice.collab@kuali.org)
80   *
81   */
82  public class RuleBaseValuesLookupableHelperServiceImpl extends KualiLookupableHelperServiceImpl {
83      private List<Row> rows = new ArrayList<Row>();
84      //private List<Column> columns = establishColumns();
85      //private Long previousRuleTemplateId;
86      private LookupableHelperService ruleDelegationLookupableHelperService;
87      private List<?> delegationPkNames;
88  
89      private static final String RULE_TEMPLATE_PROPERTY_NAME = "ruleTemplate.name";
90      private static final String RULE_ID_PROPERTY_NAME = "id";
91      private static final String RULE_TEMPLATE_ID_PROPERTY_NAME = "ruleTemplateId";
92      private static final String ACTIVE_IND_PROPERTY_NAME = "active";
93      private static final String DELEGATE_RULE_PROPERTY_NAME = "delegateRule";
94      private static final String GROUP_REVIEWER_PROPERTY_NAME = "groupReviewer";
95      private static final String GROUP_REVIEWER_NAME_PROPERTY_NAME = "groupReviewerName";
96      private static final String GROUP_REVIEWER_NAMESPACE_PROPERTY_NAME = "groupReviewerNamespace";
97      private static final String PERSON_REVIEWER_PROPERTY_NAME = "personReviewer";
98      private static final String PERSON_REVIEWER_TYPE_PROPERTY_NAME = "personReviewerType";
99      private static final String DOC_TYP_NAME_PROPERTY_NAME = "documentType.name";
100     private static final String RULE_DESC_PROPERTY_NAME = "description";
101 
102     private static final String BACK_LOCATION = "backLocation";
103     private static final String DOC_FORM_KEY = "docFormKey";
104     private static final String INVALID_WORKGROUP_ERROR = "The Group Reviewer Namespace and Name combination is not valid";
105     private static final String INVALID_PERSON_ERROR = "The Person Reviewer is not valid";
106 
107     @Override
108 	public List<Row> getRows() {
109         List<Row> superRows = super.getRows();
110         List<Row> returnRows = new ArrayList<Row>();
111         returnRows.addAll(superRows);
112         returnRows.addAll(rows);
113         return returnRows;
114     }
115 
116     @Override
117     public boolean checkForAdditionalFields(Map<String, String> fieldValues) {
118         String ruleTemplateNameParam = (String) fieldValues.get(RULE_TEMPLATE_PROPERTY_NAME);
119 
120         if (StringUtils.isNotBlank(ruleTemplateNameParam)) {
121             rows = new ArrayList<Row>();
122             RuleTemplate ruleTemplate = KewApiServiceLocator.getRuleService().getRuleTemplateByName(ruleTemplateNameParam);
123 
124             for (RuleTemplateAttribute ruleTemplateAttribute : ruleTemplate.getActiveRuleTemplateAttributes()) {
125                 List<RemotableAttributeField> attributeFields = null;
126                 WorkflowRuleAttributeHandlerService wrahs = KewFrameworkServiceLocator.getWorkflowRuleAttributeHandlerService();
127                 String attributeName = ruleTemplateAttribute.getRuleAttribute().getName();
128                 attributeFields = wrahs.getSearchRows(attributeName);
129 
130                 List<Row> searchRows = FieldUtils.convertRemotableAttributeFields(attributeFields);
131                 rows.addAll(searchRows);
132 
133             }
134 
135             return true;
136         }
137         rows.clear();
138         return false;
139     }
140 
141     @Override
142     public List<? extends BusinessObject> getSearchResults(Map<String, String> fieldValues) {
143         new ArrayList();
144 
145         String docTypeNameParam = fieldValues.get(DOC_TYP_NAME_PROPERTY_NAME);
146         String ruleTemplateIdParam = fieldValues.get(RULE_TEMPLATE_ID_PROPERTY_NAME);
147         String ruleTemplateNameParam = fieldValues.get(RULE_TEMPLATE_PROPERTY_NAME);
148         String groupIdParam = fieldValues.get(GROUP_REVIEWER_PROPERTY_NAME);
149         String groupNameParam = fieldValues.get(GROUP_REVIEWER_NAME_PROPERTY_NAME);
150         String groupNamespaceParam = fieldValues.get(GROUP_REVIEWER_NAMESPACE_PROPERTY_NAME);
151         String networkIdParam = fieldValues.get(PERSON_REVIEWER_PROPERTY_NAME);
152         String userDirectiveParam = fieldValues.get(PERSON_REVIEWER_TYPE_PROPERTY_NAME);
153         String activeParam = fieldValues.get(ACTIVE_IND_PROPERTY_NAME);
154         String ruleIdParam = fieldValues.get(RULE_ID_PROPERTY_NAME);
155         String ruleDescription = fieldValues.get(RULE_DESC_PROPERTY_NAME);
156         String deleteSelection = fieldValues.get(DELEGATE_RULE_PROPERTY_NAME);
157 
158         String docTypeSearchName = null;
159         String workflowId = null;
160         String workgroupId = null;
161         String ruleTemplateId = null;
162         Boolean isDelegateRule = null;
163         Boolean isActive = null;
164         String ruleId = null;
165       
166         
167         //for KULRICE-3678
168         if(deleteSelection != null && !"".equals(deleteSelection.trim()))
169         {
170         	if(deleteSelection.equalsIgnoreCase("Y")) {
171 				isDelegateRule = Boolean.TRUE;
172 			} else {
173 				isDelegateRule = Boolean.FALSE;
174 			}
175         }
176         
177         if (ruleIdParam != null && !"".equals(ruleIdParam.trim())) {
178             try {
179                 ruleId = ruleIdParam.trim();
180             } catch (NumberFormatException e) {
181                 // FIXME: KULRICE-5201 / KULRICE-5329 : Need to check to see if this makes sense now that ruleId is not a Long anymore
182             	ruleId = "-1";
183             }
184         }
185 
186         if (!activeParam.equals("")) {
187             if (activeParam.equals("Y")) {
188                 isActive = Boolean.TRUE;
189             } else {
190                 isActive = Boolean.FALSE;
191             }
192         }
193 
194         if (docTypeNameParam != null && !"".equals(docTypeNameParam.trim())) {
195             docTypeSearchName = docTypeNameParam.replace('*', '%');
196             docTypeSearchName = "%" + docTypeSearchName.trim() + "%";
197         }
198 
199         if (!StringUtils.isEmpty(networkIdParam)) {
200         	Person person = KimApiServiceLocator.getPersonService().getPersonByPrincipalName(networkIdParam);
201         	if (person != null) {
202         		workflowId = person.getPrincipalId();
203         	}
204         }
205         
206         if (!StringUtils.isEmpty(groupIdParam) || !StringUtils.isEmpty(groupNameParam)) {
207             Group group = null;
208             if (groupIdParam != null && !"".equals(groupIdParam)) {
209                 group = getGroupService().getGroup(groupIdParam.trim());
210             } else {
211                 if (groupNamespaceParam == null) {
212                     groupNamespaceParam = KimConstants.KIM_GROUP_DEFAULT_NAMESPACE_CODE;
213                 }
214                 group = getGroupService().getGroupByNamespaceCodeAndName(groupNamespaceParam, groupNameParam.trim());
215                 if (group == null) {
216                     GlobalVariables.getMessageMap().putError(GROUP_REVIEWER_NAMESPACE_PROPERTY_NAME, RiceKeyConstants.ERROR_CUSTOM, INVALID_WORKGROUP_ERROR);
217                 } else {
218                     workgroupId = group.getId();
219                 }
220             }
221         }
222 
223         Map<String, String> attributes = null;
224         MyColumns myColumns = new MyColumns();
225         if (StringUtils.isNotBlank(ruleTemplateNameParam) || StringUtils.isNotBlank(ruleTemplateIdParam) && !"null".equals(ruleTemplateIdParam)) {
226             RuleTemplate ruleTemplate = null;
227             if (StringUtils.isNotBlank(ruleTemplateIdParam)) {
228                 ruleTemplate = KewApiServiceLocator.getRuleService().getRuleTemplate(ruleTemplateIdParam);
229             } else {
230                 ruleTemplate = KewApiServiceLocator.getRuleService().getRuleTemplateByName(ruleTemplateNameParam.trim());
231             }
232 
233             attributes = new HashMap<String, String>();
234             for (RuleTemplateAttribute ruleTemplateAttribute : ruleTemplate.getActiveRuleTemplateAttributes()) {
235                 /*WorkflowRuleAttribute attribute = (WorkflowRuleAttribute)GlobalResourceLoader.getObject(new ObjectDefinition(ruleTemplateAttribute.getRuleAttribute().getResourceDescriptor(), ruleTemplateAttribute.getRuleAttribute().getApplicationId()));//SpringServiceLocator.getExtensionService().getWorkflowAttribute(ruleTemplateAttribute.getRuleAttribute().getClassName());
236                 RuleAttribute ruleAttribute = ruleTemplateAttribute.getRuleAttribute();
237                 ExtensionDefinition extensionDefinition = RuleAttribute.to(ruleAttribute);
238                 if (ruleAttribute.getType().equals(KewApiConstants.RULE_XML_ATTRIBUTE_TYPE)) {
239                     ((GenericXMLRuleAttribute) attribute).setExtensionDefinition(extensionDefinition);
240                 }
241                 attribute.setRequired(false);*/
242                 List<RemotableAttributeField> searchRows = null;
243                 String curExtId = "0";//debugging for EN-1682
244 
245                 String attributeName = ruleTemplateAttribute.getRuleAttribute().getName();
246                 WorkflowRuleAttributeHandlerService wrahs = KewFrameworkServiceLocator.getWorkflowRuleAttributeHandlerService();
247                 ValidationResults validationResults = wrahs.validateRuleData(attributeName, fieldValues);
248                 for (Map.Entry<String, String> entry : validationResults.getErrors().entrySet()) {
249                     GlobalVariables.getMessageMap().putError(entry.getValue(), RiceKeyConstants.ERROR_CUSTOM, entry.getKey());
250                 }
251 
252                 //Validate extension data
253                 Map<String, String> curExts = ruleTemplateAttribute.getRuleExtensionMap();
254                 ValidationResults extensionValidationResults = wrahs.validateRuleData(attributeName, curExts);
255                 if (!extensionValidationResults.getErrors().isEmpty()) {
256                     for (Map.Entry<String, String> entry : extensionValidationResults.getErrors().entrySet()) {
257                         LOG.warn("Exception caught attempting to validate attribute data for extension id:" + entry.getKey() + ". Reason: " + entry.getValue());
258                     }
259                 }
260              
261                 searchRows = wrahs.getSearchRows(attributeName);
262                 
263                 for (RemotableAttributeField field : searchRows) {
264                     if (fieldValues.get(field.getName()) != null) {
265                         String attributeParam = fieldValues.get(field.getName());
266                         if (StringUtils.isNotBlank(attributeParam)) {
267                             attributes.put(field.getName(), attributeParam.trim());
268                         }
269                     }
270                     if (field.getControl() instanceof RemotableTextInput || field.getControl() instanceof RemotableSelect
271                             || field.getControl() instanceof RemotableCheckboxGroup
272                             || field.getControl() instanceof RemotableCheckbox
273                             || field.getControl() instanceof RemotableRadioButtonGroup) {
274                         myColumns.getColumns().add(new ConcreteKeyValue(field.getName(), ruleTemplateAttribute.getId()));
275                     }
276                 }
277             }
278         }
279 
280         if (!StringUtils.isEmpty(ruleDescription)) {
281             ruleDescription = ruleDescription.replace('*', '%');
282             ruleDescription = "%" + ruleDescription.trim() + "%";
283         }
284        
285         if (!GlobalVariables.getMessageMap().hasNoErrors()) {
286             throw new ValidationException("errors in search criteria");
287         }
288        
289 
290         // TODO: replace this with new API find method ??
291         List<RuleBaseValues> rules = getRuleService().search(docTypeSearchName, ruleId, ruleTemplateId, ruleDescription, workgroupId, workflowId, isDelegateRule, isActive, attributes, userDirectiveParam);
292         List<RuleBaseValues> displayList = new ArrayList<RuleBaseValues>();
293 
294         for (RuleBaseValues record : rules) {
295             if (StringUtils.isEmpty(record.getDescription())) {
296                 record.setDescription("");
297             }
298 
299             if (ruleTemplateNameParam != null && !ruleTemplateNameParam.trim().equals("") || ruleTemplateIdParam != null && !"".equals(ruleTemplateIdParam) && !"null".equals(ruleTemplateIdParam)) {
300                 MyColumns myNewColumns = new MyColumns();
301                 for (KeyValue pair : myColumns.getColumns()) {
302                     final KeyValue newPair;
303                     if (record.getRuleExtensionValue(pair.getValue(), pair.getKey().toString()) != null) {
304                     	newPair = new ConcreteKeyValue(pair.getKey(), record.getRuleExtensionValue(pair.getValue(), pair.getKey().toString()).getValue());
305                     } else {
306                     	newPair = new ConcreteKeyValue(pair.getKey(), "");
307                     }
308                     myNewColumns.getColumns().add(newPair);
309                     record.getFieldValues().put(newPair.getKey(), newPair.getValue());
310                 }
311                 record.setMyColumns(myNewColumns);
312             }
313 
314             StringBuffer returnUrl = new StringBuffer("<a href=\"");
315             returnUrl.append(fieldValues.get(BACK_LOCATION)).append("?methodToCall=refresh&docFormKey=").append(fieldValues.get(DOC_FORM_KEY)).append("&");
316 
317             returnUrl.append(RULE_ID_PROPERTY_NAME);
318             returnUrl.append("=").append(record.getId()).append("\">return value</a>");
319             record.setReturnUrl(returnUrl.toString());
320 
321             String destinationUrl = "<a href=\"Rule.do?methodToCall=report&currentRuleId=" + record.getId() + "\">report</a>";
322 
323             record.setDestinationUrl(destinationUrl);
324 
325             displayList.add(record);
326         }
327         return displayList;
328 
329     }
330 
331 
332 
333     private GroupService getGroupService() {
334        return KimApiServiceLocator.getGroupService();
335     }
336 
337     private RuleTemplateService getRuleTemplateService() {
338         return (RuleTemplateService) KEWServiceLocator.getService(KEWServiceLocator.RULE_TEMPLATE_SERVICE);
339     }
340     private RuleServiceInternal getRuleService() {
341         return (RuleServiceInternal) KEWServiceLocator.getService(KEWServiceLocator.RULE_SERVICE);
342     }
343 
344     @Override
345     public void validateSearchParameters(Map<String, String> fieldValues) {
346         super.validateSearchParameters(fieldValues);
347 
348         // make sure that if we have either groupName or Namespace, that both are filled in
349         String groupName = (String)fieldValues.get(GROUP_REVIEWER_NAME_PROPERTY_NAME);
350         String groupNamespace = (String)fieldValues.get(GROUP_REVIEWER_NAMESPACE_PROPERTY_NAME);
351         String principalName = (String)fieldValues.get(PERSON_REVIEWER_PROPERTY_NAME);
352 
353         if (StringUtils.isEmpty(groupName) && !StringUtils.isEmpty(groupNamespace)) {
354             String attributeLabel = getDataDictionaryService().getAttributeLabel(getBusinessObjectClass(), GROUP_REVIEWER_NAME_PROPERTY_NAME);
355             GlobalVariables.getMessageMap().putError(GROUP_REVIEWER_NAME_PROPERTY_NAME, RiceKeyConstants.ERROR_REQUIRED, attributeLabel);
356         }
357 
358         if  (!StringUtils.isEmpty(groupName) && StringUtils.isEmpty(groupNamespace)) {
359             String attributeLabel = getDataDictionaryService().getAttributeLabel(getBusinessObjectClass(), GROUP_REVIEWER_NAMESPACE_PROPERTY_NAME);
360             GlobalVariables.getMessageMap().putError(GROUP_REVIEWER_NAMESPACE_PROPERTY_NAME, RiceKeyConstants.ERROR_REQUIRED, attributeLabel);
361         }
362 
363         if  (!StringUtils.isEmpty(groupName) && !StringUtils.isEmpty(groupNamespace)) {
364             Group group = KimApiServiceLocator.getGroupService().getGroupByNamespaceCodeAndName(groupNamespace,
365                     groupName);
366             if (group == null) {
367                 GlobalVariables.getMessageMap().putError(GROUP_REVIEWER_NAME_PROPERTY_NAME, RiceKeyConstants.ERROR_CUSTOM, INVALID_WORKGROUP_ERROR);
368             }
369         }
370 
371         if  (!StringUtils.isEmpty(principalName)) {
372             Person person = KimApiServiceLocator.getPersonService().getPersonByPrincipalName(principalName);
373             if (person == null) {
374                 GlobalVariables.getMessageMap().putError(PERSON_REVIEWER_PROPERTY_NAME, RiceKeyConstants.ERROR_CUSTOM, INVALID_PERSON_ERROR);
375             }
376         }
377         if (!GlobalVariables.getMessageMap().hasNoErrors()) {
378             throw new ValidationException("errors in search criteria");
379         }
380     }
381 
382     @Override
383     public Collection performLookup(LookupForm lookupForm,
384             Collection resultTable, boolean bounded) {
385         //return super.performLookup(lookupForm, resultTable, bounded);
386         setBackLocation((String) lookupForm.getFieldsForLookup().get(KRADConstants.BACK_LOCATION));
387         setDocFormKey((String) lookupForm.getFieldsForLookup().get(KRADConstants.DOC_FORM_KEY));
388         Collection displayList;
389 
390         // call search method to get results
391         if (bounded) {
392             displayList = getSearchResults(lookupForm.getFieldsForLookup());
393         }
394         else {
395             displayList = getSearchResultsUnbounded(lookupForm.getFieldsForLookup());
396         }
397 
398         HashMap<String,Class> propertyTypes = new HashMap<String, Class>();
399 
400         boolean hasReturnableRow = false;
401 
402         List returnKeys = getReturnKeys();
403         List pkNames = getBusinessObjectMetaDataService().listPrimaryKeyFieldNames(getBusinessObjectClass());
404         Person user = GlobalVariables.getUserSession().getPerson();
405         
406         // iterate through result list and wrap rows with return url and action urls
407         for (Iterator iter = displayList.iterator(); iter.hasNext();) {
408             BusinessObject element = (BusinessObject) iter.next();
409             if(element instanceof PersistableBusinessObject){
410                 lookupForm.setLookupObjectId(((PersistableBusinessObject)element).getObjectId());
411             }
412 
413             BusinessObjectRestrictions businessObjectRestrictions = getBusinessObjectAuthorizationService().getLookupResultRestrictions(element, user);
414 
415             HtmlData returnUrl = getReturnUrl(element, lookupForm, returnKeys, businessObjectRestrictions);
416 
417             String actionUrls = getActionUrls(element, pkNames, businessObjectRestrictions);
418             //Fix for JIRA - KFSMI-2417
419             if("".equals(actionUrls)){
420                 actionUrls = ACTION_URLS_EMPTY;
421             }
422 
423             // Determine whether or not this rule is a delegate rule.
424             boolean isRuleDelegation = (element instanceof RuleBaseValues && ((RuleBaseValues) element).getDelegateRule().booleanValue());
425             
426             List<Column> columns = getColumns();
427             for (Object element2 : columns) {
428 
429                 Column col = (Column) element2;
430                 Formatter formatter = col.getFormatter();
431 
432                 // pick off result column from result list, do formatting
433                 String propValue = KRADConstants.EMPTY_STRING;
434                 Object prop = null;
435                 boolean skipPropTypeCheck = false;
436                 //try to get value elsewhere
437                 if (element instanceof RuleBaseValues) {
438                     prop = ((RuleBaseValues)element).getFieldValues().get(col.getPropertyName());
439                     skipPropTypeCheck = true;
440                 }
441                 if (prop == null) {
442                     prop = ObjectUtils.getPropertyValue(element, col.getPropertyName());
443                 }
444 
445                 // set comparator and formatter based on property type
446                 Class propClass = propertyTypes.get(col.getPropertyName());
447                 if ( propClass == null && !skipPropTypeCheck) {
448                     try {
449                         propClass = ObjectUtils.getPropertyType( element, col.getPropertyName(), getPersistenceStructureService() );
450                         propertyTypes.put( col.getPropertyName(), propClass );
451                     } catch (Exception e) {
452                         throw new RuntimeException("Cannot access PropertyType for property " + "'" + col.getPropertyName() + "' " + " on an instance of '" + element.getClass().getName() + "'.", e);
453                     }
454                 }
455 
456                 // formatters
457                 if (prop != null) {
458                     // for Booleans, always use BooleanFormatter
459                     if (prop instanceof Boolean) {
460                         formatter = new BooleanFormatter();
461                     }
462 
463                     // for Dates, always use DateFormatter
464                     if (prop instanceof Date) {
465                         formatter = new DateFormatter();
466                     }
467 
468                     // for collection, use the list formatter if a formatter hasn't been defined yet
469                     if (prop instanceof Collection && formatter == null) {
470                     formatter = new CollectionFormatter();
471                     }
472 
473                     if (formatter != null) {
474                         propValue = (String) formatter.format(prop);
475                     }
476                     else {
477                         propValue = prop.toString();
478                     }
479                 }
480 
481                 // comparator
482                 col.setComparator(CellComparatorHelper.getAppropriateComparatorForPropertyClass(propClass));
483                 col.setValueComparator(CellComparatorHelper.getAppropriateValueComparatorForPropertyClass(propClass));
484 
485                 propValue = maskValueIfNecessary(element.getClass(), col.getPropertyName(), propValue, businessObjectRestrictions);
486 
487                 col.setPropertyValue(propValue);
488 
489                 if (StringUtils.isNotBlank(propValue)) {
490                 	if (RULE_ID_PROPERTY_NAME.equals(col.getPropertyName()) && isRuleDelegation) {
491                 		// If the row represents a delegate rule, make the ID column's inquiry link lead to the corresponding delegate rule instead.
492                    		List<?> delegationList = KEWServiceLocator.getRuleDelegationService().findByDelegateRuleId(
493                    				((RuleBaseValues) element).getId());
494                 		if (ObjectUtils.isNotNull(delegationList) && !delegationList.isEmpty()) {
495                 			BusinessObject ruleDelegation = (BusinessObject) delegationList.get(0);
496                 			col.setColumnAnchor(getInquiryUrl(ruleDelegation, "ruleDelegationId"));
497                 		} else {
498                 			col.setColumnAnchor(getInquiryUrl(element, col.getPropertyName()));
499                 		}
500                 	}else {
501                 		col.setColumnAnchor(getInquiryUrl(element, col.getPropertyName()));
502                 	}
503 
504                 }
505             }
506 
507             ResultRow row = new ResultRow(columns, returnUrl.constructCompleteHtmlTag(), actionUrls);
508             row.setRowId(returnUrl.getName());
509             row.setReturnUrlHtmlData(returnUrl);
510             // because of concerns of the BO being cached in session on the ResultRow,
511             // let's only attach it when needed (currently in the case of export)
512             if (getBusinessObjectDictionaryService().isExportable(getBusinessObjectClass())) {
513                 row.setBusinessObject(element);
514             }
515             if(element instanceof PersistableBusinessObject){
516                 row.setObjectId((((PersistableBusinessObject)element).getObjectId()));
517             }
518 
519 
520             boolean rowReturnable = isResultReturnable(element);
521             row.setRowReturnable(rowReturnable);
522             if (rowReturnable) {
523                 hasReturnableRow = true;
524             }
525             resultTable.add(row);
526         }
527 
528         lookupForm.setHasReturnableRow(hasReturnableRow);
529 
530         return displayList;
531     }
532 
533     @Override
534     public List<Column> getColumns() {
535         List<Column> columns = super.getColumns();
536         for (Row row : rows) {
537             for (Field field : row.getFields()) {
538                 Column newColumn = new Column();
539                 newColumn.setColumnTitle(field.getFieldLabel());
540                 newColumn.setMaxLength(field.getMaxLength());
541                 newColumn.setPropertyName(field.getPropertyName());
542                 columns.add(newColumn);
543             }
544         }
545         return columns;
546     }
547 
548     @Override
549     public List<HtmlData> getCustomActionUrls(BusinessObject businessObject,
550             List pkNames) {
551         RuleBaseValues ruleBaseValues = (RuleBaseValues)businessObject;
552         List<HtmlData> htmlDataList = new ArrayList<HtmlData>();
553         if (StringUtils.isNotBlank(ruleBaseValues.getRuleTemplateName()) && StringUtils.isNotBlank(getMaintenanceDocumentTypeName())) {
554         	if (ruleBaseValues.getDelegateRule().booleanValue()) {
555         		// If the rule is a delegate rule, have the edit/copy links open the rule delegation maintenance document screen instead.
556         		List<?> delegationList = KEWServiceLocator.getRuleDelegationService().findByDelegateRuleId(ruleBaseValues.getId());
557         		if (ObjectUtils.isNotNull(delegationList) && !delegationList.isEmpty()) {
558         			BusinessObject ruleDelegation = (BusinessObject) delegationList.get(0);
559     				// Retrieve the rule delegation lookupable helper service and the primary key names, if they have not been obtained yet.
560         	        if (ruleDelegationLookupableHelperService == null) {
561         				ruleDelegationLookupableHelperService = KNSServiceLocator.getLookupable(
562                                 KNSServiceLocator.getBusinessObjectDictionaryService()
563                                         .getLookupableID(ruleDelegation.getClass())).getLookupableHelperService();
564         				if (ruleDelegationLookupableHelperService.getBusinessObjectClass() == null) {
565         					ruleDelegationLookupableHelperService.setBusinessObjectClass(ruleDelegation.getClass());
566         				}
567         				delegationPkNames = getBusinessObjectMetaDataService().listPrimaryKeyFieldNames(ruleDelegation.getClass());
568         			}
569         	        // Allow the rule delegation's lookupable helper service to handle the custom action URL generation instead.
570         			htmlDataList = ruleDelegationLookupableHelperService.getCustomActionUrls(ruleDelegation, delegationPkNames);
571         		}
572         	} else {
573         		// Otherwise, have the links open the regular routing rule maintenance document screen.
574         		if (allowsMaintenanceEditAction(businessObject)) {
575         			htmlDataList.add(getUrlData(businessObject, KRADConstants.MAINTENANCE_EDIT_METHOD_TO_CALL, pkNames));
576         		}
577         		if (allowsMaintenanceNewOrCopyAction()) {
578                 	htmlDataList.add(getUrlData(businessObject, KRADConstants.MAINTENANCE_COPY_METHOD_TO_CALL, pkNames));
579             	}
580         	}
581         }
582         
583         return htmlDataList;
584     }
585 
586 
587 
588 }