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