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