View Javadoc

1   /**
2    * Copyright 2005-2014 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.RemotableAttributeError;
20  import org.kuali.rice.core.api.util.ConcreteKeyValue;
21  import org.kuali.rice.core.api.util.KeyValue;
22  import org.kuali.rice.core.api.util.RiceKeyConstants;
23  import org.kuali.rice.core.web.format.BooleanFormatter;
24  import org.kuali.rice.core.web.format.CollectionFormatter;
25  import org.kuali.rice.core.web.format.DateFormatter;
26  import org.kuali.rice.core.web.format.Formatter;
27  import org.kuali.rice.kew.api.KewApiConstants;
28  import org.kuali.rice.kew.api.KewApiServiceLocator;
29  import org.kuali.rice.kew.api.rule.RuleTemplate;
30  import org.kuali.rice.kew.api.rule.RuleTemplateAttribute;
31  import org.kuali.rice.kew.lookupable.MyColumns;
32  import org.kuali.rice.kew.rule.RuleBaseValues;
33  import org.kuali.rice.kew.rule.RuleDelegationBo;
34  import org.kuali.rice.kew.rule.RuleExtensionBo;
35  import org.kuali.rice.kew.rule.WorkflowRuleAttributeRows;
36  import org.kuali.rice.kew.rule.service.RuleDelegationService;
37  import org.kuali.rice.kew.rule.service.RuleTemplateService;
38  import org.kuali.rice.kew.service.KEWServiceLocator;
39  import org.kuali.rice.kim.api.KimConstants;
40  import org.kuali.rice.kim.api.group.Group;
41  import org.kuali.rice.kim.api.group.GroupService;
42  import org.kuali.rice.kim.api.identity.Person;
43  import org.kuali.rice.kim.api.identity.principal.Principal;
44  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
45  import org.kuali.rice.kns.document.authorization.BusinessObjectRestrictions;
46  import org.kuali.rice.kns.lookup.HtmlData;
47  import org.kuali.rice.kns.lookup.KualiLookupableHelperServiceImpl;
48  import org.kuali.rice.kns.web.comparator.CellComparatorHelper;
49  import org.kuali.rice.kns.web.struts.form.LookupForm;
50  import org.kuali.rice.kns.web.ui.Column;
51  import org.kuali.rice.kns.web.ui.Field;
52  import org.kuali.rice.kns.web.ui.ResultRow;
53  import org.kuali.rice.kns.web.ui.Row;
54  import org.kuali.rice.krad.bo.BusinessObject;
55  import org.kuali.rice.krad.bo.PersistableBusinessObject;
56  import org.kuali.rice.krad.exception.ValidationException;
57  import org.kuali.rice.krad.util.GlobalVariables;
58  import org.kuali.rice.krad.util.KRADConstants;
59  import org.kuali.rice.krad.util.ObjectUtils;
60  
61  import java.sql.Date;
62  import java.util.ArrayList;
63  import java.util.Collection;
64  import java.util.HashMap;
65  import java.util.Iterator;
66  import java.util.List;
67  import java.util.Map;
68  
69  /**
70   * This is a description of what this class does - jjhanso don't forget to fill this in.
71   *
72   * @author Kuali Rice Team (rice.collab@kuali.org)
73   *
74   */
75  public class RuleDelegationLookupableHelperServiceImpl extends KualiLookupableHelperServiceImpl {
76      private List<Row> rows = new ArrayList<Row>();
77      //private List<Column> columns = establishColumns();
78      //private Long previousRuleTemplateId;
79      private static final String PARENT_RESPONSIBILITY_ID_PROPERTY_NAME = "responsibilityId";
80      private static final String PARENT_RULE_ID_PROPERTY_NAME = "ruleResponsibility.ruleBaseValuesId";
81      private static final String RULE_TEMPLATE_PROPERTY_NAME = "delegationRule.ruleTemplate.name";
82      private static final String RULE_ID_PROPERTY_NAME = "delegationRule.id";
83      private static final String ACTIVE_IND_PROPERTY_NAME = "delegationRule.active";
84      private static final String DELEGATION_PROPERTY_NAME = "delegationType";
85      private static final String GROUP_REVIEWER_PROPERTY_NAME = "groupReviewer";
86      private static final String GROUP_REVIEWER_NAME_PROPERTY_NAME = "groupReviewerName";
87      private static final String GROUP_REVIEWER_NAMESPACE_PROPERTY_NAME = "groupReviewerNamespace";
88      private static final String PERSON_REVIEWER_PROPERTY_NAME = "personReviewer";
89      private static final String PERSON_REVIEWER_TYPE_PROPERTY_NAME = "personReviewerType";
90      private static final String DOC_TYP_NAME_PROPERTY_NAME = "delegationRule.documentType.name";
91      private static final String RULE_DESC_PROPERTY_NAME = "delegationRule.description";
92  
93      private static final String BACK_LOCATION = "backLocation";
94      private static final String DOC_FORM_KEY = "docFormKey";
95      private static final String INVALID_WORKGROUP_ERROR = "The Group Reviewer Namespace and Name combination is not valid";
96      private static final String INVALID_PERSON_ERROR = "The Person Reviewer is not valid";
97  
98      @Override
99  	public List<Row> getRows() {
100         List<Row> superRows = super.getRows();
101         List<Row> returnRows = new ArrayList<Row>();
102         returnRows.addAll(superRows);
103         returnRows.addAll(rows);
104         return returnRows;
105     }
106 
107     @Override
108     public boolean checkForAdditionalFields(Map<String, String> fieldValues) {
109         String ruleTemplateNameParam = fieldValues.get(RULE_TEMPLATE_PROPERTY_NAME);
110 
111         if (StringUtils.isNotBlank(ruleTemplateNameParam)) {
112             rows = new ArrayList<Row>();
113             RuleTemplate ruleTemplate = KewApiServiceLocator.getRuleService().getRuleTemplateByName(ruleTemplateNameParam);
114 
115             for (RuleTemplateAttribute ruleTemplateAttribute : ruleTemplate.getActiveRuleTemplateAttributes()) {
116                 if (!RuleAttribute.isWorkflowAttribute(ruleTemplateAttribute.getRuleAttribute().getType())) {
117                     continue;
118                 }
119                 // run through the attributes fields once to populate field values we have to do this
120                 // to allow rows dependent on another row value to populate correctly in the loop below
121                 WorkflowRuleAttributeRows workflowRuleAttributeRows =
122                         KEWServiceLocator.getWorkflowRuleAttributeMediator().getSearchRows(fieldValues, ruleTemplateAttribute);
123                 for (Row row : workflowRuleAttributeRows.getRows()) {
124                     List<Field> fields = new ArrayList<Field>();
125                     for (Iterator<Field> iterator2 = row.getFields().iterator(); iterator2.hasNext(); ) {
126                         Field field = (Field) iterator2.next();
127                         if (fieldValues.get(field.getPropertyName()) != null) {
128                             field.setPropertyValue(fieldValues.get(field.getPropertyName()));
129                         }
130                         fields.add(field);
131                         fieldValues.put(field.getPropertyName(), field.getPropertyValue());
132                     }
133                 }
134                 // now run through a second time with our shiny new field values
135                 // ...by the way, just trying to preserve behavior from Rice 1.0.x here...generally speaking, this stuff is crazy!!!
136                 workflowRuleAttributeRows =
137                         KEWServiceLocator.getWorkflowRuleAttributeMediator().getSearchRows(fieldValues, ruleTemplateAttribute);
138                 for (Row row : workflowRuleAttributeRows.getRows()) {
139                     List<Field> fields = new ArrayList<Field>();
140                     for (Iterator<Field> iterator2 = row.getFields().iterator(); iterator2.hasNext(); ) {
141                         Field field = iterator2.next();
142                         if (fieldValues.get(field.getPropertyName()) != null) {
143                             field.setPropertyValue(fieldValues.get(field.getPropertyName()));
144                         }
145                         fields.add(field);
146                         fieldValues.put(field.getPropertyName(), field.getPropertyValue());
147                     }
148                     row.setFields(fields);
149                     rows.add(row);
150 
151                 }
152             }
153             return true;
154         }
155         rows.clear();
156         return false;
157     }
158 
159     @Override
160     public List<? extends BusinessObject> getSearchResults(Map<String, String> fieldValues) {
161         String parentRuleBaseValueId = fieldValues.get(PARENT_RULE_ID_PROPERTY_NAME);
162         String parentResponsibilityId = fieldValues.get(PARENT_RESPONSIBILITY_ID_PROPERTY_NAME);
163         String docTypeNameParam = fieldValues.get(DOC_TYP_NAME_PROPERTY_NAME);
164         String ruleTemplateIdParam = null;
165         String ruleTemplateNameParam = fieldValues.get(RULE_TEMPLATE_PROPERTY_NAME);
166         String groupIdParam = fieldValues.get(GROUP_REVIEWER_PROPERTY_NAME);
167         String groupNameParam = fieldValues.get(GROUP_REVIEWER_NAME_PROPERTY_NAME);
168         String groupNamespaceParam = fieldValues.get(GROUP_REVIEWER_NAMESPACE_PROPERTY_NAME);
169         String networkIdParam = fieldValues.get(PERSON_REVIEWER_PROPERTY_NAME);
170         String userDirectiveParam = fieldValues.get(PERSON_REVIEWER_TYPE_PROPERTY_NAME);
171         String activeParam = fieldValues.get(ACTIVE_IND_PROPERTY_NAME);
172         String delegationParam = fieldValues.get(DELEGATION_PROPERTY_NAME);
173         String ruleIdParam = fieldValues.get(RULE_ID_PROPERTY_NAME);
174         String ruleDescription = fieldValues.get(RULE_DESC_PROPERTY_NAME);
175 
176         String docTypeSearchName = null;
177         String workflowId = null;
178         String workgroupId = null;
179         String ruleTemplateId = null;
180         Boolean isActive = null;
181         String ruleId = null;
182 
183         if (ruleIdParam != null && !"".equals(ruleIdParam.trim())) {
184             ruleId = ruleIdParam.trim();
185         }
186 
187         if (!activeParam.equals("")) {
188             if (activeParam.equals("Y")) {
189                 isActive = Boolean.TRUE;
190             } else {
191                 isActive = Boolean.FALSE;
192             }
193         }
194 
195         if (docTypeNameParam != null && !"".equals(docTypeNameParam.trim())) {
196             docTypeSearchName = docTypeNameParam.replace('*', '%');
197             docTypeSearchName = "%" + docTypeSearchName.trim() + "%";
198         }
199 
200         if (StringUtils.isNotBlank(networkIdParam)) {
201             Principal principal = KimApiServiceLocator.getIdentityService().getPrincipalByPrincipalName(networkIdParam);
202         	if (principal != null) {
203         		workflowId = principal.getPrincipalId();
204         	}
205         }
206 
207         if (StringUtils.isNotBlank(groupIdParam) || StringUtils.isNotBlank(groupNameParam)) {
208             Group group = null;
209             if (groupIdParam != null && !"".equals(groupIdParam)) {
210                 group = getGroupService().getGroup(groupIdParam.trim());
211             } else {
212                 if (groupNamespaceParam == null) {
213                     groupNamespaceParam = KimConstants.KIM_GROUP_DEFAULT_NAMESPACE_CODE;
214                 }
215                 group = getGroupService().getGroupByNamespaceCodeAndName(groupNamespaceParam, groupNameParam.trim());
216                 if (group == null) {
217                     GlobalVariables.getMessageMap().putError(GROUP_REVIEWER_NAMESPACE_PROPERTY_NAME, RiceKeyConstants.ERROR_CUSTOM, INVALID_WORKGROUP_ERROR);
218                 } else {
219                     workgroupId = group.getId();
220                 }
221             }
222         }
223 
224         Map<String, String> attributes = null;
225         MyColumns myColumns = new MyColumns();
226         if (StringUtils.isNotBlank(ruleTemplateNameParam) || StringUtils.isNotBlank(ruleTemplateIdParam) && !"null".equals(ruleTemplateIdParam)) {
227             RuleTemplate ruleTemplate = null;
228             if (StringUtils.isNotBlank(ruleTemplateIdParam)) {
229                 ruleTemplate = KewApiServiceLocator.getRuleService().getRuleTemplate(ruleTemplateIdParam);
230             } else {
231                 ruleTemplate = KewApiServiceLocator.getRuleService().getRuleTemplateByName(ruleTemplateNameParam.trim());
232                 ruleTemplateId = ruleTemplate.getId();
233             }
234 
235             if (ruleTemplate == null) {
236                 rows.clear();
237                 LOG.info("Returning empty result set for Delegation Rule Lookup because a RuleTemplate Name or ID was provided, but no valid RuleTemplates were retrieved by the service.");
238                 return new ArrayList<RuleDelegationBo>();
239             }
240 
241             attributes = new HashMap<String, String>();
242 
243             for (RuleTemplateAttribute ruleTemplateAttribute : ruleTemplate.getActiveRuleTemplateAttributes()) {
244                 if (!RuleAttribute.isWorkflowAttribute(ruleTemplateAttribute.getRuleAttribute().getType())) {
245                     continue;
246                 }
247                 WorkflowRuleAttributeRows workflowRuleAttributeRows =
248                         KEWServiceLocator.getWorkflowRuleAttributeMediator().getSearchRows(fieldValues, ruleTemplateAttribute, false);
249 
250                 for (RemotableAttributeError error : workflowRuleAttributeRows.getValidationErrors()) {
251                     GlobalVariables.getMessageMap().putError(error.getAttributeName(), RiceKeyConstants.ERROR_CUSTOM, error.getMessage());
252                 }
253 
254                 for (Row row : workflowRuleAttributeRows.getRows()) {
255                     for (Field field : row.getFields()) {
256                         if (fieldValues.get(field.getPropertyName()) != null) {
257                             String attributeParam = fieldValues.get(field.getPropertyName());
258                             if (!attributeParam.equals("")) {
259                                 attributes.put(field.getPropertyName(), attributeParam.trim());
260                             }
261                         }
262                         if (field.getFieldType().equals(Field.TEXT) || field.getFieldType().equals(Field.DROPDOWN) || field.getFieldType().equals(Field.DROPDOWN_REFRESH) || field.getFieldType().equals(Field.RADIO)) {
263                             myColumns.getColumns().add(new ConcreteKeyValue(field.getPropertyName(), ruleTemplateAttribute.getId()));
264                         }
265                     }
266                 }
267             }
268         }
269 
270         if (StringUtils.isNotBlank(ruleDescription)) {
271             ruleDescription = ruleDescription.replace('*', '%');
272             ruleDescription = "%" + ruleDescription.trim() + "%";
273         }
274 
275         if (GlobalVariables.getMessageMap().hasErrors()) {
276             throw new ValidationException("errors in search criteria");
277         }
278 
279         // TODO: replace this with new API find method ??
280         Iterator<RuleDelegationBo> rules = getRuleDelegationService().search(parentRuleBaseValueId, parentResponsibilityId, docTypeSearchName, ruleId, ruleTemplateId, ruleDescription, workgroupId, workflowId, delegationParam, isActive, attributes, userDirectiveParam).iterator();
281         List<RuleDelegationBo> displayList = new ArrayList<RuleDelegationBo>();
282 
283         while (rules.hasNext()) {
284             RuleDelegationBo ruleDelegation = rules.next();
285             RuleBaseValues record = ruleDelegation.getDelegationRule();
286 
287             if (StringUtils.isBlank(record.getDescription())) {
288                 record.setDescription(KewApiConstants.HTML_NON_BREAKING_SPACE);
289             }
290 
291             if (ruleTemplateNameParam != null && !ruleTemplateNameParam.trim().equals("") || ruleTemplateIdParam != null && !"".equals(ruleTemplateIdParam) && !"null".equals(ruleTemplateIdParam)) {
292                 MyColumns myNewColumns = new MyColumns();
293                 for (Object element : myColumns.getColumns()) {
294                     KeyValue pair = (KeyValue) element;
295                     final KeyValue newPair;
296                     List<RuleExtensionBo> ruleExts = record.getRuleExtensions();
297                     for (RuleExtensionBo ruleExt : ruleExts) {
298                         if(ruleExt.getExtensionValuesMap().containsKey(pair.getKey().toString())){
299                             ruleTemplateIdParam = ruleExt.getRuleTemplateAttributeId();
300                         }
301                     }
302                     if (record.getRuleExtensionValue(ruleTemplateIdParam, pair.getKey().toString()) != null) {
303                         newPair = new ConcreteKeyValue(pair.getKey(), record.getRuleExtensionValue(ruleTemplateIdParam, pair.getKey().toString()).getValue());
304                     } else {
305                         newPair = new ConcreteKeyValue(pair.getKey(), KewApiConstants.HTML_NON_BREAKING_SPACE);
306                     }
307                     myNewColumns.getColumns().add(newPair);
308                     record.getFieldValues().put(newPair.getKey(), newPair.getValue());
309                 }
310                 record.setMyColumns(myNewColumns);
311             }
312 
313             StringBuilder returnUrl = new StringBuilder("<a href=\"");
314             returnUrl.append(fieldValues.get(BACK_LOCATION)).append("?methodToCall=refresh&docFormKey=").append(fieldValues.get(DOC_FORM_KEY)).append("&");
315 
316             returnUrl.append(RULE_ID_PROPERTY_NAME);
317             returnUrl.append("=").append(record.getId()).append("\">return value</a>");
318             record.setReturnUrl(returnUrl.toString());
319 
320             String destinationUrl = "<a href=\"Rule.do?methodToCall=report&currentRuleId=" + record.getId() + "\">report</a>";
321 
322             record.setDestinationUrl(destinationUrl);
323 
324             displayList.add(ruleDelegation);
325         }
326         return displayList;
327 
328     }
329 
330 
331 
332     private GroupService getGroupService() {
333        return KimApiServiceLocator.getGroupService();
334     }
335 
336     private RuleTemplateService getRuleTemplateService() {
337         return (RuleTemplateService) KEWServiceLocator.getService(KEWServiceLocator.RULE_TEMPLATE_SERVICE);
338     }
339     private RuleDelegationService getRuleDelegationService() {
340         return (RuleDelegationService) KEWServiceLocator.getService(KEWServiceLocator.RULE_DELEGATION_SERVICE);
341     }
342 
343     @Override
344     public void validateSearchParameters(Map<String, String> fieldValues) {
345         super.validateSearchParameters(fieldValues);
346 
347         // make sure that if we have either groupName or Namespace, that both are filled in
348         String groupName = (String)fieldValues.get(GROUP_REVIEWER_NAME_PROPERTY_NAME);
349         String groupNamespace = (String)fieldValues.get(GROUP_REVIEWER_NAMESPACE_PROPERTY_NAME);
350         String principalName = (String)fieldValues.get(PERSON_REVIEWER_PROPERTY_NAME);
351 
352         if (StringUtils.isEmpty(groupName) && !StringUtils.isEmpty(groupNamespace)) {
353             String attributeLabel = getDataDictionaryService().getAttributeLabel(getBusinessObjectClass(), GROUP_REVIEWER_NAME_PROPERTY_NAME);
354             GlobalVariables.getMessageMap().putError(GROUP_REVIEWER_NAME_PROPERTY_NAME, RiceKeyConstants.ERROR_REQUIRED, attributeLabel);
355         }
356 
357         if  (!StringUtils.isEmpty(groupName) && StringUtils.isEmpty(groupNamespace)) {
358             String attributeLabel = getDataDictionaryService().getAttributeLabel(getBusinessObjectClass(), GROUP_REVIEWER_NAMESPACE_PROPERTY_NAME);
359             GlobalVariables.getMessageMap().putError(GROUP_REVIEWER_NAMESPACE_PROPERTY_NAME, RiceKeyConstants.ERROR_REQUIRED, attributeLabel);
360         }
361 
362         if  (!StringUtils.isEmpty(groupName) && !StringUtils.isEmpty(groupNamespace)) {
363             Group group = KimApiServiceLocator.getGroupService().getGroupByNamespaceCodeAndName(groupNamespace,
364                     groupName);
365             if (group == null) {
366                 GlobalVariables.getMessageMap().putError(GROUP_REVIEWER_NAME_PROPERTY_NAME, RiceKeyConstants.ERROR_CUSTOM, INVALID_WORKGROUP_ERROR);
367             }
368         }
369 
370         if  (!StringUtils.isEmpty(principalName)) {
371             Person person = KimApiServiceLocator.getPersonService().getPersonByPrincipalName(principalName);
372             if (person == null) {
373                 GlobalVariables.getMessageMap().putError(PERSON_REVIEWER_PROPERTY_NAME, RiceKeyConstants.ERROR_CUSTOM, INVALID_PERSON_ERROR);
374             }
375         }
376         if (!GlobalVariables.getMessageMap().hasNoErrors()) {
377             throw new ValidationException("errors in search criteria");
378         }
379     }
380 
381     @Override
382     public Collection performLookup(LookupForm lookupForm,
383             Collection resultTable, boolean bounded) {
384         // TODO jjhanso - THIS METHOD NEEDS JAVADOCS
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             List<Column> columns = getColumns();
424             for (Object element2 : columns) {
425 
426                 Column col = (Column) element2;
427                 String curPropName = col.getPropertyName();
428                 Formatter formatter = col.getFormatter();
429 
430                 // pick off result column from result list, do formatting
431                 String propValue = KRADConstants.EMPTY_STRING;
432                 Object prop = null;
433                 boolean skipPropTypeCheck = false;
434                 //try to get value elsewhere
435                 if (element instanceof RuleDelegationBo) {
436                     prop = ((RuleDelegationBo)element).getDelegationRule().getFieldValues().get(curPropName);
437                     skipPropTypeCheck = true;
438                 }
439                 if (prop == null) {
440                     prop = ObjectUtils.getPropertyValue(element, curPropName);
441                 }
442 
443                 // set comparator and formatter based on property type
444                 Class propClass = propertyTypes.get(curPropName);
445                 if ( propClass == null && !skipPropTypeCheck) {
446                     try {
447                         propClass = ObjectUtils.getPropertyType( element, curPropName, getPersistenceStructureService() );
448                         propertyTypes.put( curPropName, propClass );
449                     } catch (Exception e) {
450                         throw new RuntimeException("Cannot access PropertyType for property " + "'" + curPropName + "' " + " on an instance of '" + element.getClass().getName() + "'.", e);
451                     }
452                 }
453 
454                 // formatters
455                 if (prop != null) {
456                     // for Booleans, always use BooleanFormatter
457                     if (prop instanceof Boolean) {
458                         formatter = new BooleanFormatter();
459                     }
460 
461                     // for Dates, always use DateFormatter
462                     if (prop instanceof Date) {
463                         formatter = new DateFormatter();
464                     }
465 
466                     // for collection, use the list formatter if a formatter hasn't been defined yet
467                     if (prop instanceof Collection && formatter == null) {
468                     formatter = new CollectionFormatter();
469                     }
470 
471                     if (formatter != null) {
472                         propValue = (String) formatter.format(prop);
473                     }
474                     else {
475                         propValue = prop.toString();
476                     }
477                 }
478 
479                 // comparator
480                 col.setComparator(CellComparatorHelper.getAppropriateComparatorForPropertyClass(propClass));
481                 col.setValueComparator(CellComparatorHelper.getAppropriateValueComparatorForPropertyClass(propClass));
482 
483                 propValue = maskValueIfNecessary(element.getClass(), curPropName, propValue, businessObjectRestrictions);
484 
485                 col.setPropertyValue(propValue);
486 
487                 if (StringUtils.isNotBlank(propValue)) {
488                     col.setColumnAnchor(getInquiryUrl(element, curPropName));
489 
490                 }
491             }
492 
493             ResultRow row = new ResultRow(columns, returnUrl.constructCompleteHtmlTag(), actionUrls);
494             row.setRowId(returnUrl.getName());
495             row.setReturnUrlHtmlData(returnUrl);
496             // because of concerns of the BO being cached in session on the ResultRow,
497             // let's only attach it when needed (currently in the case of export)
498             if (getBusinessObjectDictionaryService().isExportable(getBusinessObjectClass())) {
499                 row.setBusinessObject(element);
500             }
501             if(element instanceof PersistableBusinessObject){
502                 row.setObjectId((((PersistableBusinessObject)element).getObjectId()));
503             }
504 
505 
506             boolean rowReturnable = isResultReturnable(element);
507             row.setRowReturnable(rowReturnable);
508             if (rowReturnable) {
509                 hasReturnableRow = true;
510             }
511             resultTable.add(row);
512         }
513 
514         lookupForm.setHasReturnableRow(hasReturnableRow);
515 
516         return displayList;
517     }
518 
519     @Override
520     public List<Column> getColumns() {
521         List<Column> columns = super.getColumns();
522         for (Row row : rows) {
523             for (Field field : row.getFields()) {
524                 Column newColumn = new Column();
525                 newColumn.setColumnTitle(field.getFieldLabel());
526                 newColumn.setMaxLength(field.getMaxLength());
527                 newColumn.setPropertyName(field.getPropertyName());
528                 columns.add(newColumn);
529             }
530         }
531         return columns;
532     }
533 
534     @Override
535     public List<HtmlData> getCustomActionUrls(BusinessObject businessObject,
536             List pkNames) {
537         RuleDelegationBo ruleDelegation = (RuleDelegationBo)businessObject;
538         List<HtmlData> htmlDataList = new ArrayList<HtmlData>();
539         if (StringUtils.isNotBlank(ruleDelegation.getDelegationRule().getRuleTemplateName()) && StringUtils.isNotBlank(getMaintenanceDocumentTypeName())) {
540         	if (allowsMaintenanceEditAction(businessObject)) {
541         		htmlDataList.add(getUrlData(businessObject, KRADConstants.MAINTENANCE_EDIT_METHOD_TO_CALL, pkNames));
542         	}
543             if (allowsMaintenanceNewOrCopyAction()) {
544                 htmlDataList.add(getUrlData(businessObject, KRADConstants.MAINTENANCE_COPY_METHOD_TO_CALL, pkNames));
545             }
546         }
547             
548         return htmlDataList;
549     }
550 
551 
552 
553 }