View Javadoc

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