View Javadoc
1   /**
2    * Copyright 2005-2016 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.kns.web.ui;
17  
18  import org.apache.commons.beanutils.PropertyUtils;
19  import org.apache.commons.lang.StringUtils;
20  import org.kuali.rice.core.api.mo.common.active.Inactivatable;
21  import org.kuali.rice.kns.datadictionary.CollectionDefinitionI;
22  import org.kuali.rice.kns.datadictionary.FieldDefinition;
23  import org.kuali.rice.kns.datadictionary.FieldDefinitionI;
24  import org.kuali.rice.kns.datadictionary.InquiryCollectionDefinition;
25  import org.kuali.rice.kns.datadictionary.InquirySectionDefinition;
26  import org.kuali.rice.kns.datadictionary.InquirySubSectionHeaderDefinition;
27  import org.kuali.rice.kns.datadictionary.MaintainableCollectionDefinition;
28  import org.kuali.rice.kns.datadictionary.MaintainableFieldDefinition;
29  import org.kuali.rice.kns.datadictionary.MaintainableItemDefinition;
30  import org.kuali.rice.kns.datadictionary.MaintainableSectionDefinition;
31  import org.kuali.rice.kns.datadictionary.MaintainableSubSectionHeaderDefinition;
32  import org.kuali.rice.kns.datadictionary.SubSectionHeaderDefinitionI;
33  import org.kuali.rice.kns.document.authorization.FieldRestriction;
34  import org.kuali.rice.kns.inquiry.Inquirable;
35  import org.kuali.rice.kns.inquiry.InquiryRestrictions;
36  import org.kuali.rice.kns.lookup.LookupUtils;
37  import org.kuali.rice.kns.maintenance.Maintainable;
38  import org.kuali.rice.kns.service.BusinessObjectAuthorizationService;
39  import org.kuali.rice.kns.service.KNSServiceLocator;
40  import org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService;
41  import org.kuali.rice.kns.util.FieldUtils;
42  import org.kuali.rice.kns.util.MaintenanceUtils;
43  import org.kuali.rice.kns.util.WebUtils;
44  import org.kuali.rice.krad.bo.BusinessObject;
45  import org.kuali.rice.krad.bo.PersistableBusinessObject;
46  import org.kuali.rice.krad.datadictionary.mask.MaskFormatter;
47  import org.kuali.rice.krad.exception.ClassNotPersistableException;
48  import org.kuali.rice.krad.service.DataDictionaryService;
49  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
50  import org.kuali.rice.krad.util.KRADConstants;
51  import org.kuali.rice.krad.util.ObjectUtils;
52  
53  import java.util.ArrayList;
54  import java.util.Collection;
55  import java.util.HashMap;
56  import java.util.HashSet;
57  import java.util.Iterator;
58  import java.util.List;
59  import java.util.Map;
60  import java.util.Set;
61  
62  /**
63   * @deprecated KNS Struts deprecated, use KRAD and the Spring MVC framework.
64   */
65  @Deprecated
66  public class SectionBridge {
67      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(SectionBridge.class);
68      private static BusinessObjectAuthorizationService businessObjectAuthorizationService;
69      private static BusinessObjectAuthorizationService getBusinessObjectAuthorizationService() {
70      	if (businessObjectAuthorizationService == null) {
71      		businessObjectAuthorizationService = KNSServiceLocator.getBusinessObjectAuthorizationService();
72      	}
73      	return businessObjectAuthorizationService;
74      }
75      private static DataDictionaryService dataDictionaryService;
76      private static DataDictionaryService getDataDictionaryService() {
77      	if (dataDictionaryService == null) {
78      		dataDictionaryService = KRADServiceLocatorWeb.getDataDictionaryService();
79      	}
80      	return dataDictionaryService;
81      }
82      private static MaintenanceDocumentDictionaryService maintenanceDocumentDictionaryService;
83  
84      /**
85       * This method creates a Section for display on an Inquiry Screen.
86       * 
87       * @param sd The DD definition from which to construct the Section.
88       * @param o The BusinessObject from which to populate the Section values.
89       * @return A populated Section.
90       */
91      public static final Section toSection(Inquirable inquirable, InquirySectionDefinition sd, BusinessObject o, InquiryRestrictions auths) {
92          Section section = new Section();
93          section.setSectionId( sd.getId() );
94          section.setSectionTitle(sd.getTitle());
95          section.setRows(new ArrayList());
96          section.setDefaultOpen(sd.isDefaultOpen());
97          
98          if (sd.getNumberOfColumns() != null) {
99              section.setNumberOfColumns(sd.getNumberOfColumns());
100         }
101         else {
102             section.setNumberOfColumns(KRADConstants.DEFAULT_NUM_OF_COLUMNS);
103         }
104 
105         List<Field> sectionFields = new ArrayList();
106         for (FieldDefinition fieldDefinition : sd.getInquiryFields()) {
107             List row = new ArrayList();
108 
109             Field f = null;
110             if (fieldDefinition instanceof InquiryCollectionDefinition) {
111                 InquiryCollectionDefinition inquiryCollectionDefinition = (InquiryCollectionDefinition) fieldDefinition;
112 
113                 List<Row> sectionRows = new ArrayList();
114                 sectionRows = getContainerRows(section, inquiryCollectionDefinition, o, null, null, new ArrayList(), new HashSet<String>(), new StringBuffer(section.getErrorKey()), inquiryCollectionDefinition.getNumberOfColumns(), inquirable);
115                 section.setRows(sectionRows);
116             }
117             else if (fieldDefinition instanceof InquirySubSectionHeaderDefinition) {
118                 f = createMaintainableSubSectionHeader((InquirySubSectionHeaderDefinition) fieldDefinition);
119             }
120             else {
121                 f = FieldBridge.toField(fieldDefinition, o, section);
122             }
123 
124             if (null != f) {
125                 sectionFields.add(f);
126             }
127 
128         }
129 
130         if (!sectionFields.isEmpty()) {
131             section.setRows(FieldUtils.wrapFields(sectionFields, section.getNumberOfColumns()));
132         }
133 
134         applyInquirySectionAuthorizations(section, auths);
135         
136         section.setRows(reArrangeRows(section.getRows(), section.getNumberOfColumns()));
137         
138         return section;
139     }
140     
141    
142     private static final void applyInquirySectionAuthorizations(Section section, InquiryRestrictions inquiryRestrictions) {
143     	applyInquiryRowsAuthorizations(section.getRows(), inquiryRestrictions);
144     }
145     
146     private static final void applyInquiryRowsAuthorizations(List<Row> rows, InquiryRestrictions inquiryRestrictions) {
147     	for (Row row : rows) {
148     		List<Field> rowFields = row.getFields();
149     		for (Field field : rowFields) {
150     			applyInquiryFieldAuthorizations(field, inquiryRestrictions);
151     		}
152     	}
153     }
154     
155     protected static final void applyInquiryFieldAuthorizations(Field field, InquiryRestrictions inquiryRestrictions) {
156     	if (Field.CONTAINER.equals(field.getFieldType())) {
157     		applyInquiryRowsAuthorizations(field.getContainerRows(), inquiryRestrictions);
158     		field.setContainerRows(reArrangeRows(field.getContainerRows(), field.getNumberOfColumnsForCollection()));
159     	}
160     	else if (!Field.IMAGE_SUBMIT.equals(field.getFieldType())) {
161     		FieldRestriction fieldRestriction = inquiryRestrictions.getFieldRestriction(field.getPropertyName());
162     		if (fieldRestriction.isHidden()) {
163     			field.setFieldType(Field.HIDDEN);
164     			field.setPropertyValue(null);
165     		}
166     		// KULRICE-8271: partially masked field can't be masked properly 
167     		else if (fieldRestriction.isMasked() || fieldRestriction.isPartiallyMasked()) {
168             	field.setSecure(true);
169             	MaskFormatter maskFormatter = fieldRestriction.getMaskFormatter();
170             	String displayMaskValue = maskFormatter.maskValue(field.getPropertyValue());
171             	field.setDisplayMaskValue(displayMaskValue);
172             	// since it's an inquiry, let's wipe out the encrypted field value since we don't need to post it back
173             	field.setEncryptedValue("");
174     		}
175        	}
176     }
177     
178     //This method is used to remove hidden fields (To fix JIRA KFSMI-2449)
179     private static final List<Row> reArrangeRows(List<Row> rows, int numberOfColumns){
180     	List<Row> rearrangedRows = new ArrayList<Row>();
181     	
182     	for (Row row : rows) {
183     		List<Field> fields = new ArrayList<Field>();
184     		List<Field> rowFields = row.getFields();
185     		for (Field field : rowFields) {
186     			if(!Field.HIDDEN.equals(field.getFieldType()) && !Field.BLANK_SPACE.equals(field.getFieldType())){
187     				fields.add(field);
188     			}
189     		}
190     		List<Row> rewrappedFieldRows = FieldUtils.wrapFields(fields, numberOfColumns);
191     		if (row.isHidden()) {
192     			for (Row rewrappedRow : rewrappedFieldRows) {
193     				rewrappedRow.setHidden(true);
194     			}
195     		}
196     		rearrangedRows.addAll(rewrappedFieldRows);
197     	}
198     	
199     	return rearrangedRows;
200     }
201 
202     
203     /**
204      * This method creates a Section for a MaintenanceDocument.
205      * 
206      * @param sd The DD definition of the Section.
207      * @param o The BusinessObject from which the Section will be populated.
208      * @param maintainable
209      * @param maintenanceAction The action (new, newwithexisting, copy, edit, etc) requested from the UI.
210      * @param autoFillDefaultValues Should default values be auto-filled?
211      * @param autoFillBlankRequiredValues Should required values left blank on the UI be auto-filled?
212      * @param displayedFieldNames What fields are displayed on the UI?
213      * @return A populated Section.
214      * @throws InstantiationException
215      * @throws IllegalAccessException
216      */
217     public static final Section toSection(MaintainableSectionDefinition sd, BusinessObject o, Maintainable maintainable, Maintainable oldMaintainable, String maintenanceAction,  List<String> displayedFieldNames, Set<String> conditionallyRequiredMaintenanceFields) throws InstantiationException, IllegalAccessException {
218     	Section section = new Section();
219 
220         section.setSectionId( sd.getId() );
221         section.setSectionTitle(sd.getTitle());
222         section.setSectionClass(o.getClass());
223         section.setHidden( sd.isHidden() );
224         section.setDefaultOpen(sd.isDefaultOpen());
225         section.setHelpUrl(sd.getHelpUrl());
226 
227         // iterate through section maint items and contruct Field UI objects
228         Collection<MaintainableItemDefinition> maintItems = sd.getMaintainableItems();
229         List<Row> sectionRows = new ArrayList<Row>();
230         List<Field> sectionFields = new ArrayList<Field>();
231 
232         for (MaintainableItemDefinition maintItem : maintItems) {
233             Field field = FieldBridge.toField(maintItem, sd, o, maintainable, section, displayedFieldNames, conditionallyRequiredMaintenanceFields);
234             boolean skipAdd = false;
235 
236             // if CollectionDefiniton, then have a many section
237             if (maintItem instanceof MaintainableCollectionDefinition) {
238                 MaintainableCollectionDefinition definition = (MaintainableCollectionDefinition) maintItem;
239                 section.getContainedCollectionNames().add(maintItem.getName());
240 
241                 StringBuffer containerRowErrorKey = new StringBuffer();
242                 sectionRows = getContainerRows(section, definition, o, maintainable, oldMaintainable, displayedFieldNames, conditionallyRequiredMaintenanceFields, containerRowErrorKey, KRADConstants.DEFAULT_NUM_OF_COLUMNS, null);
243             } else if (maintItem instanceof MaintainableSubSectionHeaderDefinition) {
244                 MaintainableSubSectionHeaderDefinition definition = (MaintainableSubSectionHeaderDefinition) maintItem;
245                 field = createMaintainableSubSectionHeader(definition);
246             }
247 
248             if (!skipAdd) {
249                 sectionFields.add(field);
250             }
251         }
252 
253         // populate field values from business object
254         //if (o != null && !autoFillDefaultValues) {
255         if (o != null) {
256             sectionFields = FieldUtils.populateFieldsFromBusinessObject(sectionFields, o);
257 
258             /* if maintenance action is copy, clear out secure fields */
259             if (KRADConstants.MAINTENANCE_COPY_ACTION.equals(maintenanceAction)) {
260                 for (Iterator iterator = sectionFields.iterator(); iterator.hasNext();) {
261                     Field element = (Field) iterator.next();
262                     if (element.isSecure()) {
263                         element.setPropertyValue("");
264                     }
265                 }
266             }
267         }
268 
269         sectionRows.addAll(FieldUtils.wrapFields(sectionFields));
270         section.setRows(sectionRows);
271 
272         return section;
273 
274     }
275     
276     
277     /**
278      * @see #getContainerRows(Section, CollectionDefinitionI, BusinessObject, Maintainable, List<String>, StringBuffer, String,
279      *      boolean, int)
280      */
281     public static final List<Row> getContainerRows(Section s, CollectionDefinitionI collectionDefinition, BusinessObject o, Maintainable m, Maintainable oldMaintainable, List<String> displayedFieldNames, Set<String> conditionallyRequiredMaintenanceFields, StringBuffer containerRowErrorKey, int numberOfColumns, Inquirable inquirable) {
282         return getContainerRows(s, collectionDefinition, o, m, oldMaintainable, displayedFieldNames, conditionallyRequiredMaintenanceFields, containerRowErrorKey, "", false, numberOfColumns, inquirable);
283     }
284 
285     /**
286      * Builds a list of Rows with Fields of type containers for a many section.
287      * 
288      * @param s The Section containing the Collection/Container.
289      * @param collectionDefinition The DD definition of the Collection.
290      * @param o The BusinessObject from which the Container/Collection will be populated.
291      * @param m The Maintainable for the BO (needed by some methods called on FieldBridge, FieldUtils etc.)
292      * @param displayedFieldNames
293      * @param containerRowErrorKey The error key for the Container/Collection.
294      * @param parents
295      * @param hideAdd Should the add line be added to the Container/Collection?
296      * @param numberOfColumns In how many columns in the UI will the fields in the Container/Collection be shown?
297      * @return
298      */
299      public static final List<Row> getContainerRows(Section s, CollectionDefinitionI collectionDefinition, BusinessObject o, Maintainable m, Maintainable oldMaintainable, List<String> displayedFieldNames, Set<String> conditionallyRequiredMaintenanceFields, StringBuffer containerRowErrorKey, String parents, boolean hideAdd, int numberOfColumns, Inquirable inquirable) {
300         List<Row> containerRows = new ArrayList<Row>();
301         List<Field> collFields = new ArrayList<Field>();
302         
303         String collectionName = collectionDefinition.getName();
304         
305         // add the toggle inactive record display button for the collection
306         if (m != null && Inactivatable.class.isAssignableFrom(collectionDefinition.getBusinessObjectClass()) && StringUtils.isBlank(parents)) {
307             addShowInactiveButtonField(s, collectionName, !m.getShowInactiveRecords(collectionName));
308         }
309         if (inquirable != null && Inactivatable.class.isAssignableFrom(collectionDefinition.getBusinessObjectClass()) && StringUtils.isBlank(parents)) {
310             addShowInactiveButtonField(s, collectionName, !inquirable.getShowInactiveRecords(collectionName));
311         }
312         
313         // first need to populate the containerRows with the "new" form if available
314         if (!hideAdd) {
315             List<Field> newFormFields = new ArrayList<Field>();
316             if (collectionDefinition.getIncludeAddLine()) {
317 
318 
319                 newFormFields = FieldBridge.getNewFormFields(collectionDefinition, o, m, displayedFieldNames, conditionallyRequiredMaintenanceFields, containerRowErrorKey, parents, hideAdd, numberOfColumns);
320 
321 
322             } else if(collectionDefinition instanceof MaintainableCollectionDefinition) {
323                 MaintainableCollectionDefinition mcd = (MaintainableCollectionDefinition)collectionDefinition;
324                 if(FieldUtils.isCollectionMultipleLookupEnabled(mcd)) {
325                     //do just the top line for collection if add is not allowed
326                   newFormFields = FieldBridge.constructContainerField(collectionDefinition, parents, o, hideAdd, numberOfColumns, mcd.getName(), new ArrayList<Field>());
327                 }
328             }
329             if (null != newFormFields) {
330                 containerRows.add(new Row(newFormFields));
331             }
332         }
333 
334         Collection<? extends CollectionDefinitionI> collections = collectionDefinition.getCollections();
335         for (CollectionDefinitionI collection : collections) {
336             int subCollectionNumberOfColumn = numberOfColumns;
337             if (collectionDefinition instanceof InquiryCollectionDefinition) {
338                 InquiryCollectionDefinition icd = (InquiryCollectionDefinition) collection;
339                 if (icd.getNumberOfColumns() != null) {
340                     subCollectionNumberOfColumn = icd.getNumberOfColumns();
341                 }
342             }
343             // no colNum for add rows
344              containerRows.addAll(getContainerRows(s, collection, o, m, oldMaintainable, displayedFieldNames, conditionallyRequiredMaintenanceFields, containerRowErrorKey, parents + collectionDefinition.getName() + ".", true, subCollectionNumberOfColumn, inquirable));
345         }
346 
347         // then we need to loop through the existing collection and add those fields
348         Collection<? extends FieldDefinitionI> collectionFields = collectionDefinition.getFields();
349         // get label for collection
350         String collectionLabel = getDataDictionaryService().getCollectionLabel(o.getClass(), collectionDefinition.getName());
351         
352         // retrieve the summary label either from the override or from the DD
353         String collectionElementLabel = collectionDefinition.getSummaryTitle();
354         if (StringUtils.isEmpty(collectionElementLabel)) {
355             collectionElementLabel = getDataDictionaryService().getCollectionElementLabel(o.getClass().getName(), collectionDefinition.getName(), collectionDefinition.getBusinessObjectClass());
356         }
357 
358         boolean translateCodes = getMaintenanceDocumentDictionaryService().translateCodes(o.getClass());
359 
360         if (o != null) {
361             if (PropertyUtils.isWriteable(o, collectionDefinition.getName()) && ObjectUtils.getPropertyValue(o, collectionDefinition.getName()) != null) {
362                 Object obj = ObjectUtils.getPropertyValue(o, collectionName);
363                 
364                 Object oldObj = null;
365                 if (oldMaintainable != null && oldMaintainable.getBusinessObject() != null) {
366                     oldObj = ObjectUtils.getPropertyValue(oldMaintainable.getBusinessObject(), collectionName);
367                 }
368 
369                 if (obj instanceof List) {
370                     Map summaryFields = new HashMap();
371                     boolean hidableRowsPresent = false;
372                     for (int i = 0; i < ((List) obj).size(); i++) {
373                         BusinessObject lineBusinessObject = (BusinessObject) ((List) obj).get(i);
374                         
375                         if (lineBusinessObject instanceof PersistableBusinessObject) {
376                         	((PersistableBusinessObject) lineBusinessObject).refreshNonUpdateableReferences();
377                         }
378                         
379                         /*
380                          * Handle display of inactive records. The old maintainable is used to compare the old side (if it exists). If the row should not be displayed, it is set as
381                          * hidden and will be handled in the maintenance rowDisplay.tag.   
382                          */  
383                         boolean setRowHidden = false;
384                         BusinessObject oldLineBusinessObject = null;
385                         if (oldObj != null && ((List) oldObj).size() > i) {
386                             oldLineBusinessObject = (BusinessObject) ((List) oldObj).get(i);
387                         }
388                         
389                         if (lineBusinessObject instanceof Inactivatable && !((Inactivatable) lineBusinessObject).isActive()) {
390                             if (m != null) {
391                                 // rendering a maint doc
392                                 if (!hidableRowsPresent) {
393                                     hidableRowsPresent = isRowHideableForMaintenanceDocument(lineBusinessObject, oldLineBusinessObject);
394                                     }
395                                 setRowHidden = isRowHiddenForMaintenanceDocument(lineBusinessObject, oldLineBusinessObject, m, collectionName);
396                                     }
397                             if (inquirable != null) {
398                                 // rendering an inquiry screen
399                                 if (!hidableRowsPresent) {
400                                     hidableRowsPresent = isRowHideableForInquiry(lineBusinessObject);
401                                 }
402                                 setRowHidden = isRowHiddenForInquiry(lineBusinessObject, inquirable, collectionName);
403                             }
404                         }
405 
406                         collFields = new ArrayList<Field>();
407                         List<String> duplicateIdentificationFieldNames = new ArrayList<String>(); 
408                         //We only need to do this if the collection definition is a maintainable collection definition, 
409                         //don't need it for inquiry collection definition.
410                         if (collectionDefinition instanceof MaintainableCollectionDefinition) {
411                             Collection<MaintainableFieldDefinition> duplicateFieldDefs = ((MaintainableCollectionDefinition)collectionDefinition).getDuplicateIdentificationFields();
412                             for (MaintainableFieldDefinition eachFieldDef : duplicateFieldDefs) {
413                                 duplicateIdentificationFieldNames.add(eachFieldDef.getName());
414                             }
415                         }
416                         
417                         for (FieldDefinitionI collectionField : collectionFields) {
418 
419                             // construct Field UI object from definition
420                             Field collField = FieldUtils.getPropertyField(collectionDefinition.getBusinessObjectClass(), collectionField.getName(), false);
421                             
422             				if (translateCodes) {
423             					FieldUtils.setAdditionalDisplayPropertyForCodes(lineBusinessObject.getClass(), collField.getPropertyName(), collField);
424             				}
425                             
426                             FieldBridge.setupField(collField, collectionField, conditionallyRequiredMaintenanceFields);
427                             setPrimaryKeyFieldsReadOnly(collectionDefinition.getBusinessObjectClass(), collField);
428 
429                             //If the duplicateIdentificationFields were specified in the maint. doc. DD, we'll need
430                             //to set the fields to be read only as well, in addition to the primary key fields.
431                             if (duplicateIdentificationFieldNames.size() > 0) {
432                                 setDuplicateIdentificationFieldsReadOnly(collField, duplicateIdentificationFieldNames);
433                             }
434                             
435                             FieldUtils.setInquiryURL(collField, lineBusinessObject, collectionField.getName());
436                             // save the simple property name
437                             String name = collField.getPropertyName();
438 
439                             // prefix name for multi line (indexed)
440                             collField.setPropertyName(collectionDefinition.getName() + "[" + (new Integer(i)).toString() + "]." + collField.getPropertyName());
441 
442                             // commenting out codes for sub-collections show/hide for now
443                             // subCollField.setContainerName(collectionDefinition.getName() + "["+i+"]" +"." +
444                             // subCollectionDefinition.getName() + "[" + j + "]");
445 
446                             if (collectionField instanceof MaintainableFieldDefinition) {
447                                 MaintenanceUtils.setFieldQuickfinder(lineBusinessObject, collectionDefinition.getName(), false, i, name, collField, displayedFieldNames, m, (MaintainableFieldDefinition) collectionField);
448                                 MaintenanceUtils.setFieldDirectInquiry(lineBusinessObject, name, (MaintainableFieldDefinition) collectionField, collField, displayedFieldNames);
449                             } else {
450                                 LookupUtils
451                                         .setFieldQuickfinder(lineBusinessObject, collectionDefinition.getName(), false,
452                                                 i, name, collField, displayedFieldNames, m);
453                                 LookupUtils.setFieldDirectInquiry(lineBusinessObject, name, collField);
454                             }
455 
456                             String propertyValue = ObjectUtils.getFormattedPropertyValueUsingDataDictionary(lineBusinessObject, collectionField.getName());
457                             // For files the FormFile is not being persisted instead the file data is stored in
458                             // individual fields as defined by PersistableAttachment.  However, newly added rows contain all data
459                             // in the FormFile, so check if it's empty.
460                             if (StringUtils.isBlank(propertyValue) && Field.FILE.equals(collField.getFieldType())) {
461                                 Object fileName = ObjectUtils.getNestedValue(lineBusinessObject, KRADConstants.BO_ATTACHMENT_FILE_NAME);
462                                 collField.setPropertyValue(fileName);
463                             } else {
464                                 collField.setPropertyValue(propertyValue);
465 
466                             }
467 
468                             if (Field.FILE.equals(collField.getFieldType())) {
469                                 Object fileType = ObjectUtils.getNestedValue(lineBusinessObject, KRADConstants.BO_ATTACHMENT_FILE_CONTENT_TYPE);
470                                 if (fileType == null
471                                         && collField.getPropertyName().contains(".")) {
472                                     // fileType not found on bo, so check in the attachment field on bo
473                                     String tempName = collField.getPropertyName().substring(collField.getPropertyName().lastIndexOf('.')+1);
474                                     fileType =  ObjectUtils.getNestedValue(lineBusinessObject, (tempName + "." + KRADConstants.BO_ATTACHMENT_FILE_CONTENT_TYPE));
475                                 }
476                                 collField.setImageSrc(WebUtils.getAttachmentImageForUrl((String) fileType));
477                             }
478                             
479 							if (StringUtils.isNotBlank(collField.getAlternateDisplayPropertyName())) {
480 								String alternateDisplayPropertyValue = ObjectUtils.getFormattedPropertyValueUsingDataDictionary(lineBusinessObject,
481 										collField.getAlternateDisplayPropertyName());
482 								collField.setAlternateDisplayPropertyValue(alternateDisplayPropertyValue);
483 							}
484 							
485 							if (StringUtils.isNotBlank(collField.getAdditionalDisplayPropertyName())) {
486 								String additionalDisplayPropertyValue = ObjectUtils.getFormattedPropertyValueUsingDataDictionary(lineBusinessObject,
487 										collField.getAdditionalDisplayPropertyName());
488 								collField.setAdditionalDisplayPropertyValue(additionalDisplayPropertyValue);
489 							}
490                                 
491 							//update user fields with universal id and/or name
492 							updateUserFields(collField, lineBusinessObject);
493                                 
494                             // the the field as read only (if appropriate)
495                             if (collectionField.isReadOnlyAfterAdd()) {
496                                 collField.setReadOnly(true);
497                             }
498 
499                             // check if this is a summary field
500                             if (collectionDefinition.hasSummaryField(collectionField.getName())) {
501                                 summaryFields.put(collectionField.getName(), collField);
502                             }
503 
504                             collFields.add(collField);
505                         }
506 
507                         Field containerField;
508                         containerField = FieldUtils.constructContainerField(
509                                 KRADConstants.EDIT_PREFIX + "[" + (new Integer(i)).toString() + "]", collectionLabel + " " + (i + 1), collFields, numberOfColumns);
510                         // why is this only on collections and not subcollections any significance or just oversight?
511                         containerField.setContainerName(collectionDefinition.getName() + "[" + (new Integer(i)).toString() + "].");
512 
513                         /* If the collection line is pending (meaning added by this document) the isNewCollectionRecord will be set to true. In this
514                            case we give an option to delete the line. The parameters for the delete action method are embedded into the button name. */
515                         if (lineBusinessObject instanceof PersistableBusinessObject && 
516                         		(((PersistableBusinessObject) lineBusinessObject).isNewCollectionRecord() 
517                         				|| collectionDefinition.isAlwaysAllowCollectionDeletion())) {
518                             containerField.getContainerRows().add(new Row(getDeleteRowButtonField(parents + collectionDefinition.getName(), (new Integer(i)).toString())));
519                         }
520 
521                         if (StringUtils.isNotEmpty(collectionElementLabel)) {
522                             //We don't want to associate any indexes to the containerElementName anymore so that
523                             //when the element is deleted, the currentTabIndex won't be associated with the
524                             //wrong tab for the remaining tab.
525                             //containerField.setContainerElementName(collectionElementLabel + " " + (i + 1));
526                             containerField.setContainerElementName(collectionElementLabel);
527                             // reorder summaryFields to make sure they are in the order specified in the summary section
528                             List orderedSummaryFields = getSummaryFields(summaryFields, collectionDefinition);
529                             containerField.setContainerDisplayFields(orderedSummaryFields);
530                         }
531                         
532                         Row containerRow = new Row(containerField);
533                         if (setRowHidden) {
534                             containerRow.setHidden(true);
535                         }
536                         containerRows.add(containerRow);
537                         
538                         
539 
540                         Collection<? extends CollectionDefinitionI> subCollections = collectionDefinition.getCollections();
541                         List<Field> subCollFields = new ArrayList<Field>();
542 
543                         summaryFields = new HashMap();
544                         // iterate over the subCollections directly on this collection
545                         for (CollectionDefinitionI subCollection : subCollections) {
546                             Collection<? extends FieldDefinitionI> subCollectionFields = subCollection.getFields();
547                             int subCollectionNumberOfColumns = numberOfColumns;
548 
549                             if (!s.getContainedCollectionNames().contains(collectionDefinition.getName() + "." + subCollection.getName())) {
550                                 s.getContainedCollectionNames().add(collectionDefinition.getName() + "." + subCollection.getName());
551                             }
552 
553                             if (subCollection instanceof InquiryCollectionDefinition) {
554                                 InquiryCollectionDefinition icd = (InquiryCollectionDefinition) subCollection;
555                                 if (icd.getNumberOfColumns() != null) {
556                                     subCollectionNumberOfColumns = icd.getNumberOfColumns();
557                                 }
558                             }
559                             // get label for collection
560                             String subCollectionLabel = getDataDictionaryService().getCollectionLabel(o.getClass(), subCollection.getName());
561 
562                             // retrieve the summary label either from the override or from the DD
563                             String subCollectionElementLabel = subCollection.getSummaryTitle();
564                             if (StringUtils.isEmpty(subCollectionElementLabel)) {
565                                 subCollectionElementLabel = getDataDictionaryService().getCollectionElementLabel(o.getClass().getName(), subCollection.getName(), subCollection.getBusinessObjectClass());
566                             }
567                             // make sure it's really a collection (i.e. list)
568 
569                             String subCollectionName = subCollection.getName();
570                             Object subObj = ObjectUtils.getPropertyValue(lineBusinessObject, subCollectionName);
571                             
572                             Object oldSubObj = null;
573                             if (oldLineBusinessObject != null) {
574                                 oldSubObj = ObjectUtils.getPropertyValue(oldLineBusinessObject, subCollectionName);
575                             }
576                             
577                             if (subObj instanceof List) {
578                                 /* recursively call this method to get the add row and exisiting members of the subCollections subcollections containerRows.addAll(getContainerRows(subCollectionDefinition,
579                                    displayedFieldNames,containerRowErrorKey, parents+collectionDefinition.getName()+"["+i+"]"+".","[0]",false, subCollectionNumberOfColumn)); */
580                                 containerField.getContainerRows().addAll(getContainerRows(s, subCollection, o, m, oldMaintainable, displayedFieldNames, conditionallyRequiredMaintenanceFields, containerRowErrorKey, parents + collectionDefinition.getName() + "[" + i + "]" + ".", false, subCollectionNumberOfColumns, inquirable));
581                              
582                                 // iterate over the fields
583                                 for (int j = 0; j < ((List) subObj).size(); j++) {
584                                     BusinessObject lineSubBusinessObject = (BusinessObject) ((List) subObj).get(j);
585                                     
586                                     if (lineSubBusinessObject instanceof PersistableBusinessObject) {
587                                     	((PersistableBusinessObject) lineSubBusinessObject).refreshNonUpdateableReferences();
588                                     }
589                                     
590                                     // determine if sub collection line is inactive and should be hidden
591                                     boolean setSubRowHidden = false;
592                                     if (lineSubBusinessObject instanceof Inactivatable && !((Inactivatable) lineSubBusinessObject).isActive()) {
593                                         if (oldSubObj != null) { 
594                                             // get corresponding elements in both the new list and the old list
595                                             BusinessObject oldLineSubBusinessObject = (BusinessObject) ((List) oldSubObj).get(j);
596                                             if (m != null) {
597                                                     if (!hidableRowsPresent) {
598                                                         hidableRowsPresent = isRowHideableForMaintenanceDocument(lineSubBusinessObject, oldLineSubBusinessObject);
599                                                 }
600                                                     setSubRowHidden = isRowHiddenForMaintenanceDocument(lineSubBusinessObject, oldLineSubBusinessObject, m, collectionName);
601                                                 }
602                                             }
603                                         if (inquirable != null) {
604                                             if (!hidableRowsPresent) {
605                                                 hidableRowsPresent = isRowHideableForInquiry(lineSubBusinessObject);
606                                         }
607                                             setSubRowHidden = isRowHiddenForInquiry(lineSubBusinessObject, inquirable, collectionName);
608                                     }
609                                     }
610 
611                                     
612                                     subCollFields = new ArrayList<Field>();
613                                     // construct field objects based on fields
614                                     for (FieldDefinitionI subCollectionField : subCollectionFields) {
615 
616                                         // construct Field UI object from definition
617                                         Field subCollField = FieldUtils.getPropertyField(subCollection.getBusinessObjectClass(), subCollectionField.getName(), false);
618 
619                                         String subCollectionFullName = collectionDefinition.getName() + "[" + i + "]" + "." + subCollection.getName();
620                                         
621                         				if (translateCodes) {
622                         					FieldUtils.setAdditionalDisplayPropertyForCodes(lineSubBusinessObject.getClass(), subCollField.getPropertyName(), subCollField);
623                         				}
624 
625                                         FieldBridge.setupField(subCollField, subCollectionField, conditionallyRequiredMaintenanceFields);
626                                         setPrimaryKeyFieldsReadOnly(subCollection.getBusinessObjectClass(), subCollField);
627                                        
628                                         // save the simple property name
629                                         String name = subCollField.getPropertyName();
630 
631                                         // prefix name for multi line (indexed)
632                                         subCollField.setPropertyName(subCollectionFullName + "[" + j + "]." + subCollField.getPropertyName());
633 
634                                         // commenting out codes for sub-collections show/hide for now
635                                         if (subCollectionField instanceof MaintainableFieldDefinition) {
636                                             MaintenanceUtils.setFieldQuickfinder(lineSubBusinessObject, subCollectionFullName, false, j, name, subCollField, displayedFieldNames, m, (MaintainableFieldDefinition) subCollectionField);
637                                             MaintenanceUtils
638                                                     .setFieldDirectInquiry(lineSubBusinessObject, subCollectionFullName,
639                                                             false, j, name, subCollField, displayedFieldNames, m,
640                                                             (MaintainableFieldDefinition) subCollectionField);
641                                         } else {
642                                             LookupUtils.setFieldQuickfinder(lineSubBusinessObject, subCollectionFullName, false, j, name, subCollField, displayedFieldNames);
643                                             LookupUtils.setFieldDirectInquiry(lineBusinessObject, name, subCollField);
644                                         }
645 
646                                         String propertyValue = ObjectUtils.getFormattedPropertyValueUsingDataDictionary(lineSubBusinessObject, subCollectionField.getName());
647                                         subCollField.setPropertyValue(propertyValue);
648                                         
649             							if (StringUtils.isNotBlank(subCollField.getAlternateDisplayPropertyName())) {
650             								String alternateDisplayPropertyValue = ObjectUtils.getFormattedPropertyValueUsingDataDictionary(lineSubBusinessObject,
651             										subCollField.getAlternateDisplayPropertyName());
652             								subCollField.setAlternateDisplayPropertyValue(alternateDisplayPropertyValue);
653             							}
654                                         
655             							if (StringUtils.isNotBlank(subCollField.getAdditionalDisplayPropertyName())) {
656             								String additionalDisplayPropertyValue = ObjectUtils.getFormattedPropertyValueUsingDataDictionary(lineSubBusinessObject,
657             										subCollField.getAdditionalDisplayPropertyName());
658             								subCollField.setAdditionalDisplayPropertyValue(additionalDisplayPropertyValue);
659             							}
660                                      
661                                         // check if this is a summary field
662                                         if (subCollection.hasSummaryField(subCollectionField.getName())) {
663                                             summaryFields.put(subCollectionField.getName(), subCollField);
664                                         }
665 
666                                         if (subCollectionField.isReadOnlyAfterAdd()) {
667                                             subCollField.setReadOnly(true);
668                                         }
669 
670                                         subCollFields.add(subCollField);
671                                     }
672 
673                                     Field subContainerField = FieldUtils.constructContainerField(
674                                             KRADConstants.EDIT_PREFIX + "[" + (new Integer(j)).toString() + "]", subCollectionLabel, subCollFields);
675                                     if (lineSubBusinessObject instanceof PersistableBusinessObject && (((PersistableBusinessObject) lineSubBusinessObject).isNewCollectionRecord() || subCollection.isAlwaysAllowCollectionDeletion())) {
676                                         subContainerField.getContainerRows().add(new Row(getDeleteRowButtonField(parents + collectionDefinition.getName() + "[" + i + "]" + "." + subCollectionName, (new Integer(j)).toString())));
677                                     }
678 
679                                     // summary line code
680                                     if (StringUtils.isNotEmpty(subCollectionElementLabel)) {
681                                         //We don't want to associate any indexes to the containerElementName anymore so that
682                                         //when the element is deleted, the currentTabIndex won't be associated with the
683                                         //wrong tab for the remaining tab.
684                                         //subContainerField.setContainerElementName(subCollectionElementLabel + " " + (j + 1));
685                                         subContainerField.setContainerElementName(collectionElementLabel + "-" + subCollectionElementLabel);
686                                     }
687                                     subContainerField.setContainerName(collectionDefinition.getName() + "." + subCollectionName);
688                                     if (!summaryFields.isEmpty()) {
689                                         // reorder summaryFields to make sure they are in the order specified in the summary section
690                                         List orderedSummaryFields = getSummaryFields(summaryFields, subCollection);
691                                         subContainerField.setContainerDisplayFields(orderedSummaryFields);
692                                     }
693                                     
694                                     Row subContainerRow = new Row(subContainerField);
695                                     if (setRowHidden || setSubRowHidden) {
696                                         subContainerRow.setHidden(true);
697                                     }
698                                     containerField.getContainerRows().add(subContainerRow);
699                                 }
700                             }
701                         }
702                     }
703                     if ( !hidableRowsPresent ) {
704                         s.setExtraButtonSource( "" );
705                     }
706                 }
707             }
708         }
709         
710         return containerRows;
711     }
712 
713     /**
714       * Updates fields of type kualiuser sets the universal user id and/or name if required. 
715       * 
716       * @param field
717       * @param businessObject
718       */
719      private static final void updateUserFields(Field field, BusinessObject businessObject){
720          // for user fields, attempt to pull the principal ID and person's name from the source object
721          if ( field.getFieldType().equals(Field.KUALIUSER) ) {
722              // this is supplemental, so catch and log any errors
723              try {
724                  if ( StringUtils.isNotBlank(field.getUniversalIdAttributeName()) ) {
725                      Object principalId = ObjectUtils.getNestedValue(businessObject, field.getUniversalIdAttributeName());
726                      if ( principalId != null ) {
727                          field.setUniversalIdValue(principalId.toString());
728                      }
729                  }
730                  if ( StringUtils.isNotBlank(field.getPersonNameAttributeName()) ) {
731                      Object personName = ObjectUtils.getNestedValue(businessObject, field.getPersonNameAttributeName());
732                      if ( personName != null ) {
733                          field.setPersonNameValue( personName.toString() );
734                      }
735                  }
736              } catch ( Exception ex ) {
737                  LOG.warn( "Unable to get principal ID or person name property in SectionBridge.", ex );
738              }
739          }
740      }
741      
742     /**
743      * Helper method to build up a Field containing a delete button mapped up to remove the collection record identified by the
744      * given collection name and index.
745      * 
746      * @param collectionName - name of the collection
747      * @param rowIndex - index of the record to associate delete button
748      * @return Field - of type IMAGE_SUBMIT
749      */
750     private static final Field getDeleteRowButtonField(String collectionName, String rowIndex) {
751         Field deleteButtonField = new Field();
752 
753         String deleteButtonName = KRADConstants.DISPATCH_REQUEST_PARAMETER + "." + KRADConstants.DELETE_LINE_METHOD + "." + collectionName + "." + KRADConstants.METHOD_TO_CALL_BOPARM_LEFT_DEL + ".line" + rowIndex;
754         deleteButtonField.setPropertyName(deleteButtonName);
755         deleteButtonField.setFieldType(Field.IMAGE_SUBMIT);
756         deleteButtonField.setPropertyValue("images/tinybutton-delete1.gif");
757 
758         return deleteButtonField;
759     }
760     
761     /**
762      * Helper method to build up the show inactive button source and place in the section.
763      * 
764      * @param section - section that will display the button
765      * @param collectionName - name of the collection to toggle setting
766      * @param showInactive - boolean indicating whether inactive rows should be displayed
767      * @return Field - of type IMAGE_SUBMIT
768      */
769     private static final void addShowInactiveButtonField(Section section, String collectionName, boolean showInactive) {
770     	String methodName = KRADConstants.DISPATCH_REQUEST_PARAMETER + "." + KRADConstants.TOGGLE_INACTIVE_METHOD + "." + collectionName.replace( '.', '_' );
771         methodName += "." + KRADConstants.METHOD_TO_CALL_BOPARM_LEFT_DEL + showInactive + ".anchorshowInactive." + collectionName + KRADConstants.METHOD_TO_CALL_BOPARM_RIGHT_DEL;
772            
773         String imageSource = showInactive ? "tinybutton-showinact.gif" : "tinybutton-hideinact.gif";
774 
775         String showInactiveButton = "property=" + methodName + ";src=" + imageSource + ";alt=show(hide) inactive" + ";title=show(hide) inactive";
776 
777         section.setExtraButtonSource(showInactiveButton);
778     }
779     
780     /**
781      * Retrieves the primary key property names for the given class. If the field's property is one of those keys, makes the field
782      * read-only. This is called for collection lines. Since deletion is not allowed for existing lines, the pk fields must be
783      * read-only, otherwise a user could change the pk value which would be equivalent to deleting the line and adding a new line.
784      */
785     private static final void setPrimaryKeyFieldsReadOnly(Class businessObjectClass, Field field) {
786     	try{
787     		//TODO: Revisit this. Changing since getPrimaryKeys and listPrimaryKeyFieldNames are apparently same.
788     		//May be we might want to replace listPrimaryKeyFieldNames with getPrimaryKeys... Not sure.
789 	    	List primaryKeyPropertyNames = 
790 	    		KRADServiceLocatorWeb.getLegacyDataAdapter().listPrimaryKeyFieldNames(businessObjectClass);
791 	        if (primaryKeyPropertyNames.contains(field.getPropertyName())) {
792 	            field.setReadOnly(true);
793 	        }
794     	} catch(ClassNotPersistableException ex){
795     		//Not all classes will be persistable in a collection. For e.g. externalizable business objects.
796     		LOG.info("Not persistable dataObjectClass: "+businessObjectClass+", field: "+field);
797     	}
798     }
799     
800     private static void setDuplicateIdentificationFieldsReadOnly(Field field, List<String>duplicateIdentificationFieldNames) {
801         if (duplicateIdentificationFieldNames.contains(field.getPropertyName())) {
802             field.setReadOnly(true);
803         }
804     }
805 
806     /**
807      * This method returns an ordered list of fields.
808      * 
809      * @param collSummaryFields
810      * @param collectionDefinition
811      * @return
812      */
813     private static final List<Field> getSummaryFields(Map collSummaryFields, CollectionDefinitionI collectionDefinition) {
814         List<Field> orderedSummaryFields = new ArrayList<Field>();
815         for (FieldDefinitionI summaryField : collectionDefinition.getSummaryFields()) {
816             String name = summaryField.getName();
817             boolean found = false;
818             Field addField = (Field) collSummaryFields.get(name);
819             if (!(addField == null)) {
820                 orderedSummaryFields.add(addField);
821                 found = true;
822             }
823 
824             if (!found) {
825                 // should we throw a real error here?
826                 LOG.error("summaryField " + summaryField + " not present in the list");
827             }
828 
829         }
830         return orderedSummaryFields;
831     }
832 
833     /**
834      * This is a helper method to create a sub section header
835      * 
836      * @param definition the MaintainableSubSectionHeaderDefinition that we'll use to create the sub section header
837      * @return the Field, which is the sub section header
838      */
839     private static final Field createMaintainableSubSectionHeader(SubSectionHeaderDefinitionI definition) {
840         Field separatorField = new Field();
841         separatorField.setFieldLabel(definition.getName());
842         separatorField.setFieldType(Field.SUB_SECTION_SEPARATOR);
843         separatorField.setReadOnly(true);
844 
845         return separatorField;
846     }
847     
848     /**
849      * Determines whether a business object is hidable on a maintenance document.  Hidable means that if the user chose to hide the inactive
850      * elements in the collection in which the passed in BOs reside, then the BOs would be hidden
851      * 
852      * @param lineBusinessObject the BO in the new maintainable, should be of type {@link BusinessObject} and {@link Inquirable}
853      * @param oldLineBusinessObject the corresponding BO in the old maintainable, should be of type {@link BusinessObject} and 
854      * {@link Inquirable}
855      * @return whether the BOs are eligible to be hidden if the user decides to hide them
856      */
857     protected static boolean isRowHideableForMaintenanceDocument(BusinessObject lineBusinessObject, BusinessObject oldLineBusinessObject) {
858         if (oldLineBusinessObject != null) {
859         	if ( lineBusinessObject instanceof PersistableBusinessObject ) {
860 	            if (((PersistableBusinessObject) lineBusinessObject).isNewCollectionRecord()) {
861 	                // new records are never hidden, regardless of active status
862 	                return false;
863 	            }
864         	}
865             if (!((Inactivatable) lineBusinessObject).isActive() && !((Inactivatable) oldLineBusinessObject).isActive()) {
866                 // records with an old and new collection elements of NOT active are eligible to be hidden
867                 return true;
868             }
869         }
870         return false;
871     }
872     /**
873      * Determines whether a business object is hidden on a maintenance document.
874      * 
875      * @param lineBusinessObject the BO in the new maintainable, should be of type {@link BusinessObject}
876      * @param oldLineBusinessObject the corresponding BO in the old maintainable
877      * @param newMaintainable the new maintainable from the maintenace document
878      * @param collectionName the name of the collection from which these BOs come
879      * @return
880      */
881     protected static boolean isRowHiddenForMaintenanceDocument(BusinessObject lineBusinessObject, BusinessObject oldLineBusinessObject,
882             Maintainable newMaintainable, String collectionName) {
883         return isRowHideableForMaintenanceDocument(lineBusinessObject, oldLineBusinessObject) && !newMaintainable.getShowInactiveRecords(collectionName);
884     }
885     
886     /**
887      * Determines whether a business object is hidable on an inquiry screen.  Hidable means that if the user chose to hide the inactive
888      * elements in the collection in which the passed in BO resides, then the BO would be hidden
889      * 
890      * @param lineBusinessObject the collection element BO, should be of type {@link BusinessObject} and {@link Inquirable}
891      * @return whether the BO is eligible to be hidden if the user decides to hide them
892      */
893     protected static boolean isRowHideableForInquiry(BusinessObject lineBusinessObject) {
894         return !((Inactivatable) lineBusinessObject).isActive();
895     }
896     
897     /**
898      * Determines whether a business object is hidden on an inquiry screen.
899      * 
900      * @param lineBusinessObject the BO in the collection, should be of type {@link BusinessObject} and {@link Inquirable}
901      * @param inquirable the inquirable
902      * @param collectionName the name of the collection from which the BO comes
903      * @return true if the business object is to be hidden; false otherwise
904      */
905     protected static boolean isRowHiddenForInquiry(BusinessObject lineBusinessObject, Inquirable inquirable, String collectionName) {
906         return isRowHideableForInquiry(lineBusinessObject) && !inquirable.getShowInactiveRecords(collectionName);
907     }
908     
909 	public static MaintenanceDocumentDictionaryService getMaintenanceDocumentDictionaryService() {
910     	if (maintenanceDocumentDictionaryService == null) {
911     		maintenanceDocumentDictionaryService = KNSServiceLocator.getMaintenanceDocumentDictionaryService();
912     	}
913 		return maintenanceDocumentDictionaryService; 
914 	}
915 }
916