001/** 002 * Copyright 2005-2015 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 */ 016package org.kuali.rice.kns.maintenance; 017 018import java.beans.PropertyDescriptor; 019import java.lang.reflect.InvocationTargetException; 020import java.security.GeneralSecurityException; 021import java.security.cert.LDAPCertStoreParameters; 022import java.util.ArrayList; 023import java.util.Collection; 024import java.util.HashMap; 025import java.util.HashSet; 026import java.util.Iterator; 027import java.util.List; 028import java.util.Map; 029import java.util.Set; 030 031import org.apache.commons.beanutils.PropertyUtils; 032import org.apache.commons.lang.StringUtils; 033import org.kuali.rice.core.api.CoreApiServiceLocator; 034import org.kuali.rice.core.api.encryption.EncryptionService; 035import org.kuali.rice.core.web.format.FormatException; 036import org.kuali.rice.kim.api.identity.PersonService; 037import org.kuali.rice.kim.api.services.KimApiServiceLocator; 038import org.kuali.rice.kns.datadictionary.MaintainableCollectionDefinition; 039import org.kuali.rice.kns.datadictionary.MaintainableFieldDefinition; 040import org.kuali.rice.kns.datadictionary.MaintainableItemDefinition; 041import org.kuali.rice.kns.datadictionary.MaintainableSectionDefinition; 042import org.kuali.rice.kns.document.MaintenanceDocument; 043import org.kuali.rice.kns.document.authorization.FieldRestriction; 044import org.kuali.rice.kns.document.authorization.MaintenanceDocumentPresentationController; 045import org.kuali.rice.kns.document.authorization.MaintenanceDocumentRestrictions; 046import org.kuali.rice.kns.lookup.LookupUtils; 047import org.kuali.rice.kns.service.BusinessObjectAuthorizationService; 048import org.kuali.rice.kns.service.BusinessObjectDictionaryService; 049import org.kuali.rice.kns.service.BusinessObjectMetaDataService; 050import org.kuali.rice.kns.service.DocumentHelperService; 051import org.kuali.rice.kns.service.KNSServiceLocator; 052import org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService; 053import org.kuali.rice.kns.util.FieldUtils; 054import org.kuali.rice.kns.util.InactiveRecordsHidingUtils; 055import org.kuali.rice.kns.util.MaintenanceUtils; 056import org.kuali.rice.kns.web.ui.Section; 057import org.kuali.rice.kns.web.ui.SectionBridge; 058import org.kuali.rice.krad.bo.BusinessObject; 059import org.kuali.rice.krad.bo.DataObjectRelationship; 060import org.kuali.rice.krad.bo.PersistableBusinessObject; 061import org.kuali.rice.krad.data.KradDataServiceLocator; 062import org.kuali.rice.krad.data.MaterializeOption; 063import org.kuali.rice.krad.datadictionary.AttributeSecurity; 064import org.kuali.rice.krad.datadictionary.exception.UnknownBusinessClassAttributeException; 065import org.kuali.rice.krad.maintenance.MaintainableImpl; 066import org.kuali.rice.krad.service.DataDictionaryService; 067import org.kuali.rice.krad.service.DataObjectMetaDataService; 068import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 069import org.kuali.rice.krad.service.ModuleService; 070import org.kuali.rice.krad.service.PersistenceStructureService; 071import org.kuali.rice.krad.util.GlobalVariables; 072import org.kuali.rice.krad.util.KRADConstants; 073import org.kuali.rice.krad.util.KRADPropertyConstants; 074import org.kuali.rice.krad.util.MessageMap; 075import org.kuali.rice.krad.util.ObjectUtils; 076import org.kuali.rice.krad.valuefinder.ValueFinder; 077 078import com.sun.mail.imap.protocol.MODSEQ; 079 080/** 081 * Base Maintainable class to hold things common to all maintainables. 082 * 083 * @deprecated Use {@link org.kuali.rice.krad.maintenance.MaintainableImpl}. 084 */ 085@Deprecated 086public class KualiMaintainableImpl extends MaintainableImpl implements Maintainable { 087 private static final long serialVersionUID = 4814145799502207182L; 088 089 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(KualiMaintainableImpl.class); 090 091 protected PersistableBusinessObject businessObject; 092 093 protected Map<String, PersistableBusinessObject> newCollectionLines = new HashMap<String, PersistableBusinessObject>(); 094 protected Map<String, Boolean> inactiveRecordDisplay = new HashMap<String, Boolean>(); 095 096 // TODO: rename once 'newCollectionLines' is removed 097 protected Set<String> newCollectionLineNames = new HashSet<String>(); 098 099 protected transient BusinessObjectDictionaryService businessObjectDictionaryService; 100 protected transient PersonService personService; 101 @Deprecated 102 protected transient BusinessObjectMetaDataService businessObjectMetaDataService; 103 @Deprecated 104 protected transient BusinessObjectAuthorizationService businessObjectAuthorizationService; 105 @Deprecated 106 protected transient DocumentHelperService documentHelperService; 107 protected transient MaintenanceDocumentDictionaryService maintenanceDocumentDictionaryService; 108 @Deprecated 109 private transient DataObjectMetaDataService dataObjectMetaDataService; 110 111 /** 112 * Default empty constructor 113 */ 114 public KualiMaintainableImpl() { 115 super(); 116 } 117 118 /** 119 * Constructor which initializes the business object to be maintained. 120 * 121 * @param businessObject 122 */ 123 public KualiMaintainableImpl(PersistableBusinessObject businessObject) { 124 super(); 125 this.businessObject = businessObject; 126 super.setDataObject(businessObject); 127 } 128 129 /** 130 * @see Maintainable#populateBusinessObject(java.util.Map, 131 * org.kuali.rice.krad.maintenance.MaintenanceDocument, String) 132 */ 133 @Override 134 @SuppressWarnings("unchecked") 135 public Map populateBusinessObject(Map<String, String> fieldValues, MaintenanceDocument maintenanceDocument, 136 String methodToCall) { 137 fieldValues = decryptEncryptedData(fieldValues, maintenanceDocument, methodToCall); 138 Map newFieldValues = null; 139 newFieldValues = getPersonService().resolvePrincipalNamesToPrincipalIds(getBusinessObject(), fieldValues); 140 141 Map cachedValues = FieldUtils.populateBusinessObjectFromMap(getBusinessObject(), newFieldValues); 142 performForceUpperCase(newFieldValues); 143 144 return cachedValues; 145 } 146 147 /** 148 * Special hidden parameters are set on the maintenance jsp starting with a 149 * prefix that tells us which fields have been encrypted. This field finds 150 * the those parameters in the map, whose value gives us the property name 151 * that has an encrypted value. We then need to decrypt the value in the Map 152 * before the business object is populated. 153 * 154 * @param fieldValues 155 * - possibly with encrypted values 156 * @return Map fieldValues - with no encrypted values 157 */ 158 protected Map<String, String> decryptEncryptedData(Map<String, String> fieldValues, 159 MaintenanceDocument maintenanceDocument, String methodToCall) { 160 try { 161 MaintenanceDocumentRestrictions auths = KNSServiceLocator.getBusinessObjectAuthorizationService() 162 .getMaintenanceDocumentRestrictions(maintenanceDocument, 163 GlobalVariables.getUserSession().getPerson()); 164 for (Iterator<String> iter = fieldValues.keySet().iterator(); iter.hasNext();) { 165 String fieldName = iter.next(); 166 String fieldValue = (String) fieldValues.get(fieldName); 167 168 if (fieldValue != null && !"".equals(fieldValue) 169 && fieldValue.endsWith(EncryptionService.ENCRYPTION_POST_PREFIX)) { 170 if (shouldFieldBeEncrypted(maintenanceDocument, fieldName, auths, methodToCall)) { 171 String encryptedValue = fieldValue; 172 173 // take off the postfix 174 encryptedValue = StringUtils.stripEnd(encryptedValue, EncryptionService.ENCRYPTION_POST_PREFIX); 175 if(CoreApiServiceLocator.getEncryptionService().isEnabled()) { 176 String decryptedValue = getEncryptionService().decrypt(encryptedValue); 177 178 fieldValues.put(fieldName, decryptedValue); 179 } 180 } 181 else 182 throw new RuntimeException("The field value for field name " + fieldName 183 + " should not be encrypted."); 184 } 185 else if (fieldValue != null && !"".equals(fieldValue) 186 && auths.hasRestriction(fieldName) 187 && shouldFieldBeEncrypted(maintenanceDocument, fieldName, auths, methodToCall)) 188 throw new RuntimeException("The field value for field name " + fieldName + " should be encrypted."); 189 } 190 } 191 catch (GeneralSecurityException e) { 192 throw new RuntimeException("Unable to decrypt secure data: " + e.getMessage()); 193 } 194 195 return fieldValues; 196 } 197 198 /** 199 * Determines whether the field in a request should be encrypted. This base 200 * implementation does not work for properties of collection elements. 201 * 202 * This base implementation will only return true if the maintenance 203 * document is being refreshed after a lookup (i.e. methodToCall is 204 * "refresh") and the data dictionary-based attribute security definition 205 * has any restriction defined, whether the user would be authorized to view 206 * the field. This assumes that only fields returned from a lookup should be 207 * encrypted in a request. If the user otherwise has no permissions to 208 * view/edit the field, then a request parameter will not be sent back to 209 * the server for population. 210 * 211 * @param maintenanceDocument 212 * @param fieldName 213 * @param auths 214 * @param methodToCall 215 * @return 216 */ 217 protected boolean shouldFieldBeEncrypted(MaintenanceDocument maintenanceDocument, String fieldName, 218 MaintenanceDocumentRestrictions auths, String methodToCall) { 219 if ("refresh".equals(methodToCall) && fieldName != null) { 220 fieldName = fieldName.replaceAll("\\[[0-9]*+\\]", ""); 221 fieldName = fieldName.replaceAll("^add\\.", ""); 222 Map<String, AttributeSecurity> fieldNameToAttributeSecurityMap = MaintenanceUtils 223 .retrievePropertyPathToAttributeSecurityMappings(getDocumentTypeName()); 224 AttributeSecurity attributeSecurity = fieldNameToAttributeSecurityMap.get(fieldName); 225 return attributeSecurity != null && attributeSecurity.hasRestrictionThatRemovesValueFromUI(); 226 } 227 else { 228 return false; 229 } 230 } 231 232 /** 233 * Calls method to get all the core sections for the business object defined 234 * in the data dictionary. Then determines if the bo has custom attributes, 235 * if so builds a custom attribute section and adds to the section list. 236 * 237 * @return List of org.kuali.ui.Section objects 238 */ 239 @Override 240 public List getSections(MaintenanceDocument document, Maintainable oldMaintainable) { 241 List<Section> sections = new ArrayList<Section>(); 242 sections.addAll(getCoreSections(document, oldMaintainable)); 243 244 return sections; 245 } 246 247 /** 248 * Gets list of maintenance sections built from the data dictionary. If the 249 * section contains maintenance fields, construct Row/Field UI objects and 250 * place under Section UI. If section contains a maintenance collection, 251 * call method to build a Section UI which contains rows of Container 252 * Fields. 253 * 254 * @return List of org.kuali.ui.Section objects 255 */ 256 public List<Section> getCoreSections(MaintenanceDocument document, Maintainable oldMaintainable) { 257 List<Section> sections = new ArrayList<Section>(); 258 MaintenanceDocumentRestrictions maintenanceRestrictions = KNSServiceLocator 259 .getBusinessObjectAuthorizationService().getMaintenanceDocumentRestrictions(document, 260 GlobalVariables.getUserSession().getPerson()); 261 262 MaintenanceDocumentPresentationController maintenanceDocumentPresentationController = (MaintenanceDocumentPresentationController) getDocumentHelperService() 263 .getDocumentPresentationController(document); 264 Set<String> conditionallyRequiredFields = maintenanceDocumentPresentationController 265 .getConditionallyRequiredPropertyNames(document); 266 267 List<MaintainableSectionDefinition> sectionDefinitions = getMaintenanceDocumentDictionaryService() 268 .getMaintainableSections(getDocumentTypeName()); 269 try { 270 // iterate through section definitions and create Section UI object 271 for (Iterator iter = sectionDefinitions.iterator(); iter.hasNext();) { 272 MaintainableSectionDefinition maintSectionDef = (MaintainableSectionDefinition) iter.next(); 273 274 List<String> displayedFieldNames = new ArrayList<String>(); 275 if (!maintenanceRestrictions.isHiddenSectionId(maintSectionDef.getId())) { 276 277 for (Iterator iter2 = maintSectionDef.getMaintainableItems().iterator(); iter2.hasNext();) { 278 MaintainableItemDefinition item = (MaintainableItemDefinition) iter2.next(); 279 if (item instanceof MaintainableFieldDefinition) { 280 displayedFieldNames.add(((MaintainableFieldDefinition) item).getName()); 281 } 282 } 283 284 Section section = SectionBridge 285 .toSection(maintSectionDef, getBusinessObject(), this, oldMaintainable, 286 getMaintenanceAction(), displayedFieldNames, conditionallyRequiredFields); 287 if (maintenanceRestrictions.isReadOnlySectionId(maintSectionDef.getId())) { 288 section.setReadOnly(true); 289 } 290 291 // add to section list 292 sections.add(section); 293 } 294 295 } 296 297 } 298 catch (InstantiationException e) { 299 LOG.error("Unable to create instance of object class" + e.getMessage()); 300 throw new RuntimeException("Unable to create instance of object class" + e.getMessage()); 301 } 302 catch (IllegalAccessException e) { 303 LOG.error("Unable to create instance of object class" + e.getMessage()); 304 throw new RuntimeException("Unable to create instance of object class" + e.getMessage()); 305 } 306 307 return sections; 308 } 309 310 311 /** 312 * 313 * @see Maintainable#saveBusinessObject() 314 */ 315 @Override 316 public void saveBusinessObject() { 317 KNSServiceLocator.getBusinessObjectService().linkAndSave(businessObject); 318 } 319 320 /** 321 * delegate this call to KNS' {@link org.kuali.rice.kns.maintenance.Maintainable#saveBusinessObject()} in order 322 * to support KNS maintainables. 323 */ 324 @Override 325 public void saveDataObject() { 326 saveBusinessObject(); 327 } 328 329 /** 330 * Retrieves title for maintenance document from data dictionary 331 */ 332 @Override 333 public String getMaintainableTitle() { 334 return getMaintenanceDocumentDictionaryService().getMaintenanceLabel(getDocumentTypeName()); 335 } 336 337 @Override 338 public void setupNewFromExisting(MaintenanceDocument document, Map<String, String[]> parameters) { 339 } 340 341 @Override 342 public boolean isBoNotesEnabled() { 343 return KRADServiceLocatorWeb.getLegacyDataAdapter().areNotesSupported(getDataObjectClass()); 344 } 345 346 /** 347 * Overriding to call old (KNS) name of the method 348 */ 349 @Override 350 public boolean isNotesEnabled() { 351 return isBoNotesEnabled(); 352 } 353 354 /** 355 * @see Maintainable#refresh(java.lang.String, 356 * java.util.Map) Impls will be needed if custom action is needed on 357 * refresh. 358 */ 359 @Override 360 public void refresh(String refreshCaller, Map fieldValues, MaintenanceDocument document) { 361 String referencesToRefresh = (String) fieldValues.get(KRADConstants.REFERENCES_TO_REFRESH); 362 refreshReferences(referencesToRefresh); 363 } 364 365 @Override 366 public void refreshReferences(String referencesToRefresh) { 367 PersistenceStructureService persistenceStructureService = getPersistenceStructureService(); 368 if (StringUtils.isNotBlank(referencesToRefresh)) { 369 String[] references = StringUtils.split(referencesToRefresh, KRADConstants.REFERENCES_TO_REFRESH_SEPARATOR); 370 for (String reference : references) { 371 if (StringUtils.isNotBlank(reference)) { 372 if (reference.startsWith(KRADConstants.ADD_PREFIX + ".")) { 373 // add one for the period 374 reference = reference.substring(KRADConstants.ADD_PREFIX.length() + 1); 375 376 String boToRefreshName = StringUtils.substringBeforeLast(reference, "."); 377 String propertyToRefresh = StringUtils.substringAfterLast(reference, "."); 378 if (StringUtils.isNotBlank(propertyToRefresh)) { 379 PersistableBusinessObject addlineBO = getNewCollectionLine(boToRefreshName); 380 Class addlineBOClass = addlineBO.getClass(); 381 if (LOG.isDebugEnabled()) { 382 LOG.debug("Refresh this \"new\"/add object for the collections: " + reference); 383 } 384 if (persistenceStructureService.hasReference(addlineBOClass, propertyToRefresh) 385 || persistenceStructureService.hasCollection(addlineBOClass, propertyToRefresh)) { 386 addlineBO.refreshReferenceObject(propertyToRefresh); 387 } 388 else { 389 if (getDataDictionaryService().hasRelationship(addlineBOClass.getName(), 390 propertyToRefresh)) { 391 // a DD mapping, try to go straight to the 392 // object and refresh it there 393 Object possibleBO = ObjectUtils.getPropertyValue(addlineBO, propertyToRefresh); 394 if (possibleBO != null && possibleBO instanceof PersistableBusinessObject) { 395 ((PersistableBusinessObject) possibleBO).refresh(); 396 } 397 } 398 } 399 } 400 else { 401 LOG.error("Error: unable to refresh this \"new\"/add object for the collections: " 402 + reference); 403 } 404 } 405 else if (ObjectUtils.isNestedAttribute(reference)) { 406 Object nestedObject = ObjectUtils.getNestedValue(getBusinessObject(), 407 ObjectUtils.getNestedAttributePrefix(reference)); 408 if (nestedObject == null) { 409 LOG.warn("Unable to refresh ReferenceToRefresh (" + reference + ") was found to be null"); 410 } 411 else { 412 if (nestedObject instanceof Collection) { 413 // do nothing, probably because it's not really a 414 // collection reference but a relationship defined 415 // in the DD for a collections lookup 416 // this part will need to be rewritten when the DD 417 // supports true collection references 418 } 419 else if (nestedObject instanceof PersistableBusinessObject) { 420 String propertyToRefresh = ObjectUtils.getNestedAttributePrimitive(reference); 421 if (persistenceStructureService.hasReference(nestedObject.getClass(), propertyToRefresh) 422 || persistenceStructureService.hasCollection(nestedObject.getClass(), 423 propertyToRefresh)) { 424 if (LOG.isDebugEnabled()) { 425 LOG.debug("Refeshing " + ObjectUtils.getNestedAttributePrefix(reference) + " " 426 + ObjectUtils.getNestedAttributePrimitive(reference)); 427 } 428 ((PersistableBusinessObject) nestedObject).refreshReferenceObject(propertyToRefresh); 429 } 430 else { 431 // a DD mapping, try to go straight to the 432 // object and refresh it there 433 Object possibleBO = ObjectUtils.getPropertyValue(nestedObject, propertyToRefresh); 434 if (possibleBO != null && possibleBO instanceof PersistableBusinessObject) { 435 if (getDataDictionaryService().hasRelationship(possibleBO.getClass().getName(), 436 propertyToRefresh)) { 437 ((PersistableBusinessObject) possibleBO).refresh(); 438 } 439 } 440 } 441 } 442 else { 443 LOG.warn("Expected that a referenceToRefresh (" 444 + reference 445 + ") would be a PersistableBusinessObject or Collection, but instead, it was of class " 446 + nestedObject.getClass().getName()); 447 } 448 } 449 } 450 else { 451 if (LOG.isDebugEnabled()) { 452 LOG.debug("Refreshing " + reference); 453 } 454 if (persistenceStructureService.hasReference(getDataObjectClass(), reference) 455 || persistenceStructureService.hasCollection(getDataObjectClass(), reference)) { 456 getBusinessObject().refreshReferenceObject(reference); 457 } 458 else { 459 if (getDataDictionaryService().hasRelationship(getBusinessObject().getClass().getName(), 460 reference)) { 461 // a DD mapping, try to go straight to the 462 // object and refresh it there 463 Object possibleRelationship = ObjectUtils.getPropertyValue(getBusinessObject(), 464 reference); 465 if (possibleRelationship != null) { 466 if (possibleRelationship instanceof PersistableBusinessObject) { 467 ((PersistableBusinessObject) possibleRelationship).refresh(); 468 } 469 else if (possibleRelationship instanceof Collection) { 470 // do nothing, probably because it's not 471 // really a collection reference but a 472 // relationship defined in the DD for a 473 // collections lookup 474 // this part will need to be rewritten 475 // when the DD supports true collection 476 // references 477 } 478 else { 479 LOG.warn("Expected that a referenceToRefresh (" 480 + reference 481 + ") would be a PersistableBusinessObject or Collection, but instead, it was of class " 482 + possibleRelationship.getClass().getName()); 483 } 484 } 485 } 486 } 487 } 488 } 489 } 490 } 491 } 492 493 @Override 494 public void addMultipleValueLookupResults(MaintenanceDocument document, String collectionName, 495 Collection<PersistableBusinessObject> rawValues, boolean needsBlank, PersistableBusinessObject bo) { 496 Collection maintCollection = (Collection) ObjectUtils.getPropertyValue(bo, collectionName); 497 String docTypeName = document.getDocumentHeader().getWorkflowDocument().getDocumentTypeName(); 498 499 List<String> duplicateIdentifierFieldsFromDataDictionary = getDuplicateIdentifierFieldsFromDataDictionary( 500 docTypeName, collectionName); 501 502 List<String> existingIdentifierList = getMultiValueIdentifierList(maintCollection, 503 duplicateIdentifierFieldsFromDataDictionary); 504 505 Class collectionClass = getMaintenanceDocumentDictionaryService().getCollectionBusinessObjectClass(docTypeName, 506 collectionName); 507 508 List<MaintainableSectionDefinition> sections = getMaintenanceDocumentDictionaryService() 509 .getMaintainableSections(docTypeName); 510 Map<String, String> template = MaintenanceUtils.generateMultipleValueLookupBOTemplate(sections, collectionName); 511 try { 512 for (PersistableBusinessObject nextBo : rawValues) { 513 PersistableBusinessObject templatedBo; 514 if (needsBlank) { 515 templatedBo = (PersistableBusinessObject) collectionClass.newInstance(); 516 } 517 else { 518 // templatedBo = (PersistableBusinessObject) 519 // ObjectUtils.createHybridBusinessObject(collectionClass, 520 // nextBo, template); 521 try { 522 ModuleService moduleService = KRADServiceLocatorWeb.getKualiModuleService() 523 .getResponsibleModuleService(collectionClass); 524 if (moduleService != null && moduleService.isExternalizable(collectionClass)) 525 templatedBo = (PersistableBusinessObject) moduleService 526 .createNewObjectFromExternalizableClass(collectionClass); 527 else 528 templatedBo = (PersistableBusinessObject) collectionClass.newInstance(); 529 } 530 catch (Exception e) { 531 throw new RuntimeException("Cannot instantiate " + collectionClass.getName(), e); 532 } 533 // first set the default values specified in the DD 534 setNewCollectionLineDefaultValues(collectionName, templatedBo); 535 // then set the values from the multiple value lookup result 536 ObjectUtils.createHybridBusinessObject(templatedBo, nextBo, template); 537 538 prepareBusinessObjectForAdditionFromMultipleValueLookup(collectionName, templatedBo); 539 } 540 templatedBo.setNewCollectionRecord(true); 541 542 if (!hasBusinessObjectExisted(templatedBo, existingIdentifierList, 543 duplicateIdentifierFieldsFromDataDictionary)) { 544 maintCollection.add(templatedBo); 545 546 } 547 } 548 } 549 catch (Exception e) { 550 LOG.error("Unable to add multiple value lookup results " + e.getMessage()); 551 throw new RuntimeException("Unable to add multiple value lookup results " + e.getMessage()); 552 } 553 } 554 555 /** 556 * This method is to retrieve a List of fields which are specified in the 557 * maintenance document data dictionary as the 558 * duplicateIdentificationFields. This List is used to determine whether the 559 * new entry being added to the collection is a duplicate entry and if so, 560 * we should not add the new entry to the existing collection 561 * 562 * @param docTypeName 563 * @param collectionName 564 */ 565 @Override 566 public List<String> getDuplicateIdentifierFieldsFromDataDictionary(String docTypeName, String collectionName) { 567 List<String> duplicateIdentifierFieldNames = new ArrayList<String>(); 568 MaintainableCollectionDefinition collDef = getMaintenanceDocumentDictionaryService().getMaintainableCollection( 569 docTypeName, collectionName); 570 Collection<MaintainableFieldDefinition> fieldDef = collDef.getDuplicateIdentificationFields(); 571 for (MaintainableFieldDefinition eachFieldDef : fieldDef) { 572 duplicateIdentifierFieldNames.add(eachFieldDef.getName()); 573 } 574 return duplicateIdentifierFieldNames; 575 } 576 577 @Override 578 public List<String> getMultiValueIdentifierList(Collection maintCollection, List<String> duplicateIdentifierFields) { 579 List<String> identifierList = new ArrayList<String>(); 580 for (PersistableBusinessObject bo : (Collection<PersistableBusinessObject>) maintCollection) { 581 String uniqueIdentifier = new String(); 582 for (String identifierField : duplicateIdentifierFields) { 583 uniqueIdentifier = uniqueIdentifier + identifierField + "-" 584 + ObjectUtils.getPropertyValue(bo, identifierField); 585 } 586 if (StringUtils.isNotEmpty(uniqueIdentifier)) { 587 identifierList.add(uniqueIdentifier); 588 } 589 } 590 return identifierList; 591 } 592 593 @Override 594 public boolean hasBusinessObjectExisted(BusinessObject bo, List<String> existingIdentifierList, 595 List<String> duplicateIdentifierFields) { 596 String uniqueIdentifier = new String(); 597 for (String identifierField : duplicateIdentifierFields) { 598 uniqueIdentifier = uniqueIdentifier + identifierField + "-" 599 + ObjectUtils.getPropertyValue(bo, identifierField); 600 } 601 if (existingIdentifierList.contains(uniqueIdentifier)) { 602 return true; 603 } 604 else { 605 return false; 606 } 607 } 608 609 public void prepareBusinessObjectForAdditionFromMultipleValueLookup(String collectionName, BusinessObject bo) { 610 // default implementation does nothing 611 } 612 613 /** 614 * Set the new collection records back to true so they can be deleted (copy 615 * should act like new) 616 * 617 * @see KualiMaintainableImpl#processAfterCopy() 618 */ 619 @Override 620 public void processAfterCopy(MaintenanceDocument document, Map<String, String[]> parameters) { 621 try { 622 // This was the only remaining use of this method, as the operation with the wrapper 623 // below is not necessarily safe to use in all situations, I am calling it here 624 // from within the document code where we know it's safe: 625 626 KradDataServiceLocator.getDataObjectService().wrap(businessObject).materializeReferencedObjectsToDepth(2 627 , MaterializeOption.COLLECTIONS, MaterializeOption.UPDATE_UPDATABLE_REFS); 628 629 KRADServiceLocatorWeb.getLegacyDataAdapter().setObjectPropertyDeep(businessObject, KRADPropertyConstants.NEW_COLLECTION_RECORD, 630 boolean.class, true); 631// ObjectUtils.setObjectPropertyDeep(businessObject, KRADPropertyConstants.NEW_COLLECTION_RECORD, 632// boolean.class, true, 2); 633 } catch (Exception e) { 634 LOG.error("unable to set newCollectionRecord property: " + e.getMessage(), e); 635 throw new RuntimeException("unable to set newCollectionRecord property: " + e.getMessage(), e); 636 } 637 } 638 639 @Override 640 public void processAfterEdit(MaintenanceDocument document, Map<String, String[]> requestParameters) { 641 642 } 643 644 @Override 645 public void processAfterNew(MaintenanceDocument document, Map<String, String[]> requestParameters) { 646 647 } 648 649 @Override 650 public void processAfterPost(MaintenanceDocument document, Map<String, String[]> requestParameters) { 651 652 } 653 654 @Override 655 public void setDataObject(Object object) { 656 super.setDataObject(object); 657 658 if(object instanceof PersistableBusinessObject) { 659 this.businessObject = (PersistableBusinessObject)object; 660 } 661 } 662 663 @Override 664 public String getDocumentTitle(MaintenanceDocument document) { 665 return super.getDocumentTitle((org.kuali.rice.krad.maintenance.MaintenanceDocument) document); 666 } 667 668 /** 669 * @return Returns the instance of the business object being maintained. 670 */ 671 @Override 672 public PersistableBusinessObject getBusinessObject() { 673 return businessObject; 674 } 675 676 /** 677 * @param businessObject 678 * Sets the instance of a business object that will be 679 * maintained. 680 */ 681 @Override 682 public void setBusinessObject(PersistableBusinessObject businessObject) { 683 this.businessObject = businessObject; 684 setDataObject(businessObject); 685 } 686 687 /** 688 * @return Returns the boClass. 689 */ 690 @Override 691 public Class getBoClass() { 692 return super.getDataObjectClass(); 693 } 694 695 /** 696 * @param boClass 697 * The boClass to set. 698 */ 699 @Override 700 public void setBoClass(Class boClass) { 701 setDataObjectClass(boClass); 702 } 703 704 /** 705 * 706 * @see Maintainable#setGenerateDefaultValues() 707 */ 708 @Override 709 public void setGenerateDefaultValues(String docTypeName) { 710 List<MaintainableSectionDefinition> sectionDefinitions = getMaintenanceDocumentDictionaryService() 711 .getMaintainableSections(docTypeName); 712 Map defaultValues = new HashMap(); 713 714 try { 715 // iterate through section definitions 716 for (Iterator iter = sectionDefinitions.iterator(); iter.hasNext();) { 717 718 MaintainableSectionDefinition maintSectionDef = (MaintainableSectionDefinition) iter.next(); 719 Collection maintItems = maintSectionDef.getMaintainableItems(); 720 for (Iterator iterator = maintItems.iterator(); iterator.hasNext();) { 721 MaintainableItemDefinition item = (MaintainableItemDefinition) iterator.next(); 722 723 if (item instanceof MaintainableFieldDefinition) { 724 MaintainableFieldDefinition maintainableFieldDefinition = (MaintainableFieldDefinition) item; 725 726 String defaultValue = maintainableFieldDefinition.getDefaultValue(); 727 if (defaultValue != null) { 728 if (defaultValue.equals("true")) { 729 defaultValue = "Yes"; 730 } 731 else if (defaultValue.equals("false")) { 732 defaultValue = "No"; 733 } 734 } 735 736 Class defaultValueFinderClass = maintainableFieldDefinition.getDefaultValueFinderClass(); 737 if (defaultValueFinderClass != null) { 738 defaultValue = ((ValueFinder) defaultValueFinderClass.newInstance()).getValue(); 739 740 } 741 if (defaultValue != null) { 742 defaultValues.put(item.getName(), defaultValue); 743 } 744 } 745 } 746 } 747 Map cachedValues = FieldUtils.populateBusinessObjectFromMap(getBusinessObject(), defaultValues); 748 } 749 catch (Exception e) { 750 LOG.error("Unable to set default value " + e.getMessage(), e); 751 throw new RuntimeException("Unable to set default value" + e.getMessage(), e); 752 } 753 754 } 755 756 /** 757 * 758 * @see Maintainable#setGenerateBlankRequiredValues() 759 */ 760 @Override 761 public void setGenerateBlankRequiredValues(String docTypeName) { 762 try { 763 List<MaintainableSectionDefinition> sectionDefinitions = getMaintenanceDocumentDictionaryService() 764 .getMaintainableSections(docTypeName); 765 Map<String, String> defaultValues = new HashMap<String, String>(); 766 767 for (MaintainableSectionDefinition maintSectionDef : sectionDefinitions) { 768 for (MaintainableItemDefinition item : maintSectionDef.getMaintainableItems()) { 769 if (item instanceof MaintainableFieldDefinition) { 770 MaintainableFieldDefinition maintainableFieldDefinition = (MaintainableFieldDefinition) item; 771 if (maintainableFieldDefinition.isRequired() 772 && maintainableFieldDefinition.isUnconditionallyReadOnly()) { 773 Object currPropVal = ObjectUtils.getPropertyValue(this.getBusinessObject(), item.getName()); 774 if (currPropVal == null 775 || (currPropVal instanceof String && StringUtils.isBlank((String) currPropVal))) { 776 Class<? extends ValueFinder> defaultValueFinderClass = maintainableFieldDefinition 777 .getDefaultValueFinderClass(); 778 if (defaultValueFinderClass != null) { 779 String defaultValue = defaultValueFinderClass.newInstance().getValue(); 780 if (defaultValue != null) { 781 defaultValues.put(item.getName(), defaultValue); 782 } 783 } 784 } 785 } 786 } 787 } 788 } 789 FieldUtils.populateBusinessObjectFromMap(getBusinessObject(), defaultValues); 790 } 791 catch (Exception e) { 792 LOG.error("Unable to set blank required value " + e.getMessage(), e); 793 throw new RuntimeException("Unable to set blank required value" + e.getMessage(), e); 794 } 795 } 796 797 @Deprecated 798 public void processAfterAddLine(String colName, Class colClass) { 799 } 800 801 /** 802 * @see Maintainable#processBeforeAddLine(java.lang.String, 803 * java.lang.Class, org.kuali.rice.krad.bo.BusinessObject) 804 */ 805 @Override 806 public void processBeforeAddLine(String colName, Class colClass, BusinessObject addBO) { 807 } 808 809 /** 810 * @see Maintainable#getShowInactiveRecords(java.lang.String) 811 */ 812 @Override 813 public boolean getShowInactiveRecords(String collectionName) { 814 return InactiveRecordsHidingUtils.getShowInactiveRecords(inactiveRecordDisplay, collectionName); 815 } 816 817 /** 818 * @see Maintainable#setShowInactiveRecords(java.lang.String, 819 * boolean) 820 */ 821 @Override 822 public void setShowInactiveRecords(String collectionName, boolean showInactive) { 823 InactiveRecordsHidingUtils.setShowInactiveRecords(inactiveRecordDisplay, collectionName, showInactive); 824 } 825 826 /** 827 * @return the inactiveRecordDisplay 828 */ 829 @Override 830 public Map<String, Boolean> getInactiveRecordDisplay() { 831 return inactiveRecordDisplay; 832 } 833 834 @Override 835 public void addNewLineToCollection(String collectionName) { 836 837 if (LOG.isDebugEnabled()) { 838 LOG.debug("addNewLineToCollection( " + collectionName + " )"); 839 } 840 // get the new line from the map 841 PersistableBusinessObject addLine = newCollectionLines.get(collectionName); 842 if (addLine != null) { 843 // mark the isNewCollectionRecord so the option to delete this line 844 // will be presented 845 addLine.setNewCollectionRecord(true); 846 847 // if we add back add button on sub collection of an "add line" we 848 // may need extra logic here 849 850 // get the collection from the business object 851 Collection maintCollection = (Collection) ObjectUtils.getPropertyValue(getBusinessObject(), collectionName); 852 // add the line to the collection 853 maintCollection.add(addLine); 854 // refresh parent object since attributes could of changed prior to 855 // user clicking add 856 857 String referencesToRefresh = LookupUtils 858 .convertReferencesToSelectCollectionToString(getAllRefreshableReferences(getBusinessObject() 859 .getClass())); 860 if (LOG.isInfoEnabled()) { 861 LOG.info("References to refresh for adding line to collection " + collectionName + ": " 862 + referencesToRefresh); 863 } 864 refreshReferences(referencesToRefresh); 865 } 866 867 initNewCollectionLine(collectionName); 868 869 } 870 871 @Override 872 public PersistableBusinessObject getNewCollectionLine(String collectionName) { 873 if (LOG.isDebugEnabled()) { 874 // LOG.debug( this + ") getNewCollectionLine( " + collectionName + 875 // ")", new Exception( "tracing exception") ); 876 LOG.debug("newCollectionLines: " + newCollectionLines); 877 } 878 PersistableBusinessObject addLine = newCollectionLines.get(collectionName); 879 if (addLine == null) { 880 addLine = initNewCollectionLine(collectionName); 881 } 882 return addLine; 883 } 884 885 public PersistableBusinessObject initNewCollectionLine(String collectionName) { 886 if (LOG.isDebugEnabled()) { 887 LOG.debug("initNewCollectionLine( " + collectionName + " )"); 888 } 889 // try to get the object from the map 890 // BusinessObject addLine = newCollectionLines.get( collectionName ); 891 // if ( addLine == null ) { 892 // if not there, instantiate a new one 893 PersistableBusinessObject addLine; 894 try { 895 addLine = (PersistableBusinessObject) getMaintenanceDocumentDictionaryService() 896 .getCollectionBusinessObjectClass(getDocumentTypeName(), collectionName).newInstance(); 897 } 898 catch (Exception ex) { 899 LOG.error("unable to instantiate new collection line", ex); 900 throw new RuntimeException("unable to instantiate new collection line", ex); 901 } 902 // and add it to the map 903 newCollectionLines.put(collectionName, addLine); 904 // } 905 // set its values to the defaults 906 setNewCollectionLineDefaultValues(collectionName, addLine); 907 return addLine; 908 } 909 910 /** 911 * 912 * @see Maintainable#populateNewCollectionLines(java.util.Map) 913 */ 914 @Override 915 public Map<String, String> populateNewCollectionLines(Map<String, String> fieldValues, 916 MaintenanceDocument maintenanceDocument, String methodToCall) { 917 if (LOG.isDebugEnabled()) { 918 LOG.debug("populateNewCollectionLines: " + fieldValues); 919 } 920 fieldValues = decryptEncryptedData(fieldValues, maintenanceDocument, methodToCall); 921 922 Map<String, String> cachedValues = new HashMap<String, String>(); 923 924 // loop over all collections with an enabled add line 925 List<MaintainableCollectionDefinition> collections = getMaintenanceDocumentDictionaryService() 926 .getMaintainableCollections(getDocumentTypeName()); 927 928 for (MaintainableCollectionDefinition coll : collections) { 929 // get the collection name 930 String collName = coll.getName(); 931 if (LOG.isDebugEnabled()) { 932 LOG.debug("checking for collection: " + collName); 933 } 934 // build a map for that collection 935 Map<String, String> collectionValues = new HashMap<String, String>(); 936 Map<String, String> subCollectionValues = new HashMap<String, String>(); 937 // loop over the collection, extracting entries with a matching 938 // prefix 939 for (Map.Entry<String, String> entry : fieldValues.entrySet()) { 940 String key = entry.getKey(); 941 if (key.startsWith(collName)) { 942 String subStrKey = key.substring(collName.length() + 1); 943 // check for subcoll w/ '[', set collName to propername and 944 // put in correct name for collection values (i.e. strip 945 // '*[x].') 946 if (key.contains("[")) { 947 948 // collName = StringUtils.substringBeforeLast(key,"["); 949 950 // need the whole thing if subcollection 951 subCollectionValues.put(key, entry.getValue()); 952 } 953 else { 954 collectionValues.put(subStrKey, entry.getValue()); 955 } 956 } 957 } 958 // send those values to the business object 959 if (LOG.isDebugEnabled()) { 960 LOG.debug("values for collection: " + collectionValues); 961 } 962 cachedValues.putAll(FieldUtils.populateBusinessObjectFromMap(getNewCollectionLine(collName), 963 collectionValues, KRADConstants.MAINTENANCE_ADD_PREFIX + collName + ".")); 964 performFieldForceUpperCase(getNewCollectionLine(collName), collectionValues); 965 966 cachedValues.putAll(populateNewSubCollectionLines(coll, subCollectionValues)); 967 } 968 969 // cachedValues.putAll( FieldUtils.populateBusinessObjectFromMap( )) 970 return cachedValues; 971 } 972 973 /* 974 * Yes, I think this could be merged with the above code - I'm leaving it 975 * separate until I figure out of there are any issues which would reqire 976 * that it be separated. 977 */ 978 protected Map populateNewSubCollectionLines(MaintainableCollectionDefinition parentCollection, Map fieldValues) { 979 if (LOG.isDebugEnabled()) { 980 LOG.debug("populateNewSubCollectionLines: " + fieldValues); 981 } 982 Map cachedValues = new HashMap(); 983 984 for (MaintainableCollectionDefinition coll : parentCollection.getMaintainableCollections()) { 985 // get the collection name 986 String collName = coll.getName(); 987 988 if (LOG.isDebugEnabled()) { 989 LOG.debug("checking for sub collection: " + collName); 990 } 991 Map<String, String> parents = new HashMap<String, String>(); 992 // get parents from list 993 for (Object entry : fieldValues.entrySet()) { 994 String key = (String) ((Map.Entry) entry).getKey(); 995 if (key.contains(collName)) { 996 parents.put(StringUtils.substringBefore(key, "."), ""); 997 } 998 } 999 1000 for (String parent : parents.keySet()) { 1001 // build a map for that collection 1002 Map<String, Object> collectionValues = new HashMap<String, Object>(); 1003 // loop over the collection, extracting entries with a matching 1004 // prefix 1005 for (Object entry : fieldValues.entrySet()) { 1006 String key = (String) ((Map.Entry) entry).getKey(); 1007 if (key.contains(parent)) { 1008 String substr = StringUtils.substringAfterLast(key, "."); 1009 collectionValues.put(substr, ((Map.Entry) entry).getValue()); 1010 } 1011 } 1012 // send those values to the business object 1013 if (LOG.isDebugEnabled()) { 1014 LOG.debug("values for sub collection: " + collectionValues); 1015 } 1016 GlobalVariables.getMessageMap().addToErrorPath( 1017 KRADConstants.MAINTENANCE_ADD_PREFIX + parent + "." + collName); 1018 cachedValues.putAll(FieldUtils.populateBusinessObjectFromMap(getNewCollectionLine(parent + "." 1019 + collName), collectionValues, KRADConstants.MAINTENANCE_ADD_PREFIX + parent + "." + collName 1020 + ".")); 1021 performFieldForceUpperCase(getNewCollectionLine(parent + "." + collName), collectionValues); 1022 GlobalVariables.getMessageMap().removeFromErrorPath( 1023 KRADConstants.MAINTENANCE_ADD_PREFIX + parent + "." + collName); 1024 } 1025 1026 cachedValues.putAll(populateNewSubCollectionLines(coll, fieldValues)); 1027 } 1028 1029 return cachedValues; 1030 } 1031 1032 @Override 1033 public Collection<String> getAffectedReferencesFromLookup(BusinessObject baseBO, String attributeName, 1034 String collectionPrefix) { 1035 PersistenceStructureService pss = getPersistenceStructureService(); 1036 String nestedBOPrefix = ""; 1037 if (ObjectUtils.isNestedAttribute(attributeName)) { 1038 // if we're performing a lookup on a nested attribute, we need to 1039 // use the nested BO all the way down the chain 1040 nestedBOPrefix = ObjectUtils.getNestedAttributePrefix(attributeName); 1041 1042 // renormalize the base BO so that the attribute name is not nested 1043 // anymore 1044 Class reference = ObjectUtils.getPropertyType(baseBO, nestedBOPrefix, pss); 1045 if (!(PersistableBusinessObject.class.isAssignableFrom(reference))) { 1046 return new ArrayList<String>(); 1047 } 1048 1049 try { 1050 baseBO = (PersistableBusinessObject) reference.newInstance(); 1051 } 1052 catch (InstantiationException e) { 1053 LOG.error(e); 1054 } 1055 catch (IllegalAccessException e) { 1056 LOG.error(e); 1057 } 1058 attributeName = ObjectUtils.getNestedAttributePrimitive(attributeName); 1059 } 1060 1061 if (baseBO == null) { 1062 return new ArrayList<String>(); 1063 } 1064 1065 Map<String, Class> referenceNameToClassFromPSS = LookupUtils.getPrimitiveReference(baseBO, attributeName); 1066 if (referenceNameToClassFromPSS.size() > 1) { 1067 LOG.error("LookupUtils.getPrimitiveReference return results should only have at most one element"); 1068 } 1069 1070 BusinessObjectMetaDataService businessObjectMetaDataService = getBusinessObjectMetaDataService(); 1071 DataObjectRelationship relationship = businessObjectMetaDataService.getBusinessObjectRelationship(baseBO, 1072 attributeName); 1073 if (relationship == null) { 1074 return new ArrayList<String>(); 1075 } 1076 1077 Map<String, String> fkToPkMappings = relationship.getParentToChildReferences(); 1078 1079 Collection<String> affectedReferences = generateAllAffectedReferences(baseBO.getClass(), fkToPkMappings, 1080 nestedBOPrefix, collectionPrefix); 1081 if (LOG.isDebugEnabled()) { 1082 LOG.debug("References affected by a lookup on BO attribute \"" + collectionPrefix + nestedBOPrefix + "." 1083 + attributeName + ": " + affectedReferences); 1084 } 1085 1086 return affectedReferences; 1087 } 1088 1089 protected boolean isRelationshipRefreshable(Class boClass, String relationshipName) { 1090 if (getPersistenceStructureService().isPersistable(boClass)) { 1091 if (getPersistenceStructureService().hasCollection(boClass, relationshipName)) { 1092 return !getPersistenceStructureService().isCollectionUpdatable(boClass, relationshipName); 1093 } 1094 else if (getPersistenceStructureService().hasReference(boClass, relationshipName)) { 1095 return !getPersistenceStructureService().isReferenceUpdatable(boClass, relationshipName); 1096 } 1097 // else, assume that the relationship is defined in the DD 1098 } 1099 1100 return true; 1101 } 1102 1103 protected Collection<String> generateAllAffectedReferences(Class boClass, Map<String, String> fkToPkMappings, 1104 String nestedBOPrefix, String collectionPrefix) { 1105 Set<String> allAffectedReferences = new HashSet<String>(); 1106 DataDictionaryService dataDictionaryService = getDataDictionaryService(); 1107 PersistenceStructureService pss = getPersistenceStructureService(); 1108 1109 collectionPrefix = StringUtils.isBlank(collectionPrefix) ? "" : collectionPrefix; 1110 1111 // retrieve the attributes that are affected by a lookup on 1112 // attributeName. 1113 Collection<String> attributeReferenceFKAttributes = fkToPkMappings.keySet(); 1114 1115 // a lookup on an attribute may cause other attributes to be updated 1116 // (e.g. account code lookup would also affect chart code) 1117 // build a list of all affected FK values via mapKeyFields above, and 1118 // for each FK, see if there are any non-updatable references with that 1119 // FK 1120 1121 // deal with regular simple references (<reference-descriptor>s in OJB) 1122 for (String fkAttribute : attributeReferenceFKAttributes) { 1123 for (String affectedReference : pss.getReferencesForForeignKey(boClass, fkAttribute).keySet()) { 1124 if (isRelationshipRefreshable(boClass, affectedReference)) { 1125 if (StringUtils.isBlank(nestedBOPrefix)) { 1126 allAffectedReferences.add(collectionPrefix + affectedReference); 1127 } 1128 else { 1129 allAffectedReferences.add(collectionPrefix + nestedBOPrefix + "." + affectedReference); 1130 } 1131 } 1132 } 1133 } 1134 1135 // now with collection references (<collection-descriptor>s in OJB) 1136 for (String collectionName : pss.listCollectionObjectTypes(boClass).keySet()) { 1137 if (isRelationshipRefreshable(boClass, collectionName)) { 1138 Map<String, String> keyMappingsForCollection = pss.getInverseForeignKeysForCollection(boClass, 1139 collectionName); 1140 for (String collectionForeignKey : keyMappingsForCollection.keySet()) { 1141 if (attributeReferenceFKAttributes.contains(collectionForeignKey)) { 1142 if (StringUtils.isBlank(nestedBOPrefix)) { 1143 allAffectedReferences.add(collectionPrefix + collectionName); 1144 } 1145 else { 1146 allAffectedReferences.add(collectionPrefix + nestedBOPrefix + "." + collectionName); 1147 } 1148 } 1149 } 1150 } 1151 } 1152 1153 // now use the DD to compute more affected references 1154 List<String> ddDefinedRelationships = dataDictionaryService.getRelationshipNames(boClass.getName()); 1155 for (String ddRelationship : ddDefinedRelationships) { 1156 // note that this map is PK (key/target) => FK (value/source) 1157 Map<String, String> referencePKtoFKmappings = dataDictionaryService.getRelationshipAttributeMap( 1158 boClass.getName(), ddRelationship); 1159 for (String sourceAttribute : referencePKtoFKmappings.values()) { 1160 // the sourceAttribute is the FK pointing to the target 1161 // attribute (PK) 1162 if (attributeReferenceFKAttributes.contains(sourceAttribute)) { 1163 for (String affectedReference : dataDictionaryService.getRelationshipEntriesForSourceAttribute( 1164 boClass.getName(), sourceAttribute)) { 1165 if (isRelationshipRefreshable(boClass, ddRelationship)) { 1166 if (StringUtils.isBlank(nestedBOPrefix)) { 1167 allAffectedReferences.add(affectedReference); 1168 } 1169 else { 1170 allAffectedReferences.add(nestedBOPrefix + "." + affectedReference); 1171 } 1172 } 1173 } 1174 } 1175 } 1176 } 1177 return allAffectedReferences; 1178 } 1179 1180 protected Collection<String> getAllRefreshableReferences(Class boClass) { 1181 HashSet<String> references = new HashSet<String>(); 1182 for (String referenceName : getPersistenceStructureService().listReferenceObjectFields(boClass).keySet()) { 1183 if (isRelationshipRefreshable(boClass, referenceName)) { 1184 references.add(referenceName); 1185 } 1186 } 1187 for (String collectionName : getPersistenceStructureService().listCollectionObjectTypes(boClass).keySet()) { 1188 if (isRelationshipRefreshable(boClass, collectionName)) { 1189 references.add(collectionName); 1190 } 1191 } 1192 for (String relationshipName : getDataDictionaryService().getRelationshipNames(boClass.getName())) { 1193 if (isRelationshipRefreshable(boClass, relationshipName)) { 1194 references.add(relationshipName); 1195 } 1196 } 1197 return references; 1198 } 1199 1200 protected void setNewCollectionLineDefaultValues(String collectionName, PersistableBusinessObject addLine) { 1201 PropertyDescriptor[] descriptors = PropertyUtils.getPropertyDescriptors(addLine); 1202 for (int i = 0; i < descriptors.length; ++i) { 1203 PropertyDescriptor propertyDescriptor = descriptors[i]; 1204 1205 String fieldName = propertyDescriptor.getName(); 1206 Class propertyType = propertyDescriptor.getPropertyType(); 1207 String value = getMaintenanceDocumentDictionaryService().getCollectionFieldDefaultValue(getDocumentTypeName(), 1208 collectionName, fieldName); 1209 1210 if (value != null) { 1211 try { 1212 ObjectUtils.setObjectProperty(addLine, fieldName, propertyType, value); 1213 } 1214 catch (Exception ex) { 1215 LOG.error("Unable to set default property of collection object: " + "\nobject: " + addLine 1216 + "\nfieldName=" + fieldName + "\npropertyType=" + propertyType + "\nvalue=" + value, ex); 1217 } 1218 } 1219 1220 } 1221 } 1222 1223 /** 1224 * @see Maintainable#clearBusinessObjectOfRestrictedValues(org.kuali.rice.kns.document.authorization.MaintenanceDocumentRestrictions) 1225 */ 1226 @Override 1227 public void clearBusinessObjectOfRestrictedValues(MaintenanceDocumentRestrictions maintenanceDocumentRestrictions) { 1228 List<MaintainableSectionDefinition> sections = getMaintenanceDocumentDictionaryService() 1229 .getMaintainableSections(getDocumentTypeName()); 1230 for (MaintainableSectionDefinition sectionDefinition : sections) { 1231 for (MaintainableItemDefinition itemDefinition : sectionDefinition.getMaintainableItems()) { 1232 if (itemDefinition instanceof MaintainableFieldDefinition) { 1233 clearFieldRestrictedValues("", businessObject, (MaintainableFieldDefinition) itemDefinition, 1234 maintenanceDocumentRestrictions); 1235 } 1236 else if (itemDefinition instanceof MaintainableCollectionDefinition) { 1237 clearCollectionRestrictedValues("", businessObject, 1238 (MaintainableCollectionDefinition) itemDefinition, maintenanceDocumentRestrictions); 1239 } 1240 } 1241 } 1242 } 1243 1244 protected void clearCollectionRestrictedValues(String fieldNamePrefix, BusinessObject businessObject, 1245 MaintainableCollectionDefinition collectionDefinition, 1246 MaintenanceDocumentRestrictions maintenanceDocumentRestrictions) { 1247 String collectionName = fieldNamePrefix + collectionDefinition.getName(); 1248 Collection<BusinessObject> collection = (Collection<BusinessObject>) ObjectUtils.getPropertyValue( 1249 businessObject, collectionDefinition.getName()); 1250 1251 if (collection != null) { 1252 int i = 0; 1253 // even though it's technically a Collection, we're going to index 1254 // it like a list 1255 for (BusinessObject collectionItem : collection) { 1256 String collectionItemNamePrefix = collectionName + "[" + i + "]."; 1257 for (MaintainableCollectionDefinition subCollectionDefinition : collectionDefinition 1258 .getMaintainableCollections()) { 1259 clearCollectionRestrictedValues(collectionItemNamePrefix, collectionItem, subCollectionDefinition, 1260 maintenanceDocumentRestrictions); 1261 } 1262 for (MaintainableFieldDefinition fieldDefinition : collectionDefinition.getMaintainableFields()) { 1263 clearFieldRestrictedValues(collectionItemNamePrefix, collectionItem, fieldDefinition, 1264 maintenanceDocumentRestrictions); 1265 } 1266 i++; 1267 } 1268 } 1269 } 1270 1271 protected void clearFieldRestrictedValues(String fieldNamePrefix, BusinessObject businessObject, 1272 MaintainableFieldDefinition fieldDefinition, MaintenanceDocumentRestrictions maintenanceDocumentRestrictions) { 1273 String fieldName = fieldNamePrefix + fieldDefinition.getName(); 1274 1275 FieldRestriction fieldRestriction = maintenanceDocumentRestrictions.getFieldRestriction(fieldName); 1276 if (fieldRestriction.isRestricted()) { 1277 String defaultValue = null; 1278 if (StringUtils.isNotBlank(fieldDefinition.getDefaultValue())) { 1279 defaultValue = fieldDefinition.getDefaultValue(); 1280 } 1281 else if (fieldDefinition.getDefaultValueFinderClass() != null) { 1282 try { 1283 defaultValue = ((ValueFinder) fieldDefinition.getDefaultValueFinderClass().newInstance()) 1284 .getValue(); 1285 } 1286 catch (Exception e) { 1287 defaultValue = null; 1288 LOG.error("Error trying to instantiate ValueFinder or to determine ValueFinder for doc type: " 1289 + getDocumentTypeName() + " field name " + fieldDefinition.getName() + " with field prefix: " 1290 + fieldNamePrefix, e); 1291 } 1292 } 1293 try { 1294 ObjectUtils.setObjectProperty(businessObject, fieldDefinition.getName(), defaultValue); 1295 } 1296 catch (Exception e) { 1297 // throw an exception, because we don't want users to be able to 1298 // see the restricted value 1299 LOG.error("Unable to clear maintenance document values for field name: " + fieldName 1300 + " default value: " + defaultValue, e); 1301 throw new RuntimeException("Unable to clear maintenance document values for field name: " + fieldName, 1302 e); 1303 } 1304 } 1305 } 1306 1307 protected void performForceUpperCase(Map fieldValues) { 1308 List<MaintainableSectionDefinition> sections = getMaintenanceDocumentDictionaryService() 1309 .getMaintainableSections(getDocumentTypeName()); 1310 for (MaintainableSectionDefinition sectionDefinition : sections) { 1311 for (MaintainableItemDefinition itemDefinition : sectionDefinition.getMaintainableItems()) { 1312 if (itemDefinition instanceof MaintainableFieldDefinition) { 1313 performFieldForceUpperCase("", businessObject, (MaintainableFieldDefinition) itemDefinition, 1314 fieldValues); 1315 } 1316 else if (itemDefinition instanceof MaintainableCollectionDefinition) { 1317 performCollectionForceUpperCase("", businessObject, 1318 (MaintainableCollectionDefinition) itemDefinition, fieldValues); 1319 1320 } 1321 } 1322 } 1323 } 1324 1325 protected void performFieldForceUpperCase(String fieldNamePrefix, BusinessObject bo, 1326 MaintainableFieldDefinition fieldDefinition, Map fieldValues) { 1327 MessageMap errorMap = GlobalVariables.getMessageMap(); 1328 String fieldName = fieldDefinition.getName(); 1329 String mapKey = fieldNamePrefix + fieldName; 1330 if (fieldValues != null && fieldValues.get(mapKey) != null) { 1331 if (PropertyUtils.isWriteable(bo, fieldName) && ObjectUtils.getNestedValue(bo, fieldName) != null) { 1332 1333 try { 1334 Class type = ObjectUtils.easyGetPropertyType(bo, fieldName); 1335 // convert to upperCase based on data dictionary 1336 Class businessObjectClass = bo.getClass(); 1337 boolean upperCase = false; 1338 try { 1339 upperCase = getDataDictionaryService().getAttributeForceUppercase(businessObjectClass, 1340 fieldName); 1341 } 1342 catch (UnknownBusinessClassAttributeException t) { 1343 boolean catchme = true; 1344 // throw t; 1345 } 1346 1347 Object fieldValue = ObjectUtils.getNestedValue(bo, fieldName); 1348 1349 if (upperCase && fieldValue instanceof String) { 1350 fieldValue = ((String) fieldValue).toUpperCase(); 1351 } 1352 ObjectUtils.setObjectProperty(bo, fieldName, type, fieldValue); 1353 } 1354 catch (FormatException e) { 1355 errorMap.putError(fieldName, e.getErrorKey(), e.getErrorArgs()); 1356 } 1357 catch (IllegalAccessException e) { 1358 LOG.error("unable to populate business object" + e.getMessage()); 1359 throw new RuntimeException(e.getMessage(), e); 1360 } 1361 catch (InvocationTargetException e) { 1362 LOG.error("unable to populate business object" + e.getMessage()); 1363 throw new RuntimeException(e.getMessage(), e); 1364 } 1365 catch (NoSuchMethodException e) { 1366 LOG.error("unable to populate business object" + e.getMessage()); 1367 throw new RuntimeException(e.getMessage(), e); 1368 } 1369 } 1370 } 1371 } 1372 1373 protected void performCollectionForceUpperCase(String fieldNamePrefix, BusinessObject bo, 1374 MaintainableCollectionDefinition collectionDefinition, Map fieldValues) { 1375 String collectionName = fieldNamePrefix + collectionDefinition.getName(); 1376 Collection<BusinessObject> collection = (Collection<BusinessObject>) ObjectUtils.getPropertyValue(bo, 1377 collectionDefinition.getName()); 1378 if (collection != null) { 1379 int i = 0; 1380 // even though it's technically a Collection, we're going to index 1381 // it like a list 1382 for (BusinessObject collectionItem : collection) { 1383 String collectionItemNamePrefix = collectionName + "[" + i + "]."; 1384 // String collectionItemNamePrefix = ""; 1385 for (MaintainableFieldDefinition fieldDefinition : collectionDefinition.getMaintainableFields()) { 1386 performFieldForceUpperCase(collectionItemNamePrefix, collectionItem, fieldDefinition, fieldValues); 1387 } 1388 for (MaintainableCollectionDefinition subCollectionDefinition : collectionDefinition 1389 .getMaintainableCollections()) { 1390 performCollectionForceUpperCase(collectionItemNamePrefix, collectionItem, subCollectionDefinition, 1391 fieldValues); 1392 } 1393 i++; 1394 } 1395 } 1396 } 1397 1398 protected void performFieldForceUpperCase(BusinessObject bo, Map fieldValues) { 1399 MessageMap errorMap = GlobalVariables.getMessageMap(); 1400 1401 try { 1402 for (Iterator iter = fieldValues.keySet().iterator(); iter.hasNext();) { 1403 String propertyName = (String) iter.next(); 1404 1405 if (PropertyUtils.isWriteable(bo, propertyName) && fieldValues.get(propertyName) != null) { 1406 // if the field propertyName is a valid property on the bo 1407 // class 1408 Class type = ObjectUtils.easyGetPropertyType(bo, propertyName); 1409 try { 1410 // Keep the convert to upperCase logic here. It will be 1411 // used in populateNewCollectionLines, 1412 // populateNewSubCollectionLines 1413 // convert to upperCase based on data dictionary 1414 Class businessObjectClass = bo.getClass(); 1415 boolean upperCase = false; 1416 try { 1417 upperCase = getDataDictionaryService().getAttributeForceUppercase(businessObjectClass, 1418 propertyName); 1419 } 1420 catch (UnknownBusinessClassAttributeException t) { 1421 boolean catchme = true; 1422 // throw t; 1423 } 1424 1425 Object fieldValue = fieldValues.get(propertyName); 1426 1427 if (upperCase && fieldValue instanceof String) { 1428 fieldValue = ((String) fieldValue).toUpperCase(); 1429 } 1430 ObjectUtils.setObjectProperty(bo, propertyName, type, fieldValue); 1431 } 1432 catch (FormatException e) { 1433 errorMap.putError(propertyName, e.getErrorKey(), e.getErrorArgs()); 1434 } 1435 } 1436 } 1437 } 1438 catch (IllegalAccessException e) { 1439 LOG.error("unable to populate business object" + e.getMessage()); 1440 throw new RuntimeException(e.getMessage(), e); 1441 } 1442 catch (InvocationTargetException e) { 1443 LOG.error("unable to populate business object" + e.getMessage()); 1444 throw new RuntimeException(e.getMessage(), e); 1445 } 1446 catch (NoSuchMethodException e) { 1447 LOG.error("unable to populate business object" + e.getMessage()); 1448 throw new RuntimeException(e.getMessage(), e); 1449 } 1450 1451 } 1452 1453 /** 1454 * By default a maintainable is not external 1455 * 1456 * @see Maintainable#isExternalBusinessObject() 1457 */ 1458 @Override 1459 public boolean isExternalBusinessObject() { 1460 return false; 1461 } 1462 1463 /** 1464 * @see Maintainable#getExternalBusinessObject() 1465 */ 1466 @Override 1467 public void prepareBusinessObject(BusinessObject businessObject) { 1468 // Do nothing by default 1469 } 1470 1471 // 3070 1472 @Override 1473 public void deleteBusinessObject() { 1474 if (businessObject == null) 1475 return; 1476 1477 KNSServiceLocator.getBusinessObjectService().delete(businessObject); 1478 businessObject = null; 1479 } 1480 1481 @Override 1482 public boolean isOldBusinessObjectInDocument() { 1483 return super.isOldDataObjectInDocument(); 1484 } 1485 1486 protected BusinessObjectDictionaryService getBusinessObjectDictionaryService() { 1487 if (businessObjectDictionaryService == null) { 1488 businessObjectDictionaryService = KNSServiceLocator.getBusinessObjectDictionaryService(); 1489 } 1490 return businessObjectDictionaryService; 1491 } 1492 1493 protected PersonService getPersonService() { 1494 if (personService == null) { 1495 personService = KimApiServiceLocator.getPersonService(); 1496 } 1497 return personService; 1498 } 1499 1500 @Deprecated 1501 protected BusinessObjectMetaDataService getBusinessObjectMetaDataService() { 1502 if (businessObjectMetaDataService == null) { 1503 businessObjectMetaDataService = KNSServiceLocator.getBusinessObjectMetaDataService(); 1504 } 1505 return businessObjectMetaDataService; 1506 } 1507 1508 protected BusinessObjectAuthorizationService getBusinessObjectAuthorizationService() { 1509 if (businessObjectAuthorizationService == null) { 1510 businessObjectAuthorizationService = KNSServiceLocator.getBusinessObjectAuthorizationService(); 1511 } 1512 return businessObjectAuthorizationService; 1513 } 1514 1515 protected DocumentHelperService getDocumentHelperService() { 1516 if (documentHelperService == null) { 1517 documentHelperService = KNSServiceLocator.getDocumentHelperService(); 1518 } 1519 return documentHelperService; 1520 } 1521 1522 public void setBusinessObjectDictionaryService(BusinessObjectDictionaryService businessObjectDictionaryService) { 1523 this.businessObjectDictionaryService = businessObjectDictionaryService; 1524 } 1525 1526 public void setPersonService(PersonService personService) { 1527 this.personService = personService; 1528 } 1529 1530 @Deprecated 1531 public void setBusinessObjectMetaDataService(BusinessObjectMetaDataService businessObjectMetaDataService) { 1532 this.businessObjectMetaDataService = businessObjectMetaDataService; 1533 } 1534 1535 public void setBusinessObjectAuthorizationService( 1536 BusinessObjectAuthorizationService businessObjectAuthorizationService) { 1537 this.businessObjectAuthorizationService = businessObjectAuthorizationService; 1538 } 1539 1540 public void setDocumentHelperService(DocumentHelperService documentHelperService) { 1541 this.documentHelperService = documentHelperService; 1542 } 1543 1544 public MaintenanceDocumentDictionaryService getMaintenanceDocumentDictionaryService() { 1545 if (maintenanceDocumentDictionaryService == null) { 1546 this.maintenanceDocumentDictionaryService = KNSServiceLocator.getMaintenanceDocumentDictionaryService(); 1547 } 1548 return maintenanceDocumentDictionaryService; 1549 } 1550 1551 public void setMaintenanceDocumentDictionaryService( 1552 MaintenanceDocumentDictionaryService maintenanceDocumentDictionaryService) { 1553 this.maintenanceDocumentDictionaryService = maintenanceDocumentDictionaryService; 1554 } 1555 1556 @Deprecated // KNS Metadata service 1557 protected DataObjectMetaDataService getDataObjectMetaDataService() { 1558 if (dataObjectMetaDataService == null) { 1559 this.dataObjectMetaDataService = KNSServiceLocator.getDataObjectMetaDataService(); 1560 } 1561 return dataObjectMetaDataService; 1562 } 1563 1564 @Deprecated 1565 public void setDataObjectMetaDataService(DataObjectMetaDataService dataObjectMetaDataService) { 1566 this.dataObjectMetaDataService = dataObjectMetaDataService; 1567 } 1568 1569 public PersistenceStructureService getPersistenceStructureService() { 1570 return KNSServiceLocator.getPersistenceStructureService(); 1571 } 1572 1573}