View Javadoc

1   /*
2    * Copyright 2007 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 java.util.ArrayList;
19  import java.util.Collection;
20  import java.util.Iterator;
21  import java.util.List;
22  import java.util.Set;
23  
24  import org.apache.commons.lang.StringUtils;
25  import org.kuali.rice.core.util.ClassLoaderUtils;
26  import org.kuali.rice.core.util.KeyLabelPair;
27  import org.kuali.rice.kns.bo.BusinessObject;
28  import org.kuali.rice.kns.datadictionary.CollectionDefinitionI;
29  import org.kuali.rice.kns.datadictionary.FieldDefinition;
30  import org.kuali.rice.kns.datadictionary.FieldDefinitionI;
31  import org.kuali.rice.kns.datadictionary.MaintainableCollectionDefinition;
32  import org.kuali.rice.kns.datadictionary.MaintainableFieldDefinition;
33  import org.kuali.rice.kns.datadictionary.MaintainableItemDefinition;
34  import org.kuali.rice.kns.datadictionary.MaintainableSectionDefinition;
35  import org.kuali.rice.kns.datadictionary.control.ApcSelectControlDefinition;
36  import org.kuali.rice.kns.datadictionary.control.ControlDefinition;
37  import org.kuali.rice.kns.lookup.LookupUtils;
38  import org.kuali.rice.kns.lookup.keyvalues.ApcValuesFinder;
39  import org.kuali.rice.kns.lookup.keyvalues.KeyValuesFinder;
40  import org.kuali.rice.kns.lookup.keyvalues.PersistableBusinessObjectValuesFinder;
41  import org.kuali.rice.kns.maintenance.Maintainable;
42  import org.kuali.rice.kns.service.BusinessObjectDictionaryService;
43  import org.kuali.rice.kns.service.DataDictionaryService;
44  import org.kuali.rice.kns.service.KNSServiceLocator;
45  import org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService;
46  import org.kuali.rice.kns.service.PersistenceStructureService;
47  import org.kuali.rice.kns.util.FieldUtils;
48  import org.kuali.rice.kns.util.KNSConstants;
49  import org.kuali.rice.kns.util.MaintenanceUtils;
50  import org.kuali.rice.kns.util.ObjectUtils;
51  import org.kuali.rice.kns.web.format.Formatter;
52  
53  public class FieldBridge {
54      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(FieldBridge.class);
55      private static DataDictionaryService dataDictionaryService;
56      private static PersistenceStructureService persistenceStructureService;
57      private static BusinessObjectDictionaryService businessObjectDictionaryService;
58      private static MaintenanceDocumentDictionaryService maintenanceDocumentDictionaryService;
59  
60      /**
61       * Sets additional properties for MaintainableField(s)
62       *
63       * @param field The field to populate.
64       * @param definition The DD specification for the field.
65       */
66      public static final void setupField(Field field, FieldDefinitionI definition, Set<String> conditionallyRequiredMaintenanceFields) {
67          if (definition instanceof MaintainableFieldDefinition) {
68              MaintainableFieldDefinition maintainableFieldDefinition = ((MaintainableFieldDefinition) definition);
69              
70              field.setFieldRequired(maintainableFieldDefinition.isRequired());
71              field.setReadOnly(maintainableFieldDefinition.isUnconditionallyReadOnly());
72              if (maintainableFieldDefinition.isLookupReadOnly()) {
73              	field.setFieldType(Field.LOOKUP_READONLY);
74              }
75  
76              // set onblur and callback functions
77              if (StringUtils.isNotBlank(maintainableFieldDefinition.getWebUILeaveFieldFunction())) {
78                  field.setWebOnBlurHandler(maintainableFieldDefinition.getWebUILeaveFieldFunction());
79              }
80  
81              if (StringUtils.isNotBlank(maintainableFieldDefinition.getWebUILeaveFieldCallbackFunction())) {
82                  field.setWebOnBlurHandlerCallback(maintainableFieldDefinition.getWebUILeaveFieldCallbackFunction());
83              }
84  
85              if (maintainableFieldDefinition.getWebUILeaveFieldFunctionParameters()!=null) {
86                  field.setWebUILeaveFieldFunctionParameters(maintainableFieldDefinition.getWebUILeaveFieldFunctionParameters());
87              }
88              
89  			if (StringUtils.isNotBlank(maintainableFieldDefinition.getAlternateDisplayAttributeName())) {
90  				field.setAlternateDisplayPropertyName(maintainableFieldDefinition.getAlternateDisplayAttributeName());
91  			}
92  
93  			if (StringUtils.isNotBlank(maintainableFieldDefinition.getAdditionalDisplayAttributeName())) {
94  				field.setAdditionalDisplayPropertyName(maintainableFieldDefinition.getAdditionalDisplayAttributeName());
95  			}
96              
97              if (conditionallyRequiredMaintenanceFields != null && conditionallyRequiredMaintenanceFields.contains(field.getPropertyName())) {
98              	field.setFieldRequired(true);
99              }
100             
101             if (((MaintainableFieldDefinition) definition).isTriggerOnChange()) {
102             	field.setTriggerOnChange(true);
103             }
104         }
105     }
106 
107     /**
108 	 * Uses reflection to populate the rows of the inquiry from the business
109 	 * object value. Also formats if needed.
110      *
111 	 * @param field
112 	 *            The Field to populate.
113 	 * @param bo
114 	 *            The BusinessObject from which the Field will be popualated.
115      */
116     public static final void populateFieldFromBusinessObject(Field field, BusinessObject bo) {
117         if (bo == null) {
118             throw new RuntimeException("Inquiry Business object is null.");
119         }
120 
121         field.setReadOnly(true); // inquiry fields are always read only
122 
123         Formatter formatter = field.getFormatter();
124         String propertyName = field.getPropertyName();
125 
126         // get the field type for the property
127 		ControlDefinition fieldControl = getDataDictionaryService().getAttributeControlDefinition(bo.getClass(),
128 				propertyName);
129         try {
130             Object prop = ObjectUtils.getPropertyValue(bo, field.getPropertyName());
131 
132 			// for select fields, display the associated label (unless we are
133 			// already display an additonal attribute)
134 			String propValue = KNSConstants.EMPTY_STRING;
135 			if (fieldControl != null && fieldControl.isSelect()
136 					&& StringUtils.isBlank(field.getAdditionalDisplayPropertyName())
137 					&& StringUtils.isBlank(field.getAlternateDisplayPropertyName())) {
138 				Class<KeyValuesFinder> keyValuesFinderName = ClassLoaderUtils.getClass(fieldControl
139 						.getValuesFinderClass());
140                 KeyValuesFinder finder = keyValuesFinderName.newInstance();
141 
142                 propValue = lookupFinderValue(fieldControl, prop, finder);
143             } else {
144 				propValue = ObjectUtils.getFormattedPropertyValue(bo, field.getPropertyName(), formatter);
145                     }
146 			field.setPropertyValue(propValue);
147 			
148 			// set additional or alternate display property values if property
149 			// name is specified
150 			if (StringUtils.isNotBlank(field.getAlternateDisplayPropertyName())) {
151 				String alternatePropertyValue = ObjectUtils.getFormattedPropertyValueUsingDataDictionary(bo, field
152 						.getAlternateDisplayPropertyName());
153 				field.setAlternateDisplayPropertyValue(alternatePropertyValue);
154                 }
155 
156 			if (StringUtils.isNotBlank(field.getAdditionalDisplayPropertyName())) {
157 				String additionalPropertyValue = ObjectUtils.getFormattedPropertyValueUsingDataDictionary(bo, field
158 						.getAdditionalDisplayPropertyName());
159 				field.setAdditionalDisplayPropertyValue(additionalPropertyValue);
160             }
161 
162 			// for user fields, attempt to pull the principal ID and person's
163 			// name from the source object
164             if ( fieldControl != null && fieldControl.isKualiUser() ) {
165             	// this is supplemental, so catch and log any errors 
166             	try {
167             		if ( StringUtils.isNotBlank(field.getUniversalIdAttributeName()) ) {
168             			Object principalId = ObjectUtils.getNestedValue(bo, field.getUniversalIdAttributeName());
169             			if ( principalId != null ) {
170             				field.setUniversalIdValue(principalId.toString());
171             			}
172             		}
173             		if ( StringUtils.isNotBlank(field.getPersonNameAttributeName()) ) {
174             			Object personName = ObjectUtils.getNestedValue(bo, field.getPersonNameAttributeName());
175             			if ( personName != null ) {
176             				field.setPersonNameValue( personName.toString() );
177             			}
178             		}
179             	} catch ( Exception ex ) {
180             		LOG.warn( "Unable to get principal ID or person name property in FieldBridge.", ex );
181             	}
182             }
183             FieldUtils.setInquiryURL(field, bo, propertyName);
184 		} catch (InstantiationException e) {
185             LOG.error("Unable to get instance of KeyValuesFinder: " + e.getMessage());
186             throw new RuntimeException("Unable to get instance of KeyValuesFinder: " + e.getMessage());
187 		} catch (IllegalAccessException e) {
188             LOG.error("Unable to set columns: " + e.getMessage());
189             throw new RuntimeException("Unable to set columns: " + e.getMessage());
190         }
191 
192     }
193 
194     /**
195      * This method looks up a value in a finder class.
196      * @param fieldControl the type of web control that is associated with this field.
197      * @param prop the property to look up - either a property name as a String, or a referenced object
198      * @param finder finder to look the value up in
199      * @return the value that was returned from the lookup
200      */
201     private static String lookupFinderValue(ControlDefinition fieldControl, Object prop, KeyValuesFinder finder) {
202         String propValue = null;
203 
204         if (finder != null) {
205             if (finder instanceof ApcValuesFinder && fieldControl instanceof ApcSelectControlDefinition) {
206                 ((ApcValuesFinder) finder).setParameterNamespace(((ApcSelectControlDefinition) fieldControl).getParameterNamespace());
207                 ((ApcValuesFinder) finder).setParameterDetailType(((ApcSelectControlDefinition) fieldControl).getParameterDetailType());
208                 ((ApcValuesFinder) finder).setParameterName(((ApcSelectControlDefinition) fieldControl).getParameterName());
209             }
210         }
211 
212         // KULRICE-1808 : PersistableBusinessObjectValuesFinder is not working for inquiries that have child objects with ValuesFinder populated select lists
213         if (finder instanceof PersistableBusinessObjectValuesFinder) {
214             ((PersistableBusinessObjectValuesFinder) finder).setBusinessObjectClass(ClassLoaderUtils.getClass(fieldControl.getBusinessObjectClass()));
215             ((PersistableBusinessObjectValuesFinder) finder).setKeyAttributeName(fieldControl.getKeyAttribute());
216             ((PersistableBusinessObjectValuesFinder) finder).setLabelAttributeName(fieldControl.getLabelAttribute());
217             if (fieldControl.getIncludeBlankRow() != null) {
218             	((PersistableBusinessObjectValuesFinder) finder).setIncludeBlankRow(fieldControl.getIncludeBlankRow());
219             }
220             ((PersistableBusinessObjectValuesFinder) finder).setIncludeKeyInDescription(fieldControl.getIncludeKeyInLabel());
221         }
222         List keyValues = finder.getKeyValues();
223         propValue = getPropertyValueFromList(prop, keyValues);
224         if(propValue==null)
225         	propValue = lookupInactiveFinderValue(prop, finder);
226         return propValue;
227     }
228     
229     private static String lookupInactiveFinderValue(Object property, KeyValuesFinder finder){
230     	List keyValues = finder.getKeyValues(false);
231     	return getPropertyValueFromList(property, keyValues);
232     	
233     }
234     
235     private static String getPropertyValueFromList(Object property, List keyValues){
236     	String propertyValue = null;
237         if (property != null) {
238             for (Iterator iter = keyValues.iterator(); iter.hasNext();) {
239                 KeyLabelPair element = (KeyLabelPair) iter.next();
240                 if (element.getKey().toString().equals(property.toString())) {
241                     propertyValue = element.getLabel();
242                     break;
243                 }
244             }
245         }
246         return propertyValue;
247     }
248     
249     /**
250      * Determines whether field level help is enabled for the field corresponding to the businessObjectClass and attribute name
251      *
252      * If this value is true, then the field level help will be enabled.
253      * If false, then whether a field is enabled is determined by the value returned by {@link #isMaintenanceFieldLevelHelpDisabled(Maintainable, MaintainableFieldDefinition)}
254      * and the system-wide parameter setting.  Note that if a field is read-only, that may cause field-level help to not be rendered.
255      *
256      * @param businessObjectClass the looked up class
257      * @param attributeName the attribute for the field
258      * @return true if field level help is enabled, false if the value of this method should NOT be used to determine whether this method's return value
259      * affects the enablement of field level help
260      */
261     protected static boolean isMaintenanceFieldLevelHelpEnabled(Maintainable m, MaintainableFieldDefinition fieldDefinition) {
262         if ( fieldDefinition != null ) {
263             if ( fieldDefinition.isShowFieldLevelHelp() != null && fieldDefinition.isShowFieldLevelHelp() ) {
264                 return true;
265             }
266         }
267         return false;
268     }
269 
270     /**
271      * Determines whether field level help is disabled for the field corresponding to the businessObjectClass and attribute name
272      *
273      * If this value is true and {@link #isMaintenanceFieldLevelHelpEnabled(Maintainable, MaintainableFieldDefinition)} returns false,
274      * then the field level help will not be rendered.  If both this and {@link #isMaintenanceFieldLevelHelpEnabled(Maintainable, MaintainableFieldDefinition)} return false,
275      * then the system-wide setting will determine whether field level help is enabled.  Note that if a field is read-only, that may cause
276      * field-level help to not be rendered.
277      *
278      * @param businessObjectClass the looked up class
279      * @param attributeName the attribute for the field
280      * @return true if field level help is disabled, false if the value of this method should NOT be used to determine whether this method's return value
281      * affects the enablement of field level help
282      */
283     protected static boolean isMaintenanceFieldLevelHelpDisabled(Maintainable m, MaintainableFieldDefinition fieldDefinition) {
284         if ( fieldDefinition != null ) {
285             if ( fieldDefinition.isShowFieldLevelHelp() != null && !fieldDefinition.isShowFieldLevelHelp() ) {
286                 return true;
287             }
288         }
289         return false;
290     }
291 
292     /**
293      * This method creates a Field for display on a Maintenance Document.
294      *
295      * @param id The DD definition for the Field (can be a Collection).
296      * @param sd The DD definition for the Section in which the field will be displayed.
297      * @param o The BusinessObject will be populated from this BO.
298      * @param m
299      * @param s The Section in which the Field will be displayed.
300      * @param autoFillDefaultValues Should default values be filled in?
301      * @param autoFillBlankRequiredValues Should values be filled in for fields that are required but which were left blank when submitting the form from the UI?
302      * @param displayedFieldNames What fields are being displayed on the form in the UI?
303      *
304      * @return
305      *
306      * @throws InstantiationException
307      * @throws IllegalAccessException
308      */
309     public static final Field toField(MaintainableItemDefinition id, MaintainableSectionDefinition sd, BusinessObject o, Maintainable m, Section s, List<String> displayedFieldNames, Set<String> conditionallyRequiredMaintenanceFields) throws InstantiationException, IllegalAccessException {
310         Field field = new Field();
311 
312         // if FieldDefiniton, simply add a Field UI object
313         if (id instanceof MaintainableFieldDefinition) {
314             MaintainableFieldDefinition maintainableFieldDefinition = (MaintainableFieldDefinition) id;
315             field = FieldUtils.getPropertyField(o.getClass(), maintainableFieldDefinition.getName(), false);
316 
317 			boolean translateCodes = getMaintenanceDocumentDictionaryService().translateCodes(o.getClass());
318 			if (translateCodes) {
319 				FieldUtils.setAdditionalDisplayPropertyForCodes(o.getClass(), field.getPropertyName(), field);
320 			}
321 
322             setupField(field, maintainableFieldDefinition, conditionallyRequiredMaintenanceFields);
323 
324             MaintenanceUtils.setFieldQuickfinder(o, field.getPropertyName(), maintainableFieldDefinition, field, displayedFieldNames, m);
325             MaintenanceUtils.setFieldDirectInquiry(o, field.getPropertyName(), maintainableFieldDefinition, field, displayedFieldNames);
326 
327             // set default value
328             //TODO St. Ailish says review this. A question was raised on 11-16-2006 Tuscon meeting as to why this is done here and not in the formatter.
329             /*if (autoFillDefaultValues) {
330                 Object defaultValue = maintainableFieldDefinition.getDefaultValue();
331                 if (defaultValue != null) {
332                     if (defaultValue.toString().equals("true")) {
333                         defaultValue = "Yes";
334                     }
335                     else if (defaultValue.toString().equals("false")) {
336                         defaultValue = "No";
337                     }
338                     field.setPropertyValue(defaultValue);
339                 }
340 
341                 Class defaultValueFinderClass = maintainableFieldDefinition.getDefaultValueFinderClass();
342                 if (defaultValueFinderClass != null) {
343                     field.setPropertyValue(((ValueFinder) defaultValueFinderClass.newInstance()).getValue());
344                 }
345             }
346 
347             // if this flag is set, and the current field is required, and readonly, and blank, use the
348             // defaultValueFinder if one exists
349             if (autoFillBlankRequiredValues) {
350                 if ( maintainableFieldDefinition.isRequired() && maintainableFieldDefinition.isUnconditionallyReadOnly() ) {
351                     if ( StringUtils.isBlank( field.getPropertyValue() ) ) {
352                         Class defaultValueFinderClass = maintainableFieldDefinition.getDefaultValueFinderClass();
353                         if (defaultValueFinderClass != null) {
354                             field.setPropertyValue(((ValueFinder) defaultValueFinderClass.newInstance()).getValue());
355                         }
356                     }
357                 }
358             }
359 			*/
360             field.setFieldLevelHelpEnabled(isMaintenanceFieldLevelHelpEnabled(m, maintainableFieldDefinition));
361             field.setFieldLevelHelpDisabled(isMaintenanceFieldLevelHelpDisabled(m, maintainableFieldDefinition));
362             field.setFieldLevelHelpUrl(maintainableFieldDefinition.getFieldLevelHelpUrl());
363         }
364 
365         return field;
366 
367     }
368 
369     /**
370      * This method will return a new form for adding in a BO for a collection.
371      * This should be customized in a subclass so the default behavior is to return nothing.
372      *
373      * @param collectionDefinition The DD definition for the Collection.
374      * @param o The BusinessObject form which the new Fields will be populated.
375      * @param document MaintenanceDocument instance which we ar building fields for
376      * @param m
377      * @param displayedFieldNames What Fields are being displayed on the form in the UI?
378      * @param containerRowErrorKey The error key for the Container/Collection used for displaying error messages.
379      * @param parents
380      * @param hideAdd Should the add line be hidden when displaying this Collection/Container in the UI?
381      * @param numberOfColumns How many columns the Fields in the Collection will be split into when displaying them in the UI.
382      *
383      * @return The List of new Fields.
384      */
385     public static final List<Field> getNewFormFields(CollectionDefinitionI collectionDefinition, BusinessObject o, Maintainable m, List<String> displayedFieldNames, Set<String> conditionallyRequiredMaintenanceFields, StringBuffer containerRowErrorKey, String parents, boolean hideAdd, int numberOfColumns) {
386         LOG.debug( "getNewFormFields" );
387         String collName = collectionDefinition.getName();
388 
389         List<Field> collFields = new ArrayList<Field>();
390         Collection<? extends FieldDefinitionI> collectionFields;
391         //Class boClass = collectionDefinition.getBusinessObjectClass();
392         BusinessObject collBO = null;
393         try {
394             collectionFields = collectionDefinition.getFields();
395             collBO = m.getNewCollectionLine(parents + collName);
396 
397             if ( LOG.isDebugEnabled() ) {
398                 LOG.debug( "newBO for add line: " + collBO );
399             }
400 
401             for ( FieldDefinitionI fieldDefinition : collectionFields  ) {
402                 // construct Field UI object from definition
403                 Field collField = FieldUtils.getPropertyField(collectionDefinition.getBusinessObjectClass(), fieldDefinition.getName(), false);
404 
405                 if (fieldDefinition instanceof MaintainableFieldDefinition) {
406                     setupField(collField, (MaintainableFieldDefinition)fieldDefinition, conditionallyRequiredMaintenanceFields);
407                 }
408                 //generate the error key for the add row
409                 String[] nameParts = StringUtils.split(collField.getPropertyName(), ".");
410                 String fieldErrorKey = KNSConstants.MAINTENANCE_NEW_MAINTAINABLE + KNSConstants.ADD_PREFIX + ".";
411                 fieldErrorKey += collName + ".";
412                 for (int i = 0; i < nameParts.length; i++) {
413                     fieldErrorKey += nameParts[i];
414                     containerRowErrorKey.append(fieldErrorKey);
415                     if (i < nameParts.length) {
416                         fieldErrorKey += ".";
417                         containerRowErrorKey.append(",");
418                     }
419                 }
420 
421                 //  set the QuickFinderClass
422                 BusinessObject collectionBoInstance = (BusinessObject) collectionDefinition.getBusinessObjectClass().newInstance();
423                 FieldUtils.setInquiryURL(collField, collectionBoInstance, fieldDefinition.getName());
424                 if (collectionDefinition instanceof MaintainableCollectionDefinition) {
425                     MaintenanceUtils.setFieldQuickfinder(collectionBoInstance, parents+collectionDefinition.getName(), true, 0, fieldDefinition.getName(), collField, displayedFieldNames, m, (MaintainableFieldDefinition) fieldDefinition);
426                     MaintenanceUtils.setFieldDirectInquiry(collectionBoInstance, parents+collectionDefinition.getName(), true, 0, fieldDefinition.getName(), collField, displayedFieldNames, m, (MaintainableFieldDefinition) fieldDefinition);
427                 }
428                 else {
429                     LookupUtils.setFieldQuickfinder(collectionBoInstance, parents+collectionDefinition.getName(), true, 0, fieldDefinition.getName(), collField, displayedFieldNames, m);
430                     LookupUtils.setFieldDirectInquiry(collectionBoInstance, fieldDefinition.getName(), collField);
431                 }
432 
433                 collFields.add(collField);
434             }
435 
436         } catch (InstantiationException e) {
437             LOG.error("Unable to create instance of object class" + e.getMessage());
438             throw new RuntimeException("Unable to create instance of object class" + e.getMessage());
439         } catch (IllegalAccessException e) {
440             LOG.error("Unable to create instance of object class" + e.getMessage());
441             throw new RuntimeException("Unable to create instance of object class" + e.getMessage());
442         }
443 
444         // populate field values from business object
445         collFields = FieldUtils.populateFieldsFromBusinessObject(collFields,collBO);
446 
447         // need to append the prefix afterwards since the population command (above)
448         // does not handle the prefixes on the property names
449         for ( Field field : collFields ) {
450             // prefix name for add line
451             field.setPropertyName(KNSConstants.MAINTENANCE_ADD_PREFIX + parents + collectionDefinition.getName() + "." + field.getPropertyName());
452         }
453         LOG.debug("Error Key for section " + collectionDefinition.getName() + " : " + containerRowErrorKey.toString());
454 
455         
456         collFields = constructContainerField(collectionDefinition, parents, o, hideAdd, numberOfColumns, collName, collFields);
457 
458         return collFields;
459     }
460 
461     /**
462      * 
463      * This method handles setting up a container field not including the add fields
464      * 
465      * @param collectionDefinition
466      * @param parents
467      * @param o
468      * @param hideAdd
469      * @param numberOfColumns
470      * @param collName
471      * @param collFields
472      * @return
473      */
474     public static List<Field> constructContainerField(CollectionDefinitionI collectionDefinition, String parents, BusinessObject o, boolean hideAdd, int numberOfColumns, String collName, List<Field> collFields) {
475         // get label for collection
476         String collectionLabel = getDataDictionaryService().getCollectionLabel(o.getClass(), collectionDefinition.getName());
477 
478         // retrieve the summary label either from the override or from the DD
479         String collectionElementLabel = collectionDefinition.getSummaryTitle();
480         if(StringUtils.isEmpty(collectionElementLabel)){
481             collectionElementLabel = getDataDictionaryService().getCollectionElementLabel(o.getClass().getName(), collectionDefinition.getName(),collectionDefinition.getBusinessObjectClass());
482         }
483 
484         // container field
485         Field containerField;
486         containerField = FieldUtils.constructContainerField(collName, collectionLabel, collFields, numberOfColumns);
487         if(StringUtils.isNotEmpty(collectionElementLabel)) {
488             containerField.setContainerElementName(collectionElementLabel);
489         }
490         collFields = new ArrayList();
491         collFields.add(containerField);
492 
493         // field button for adding lines
494         if(!hideAdd  && collectionDefinition.getIncludeAddLine()) {
495             Field field = new Field();
496 
497             String addButtonName = KNSConstants.DISPATCH_REQUEST_PARAMETER + "." + KNSConstants.ADD_LINE_METHOD + "." + parents + collectionDefinition.getName() + "." + KNSConstants.METHOD_TO_CALL_BOPARM_LEFT_DEL + collectionDefinition.getBusinessObjectClass().getName() + KNSConstants.METHOD_TO_CALL_BOPARM_RIGHT_DEL;
498             field.setPropertyName(addButtonName);
499             field.setFieldType(Field.IMAGE_SUBMIT);
500             field.setPropertyValue("images/tinybutton-add1.gif");
501             // collFields.add(field);
502             containerField.getContainerRows().add(new Row(field));
503         }
504 
505         if (collectionDefinition instanceof MaintainableCollectionDefinition) {
506             if (FieldUtils.isCollectionMultipleLookupEnabled((MaintainableCollectionDefinition) collectionDefinition)) {
507                 FieldUtils.modifyFieldToSupportMultipleValueLookups(containerField, parents, (MaintainableCollectionDefinition) collectionDefinition);
508             }
509         }
510 
511         return collFields;
512     }
513 
514     /**
515      * Call getNewFormFields with no parents.
516      *
517      * @see #getNewFormFields(CollectionDefinitionI, BusinessObject, Maintainable, List, StringBuffer, String, boolean, int)
518      */
519     public static final List<Field> getNewFormFields(MaintainableCollectionDefinition collectionDefinition, BusinessObject o, Maintainable m, List<String> displayedFieldNames, Set<String> conditionallyRequiredMaintenanceFields, StringBuffer containerRowErrorKey, int numberOfColumns) {
520         String parent = "";
521         return getNewFormFields(collectionDefinition, o, m, displayedFieldNames, conditionallyRequiredMaintenanceFields, containerRowErrorKey, parent, false, numberOfColumns);
522     }
523 
524     /**
525      * Create a Field for display on an Inquiry screen.
526      *
527      * @param d The DD definition for the Field.
528      * @param o The BusinessObject from which the Field will be populated.
529      * @param s The Section in which the Field will be displayed.
530      *
531      * @return The populated Field.
532      */
533     public static Field toField(FieldDefinition d, BusinessObject o, Section s) {
534         Field field = FieldUtils.getPropertyField(o.getClass(), d.getAttributeName(), false);
535         
536         FieldUtils.setInquiryURL(field, o, field.getPropertyName());
537 
538 		String alternateDisplayPropertyName = getBusinessObjectDictionaryService()
539 				.getInquiryFieldAlternateDisplayAttributeName(o.getClass(), d.getAttributeName());
540 		if (StringUtils.isNotBlank(alternateDisplayPropertyName)) {
541 			field.setAlternateDisplayPropertyName(alternateDisplayPropertyName);
542 		}
543 
544 		String additionalDisplayPropertyName = getBusinessObjectDictionaryService()
545 				.getInquiryFieldAdditionalDisplayAttributeName(o.getClass(), d.getAttributeName());
546 		if (StringUtils.isNotBlank(additionalDisplayPropertyName)) {
547 			field.setAdditionalDisplayPropertyName(additionalDisplayPropertyName);
548 		}
549 		else {
550 			boolean translateCodes = getBusinessObjectDictionaryService().tranlateCodesInInquiry(o.getClass());
551 			if (translateCodes) {
552 				FieldUtils.setAdditionalDisplayPropertyForCodes(o.getClass(), d.getAttributeName(), field);
553 			}
554 		}
555 
556         populateFieldFromBusinessObject(field, o);
557 
558         return field;
559     }
560 
561 	public static DataDictionaryService getDataDictionaryService() {
562     	if (dataDictionaryService == null) {
563     		dataDictionaryService = KNSServiceLocator.getDataDictionaryService();
564     	}
565 		return dataDictionaryService;
566 	}
567 
568 	public static PersistenceStructureService getPersistenceStructureService() {
569     	if (persistenceStructureService == null) {
570     		persistenceStructureService = KNSServiceLocator.getPersistenceStructureService();
571     	}
572 		return persistenceStructureService;
573 	}
574 
575 	public static BusinessObjectDictionaryService getBusinessObjectDictionaryService() {
576     	if (businessObjectDictionaryService == null) {
577     		businessObjectDictionaryService = KNSServiceLocator.getBusinessObjectDictionaryService();
578     	}
579 		return businessObjectDictionaryService; 
580 	}
581 	
582 	public static MaintenanceDocumentDictionaryService getMaintenanceDocumentDictionaryService() {
583     	if (maintenanceDocumentDictionaryService == null) {
584     		maintenanceDocumentDictionaryService = KNSServiceLocator.getMaintenanceDocumentDictionaryService();
585     	}
586 		return maintenanceDocumentDictionaryService; 
587 	}
588 
589 }
590