View Javadoc

1   /**
2    * Copyright 2005-2011 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.MaintenanceDocumentRestrictions;
31  import org.kuali.rice.kns.lookup.LookupUtils;
32  import org.kuali.rice.kns.service.BusinessObjectAuthorizationService;
33  import org.kuali.rice.kns.service.BusinessObjectMetaDataService;
34  import org.kuali.rice.kns.service.KNSServiceLocator;
35  import org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService;
36  import org.kuali.rice.kns.util.FieldUtils;
37  import org.kuali.rice.kns.util.InactiveRecordsHidingUtils;
38  import org.kuali.rice.kns.util.MaintenanceUtils;
39  import org.kuali.rice.kns.web.ui.Section;
40  import org.kuali.rice.kns.web.ui.SectionBridge;
41  import org.kuali.rice.krad.bo.BusinessObject;
42  import org.kuali.rice.krad.bo.DataObjectRelationship;
43  import org.kuali.rice.krad.bo.PersistableBusinessObject;
44  import org.kuali.rice.krad.datadictionary.AttributeSecurity;
45  import org.kuali.rice.krad.datadictionary.exception.UnknownBusinessClassAttributeException;
46  import org.kuali.rice.krad.document.authorization.MaintenanceDocumentPresentationController;
47  import org.kuali.rice.krad.valuefinder.ValueFinder;
48  import org.kuali.rice.krad.maintenance.MaintainableImpl;
49  import org.kuali.rice.krad.service.BusinessObjectDictionaryService;
50  import org.kuali.rice.krad.service.DataDictionaryService;
51  import org.kuali.rice.krad.service.DocumentHelperService;
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.document.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 	 * @see Maintainable#saveBusinessObject()
296 	 */
297 	public void saveBusinessObject() {
298 		getBusinessObjectService().linkAndSave(businessObject);
299 	}
300 
301 	/**
302 	 * Retrieves title for maintenance document from data dictionary
303 	 */
304 	public String getMaintainableTitle() {
305 		return getMaintenanceDocumentDictionaryService().getMaintenanceLabel(getDocumentTypeName());
306 	}
307 
308     @Override
309     public void setupNewFromExisting(MaintenanceDocument document, Map<String, String[]> parameters) {
310     }
311 
312     /**
313 	 * Retrieves the status of the boNotesEnabled
314 	 */
315 	public boolean isBoNotesEnabled() {
316 	    return super.isNotesEnabled();
317 	}
318 
319 	/**
320 	 * @see Maintainable#refresh(java.lang.String,
321 	 *      java.util.Map) Impls will be needed if custom action is needed on
322 	 *      refresh.
323 	 */
324 	public void refresh(String refreshCaller, Map fieldValues, MaintenanceDocument document) {
325 		String referencesToRefresh = (String) fieldValues.get(KRADConstants.REFERENCES_TO_REFRESH);
326 		refreshReferences(referencesToRefresh);
327 	}
328 
329 	protected void refreshReferences(String referencesToRefresh) {
330 		PersistenceStructureService persistenceStructureService = getPersistenceStructureService();
331 		if (StringUtils.isNotBlank(referencesToRefresh)) {
332 			String[] references = StringUtils.split(referencesToRefresh, KRADConstants.REFERENCES_TO_REFRESH_SEPARATOR);
333 			for (String reference : references) {
334 				if (StringUtils.isNotBlank(reference)) {
335 					if (reference.startsWith(KRADConstants.ADD_PREFIX + ".")) {
336 						// add one for the period
337 						reference = reference.substring(KRADConstants.ADD_PREFIX.length() + 1);
338 
339 						String boToRefreshName = StringUtils.substringBeforeLast(reference, ".");
340 						String propertyToRefresh = StringUtils.substringAfterLast(reference, ".");
341 						if (StringUtils.isNotBlank(propertyToRefresh)) {
342 							PersistableBusinessObject addlineBO = getNewCollectionLine(boToRefreshName);
343 							Class addlineBOClass = addlineBO.getClass();
344 							if (LOG.isDebugEnabled()) {
345 								LOG.debug("Refresh this \"new\"/add object for the collections:  " + reference);
346 							}
347 							if (persistenceStructureService.hasReference(addlineBOClass, propertyToRefresh)
348 									|| persistenceStructureService.hasCollection(addlineBOClass, propertyToRefresh)) {
349 								addlineBO.refreshReferenceObject(propertyToRefresh);
350 							}
351 							else {
352 								if (getDataDictionaryService().hasRelationship(addlineBOClass.getName(),
353 										propertyToRefresh)) {
354 									// a DD mapping, try to go straight to the
355 									// object and refresh it there
356 									Object possibleBO = ObjectUtils.getPropertyValue(addlineBO, propertyToRefresh);
357 									if (possibleBO != null && possibleBO instanceof PersistableBusinessObject) {
358 										((PersistableBusinessObject) possibleBO).refresh();
359 									}
360 								}
361 							}
362 						}
363 						else {
364 							LOG.error("Error: unable to refresh this \"new\"/add object for the collections:  "
365 									+ reference);
366 						}
367 					}
368 					else if (ObjectUtils.isNestedAttribute(reference)) {
369 						Object nestedObject = ObjectUtils.getNestedValue(getBusinessObject(),
370 								ObjectUtils.getNestedAttributePrefix(reference));
371 						if (nestedObject instanceof Collection) {
372 							// do nothing, probably because it's not really a
373 							// collection reference but a relationship defined
374 							// in the DD for a collections lookup
375 							// this part will need to be rewritten when the DD
376 							// supports true collection references
377 						}
378 						else if (nestedObject instanceof PersistableBusinessObject) {
379 							String propertyToRefresh = ObjectUtils.getNestedAttributePrimitive(reference);
380 							if (persistenceStructureService.hasReference(nestedObject.getClass(), propertyToRefresh)
381 									|| persistenceStructureService.hasCollection(nestedObject.getClass(),
382 											propertyToRefresh)) {
383 								if (LOG.isDebugEnabled()) {
384 									LOG.debug("Refeshing " + ObjectUtils.getNestedAttributePrefix(reference) + " "
385 											+ ObjectUtils.getNestedAttributePrimitive(reference));
386 								}
387 								((PersistableBusinessObject) nestedObject).refreshReferenceObject(propertyToRefresh);
388 							}
389 							else {
390 								// a DD mapping, try to go straight to the
391 								// object and refresh it there
392 								Object possibleBO = ObjectUtils.getPropertyValue(nestedObject, propertyToRefresh);
393 								if (possibleBO != null && possibleBO instanceof PersistableBusinessObject) {
394 									if (getDataDictionaryService().hasRelationship(possibleBO.getClass().getName(),
395 											propertyToRefresh)) {
396 										((PersistableBusinessObject) possibleBO).refresh();
397 									}
398 								}
399 							}
400 						}
401 						else {
402 							LOG.warn("Expected that a referenceToRefresh ("
403 									+ reference
404 									+ ")  would be a PersistableBusinessObject or Collection, but instead, it was of class "
405 									+ nestedObject.getClass().getName());
406 						}
407 					}
408 					else {
409 						if (LOG.isDebugEnabled()) {
410 							LOG.debug("Refreshing " + reference);
411 						}
412 						if (persistenceStructureService.hasReference(getDataObjectClass(), reference)
413 								|| persistenceStructureService.hasCollection(getDataObjectClass(), reference)) {
414 							getBusinessObject().refreshReferenceObject(reference);
415 						}
416 						else {
417 							if (getDataDictionaryService().hasRelationship(getBusinessObject().getClass().getName(),
418 									reference)) {
419 								// a DD mapping, try to go straight to the
420 								// object and refresh it there
421 								Object possibleRelationship = ObjectUtils.getPropertyValue(getBusinessObject(),
422 										reference);
423 								if (possibleRelationship != null) {
424 									if (possibleRelationship instanceof PersistableBusinessObject) {
425 										((PersistableBusinessObject) possibleRelationship).refresh();
426 									}
427 									else if (possibleRelationship instanceof Collection) {
428 										// do nothing, probably because it's not
429 										// really a collection reference but a
430 										// relationship defined in the DD for a
431 										// collections lookup
432 										// this part will need to be rewritten
433 										// when the DD supports true collection
434 										// references
435 									}
436 									else {
437 										LOG.warn("Expected that a referenceToRefresh ("
438 												+ reference
439 												+ ")  would be a PersistableBusinessObject or Collection, but instead, it was of class "
440 												+ possibleRelationship.getClass().getName());
441 									}
442 								}
443 							}
444 						}
445 					}
446 				}
447 			}
448 		}
449 	}
450 
451 	public void addMultipleValueLookupResults(MaintenanceDocument document, String collectionName,
452 			Collection<PersistableBusinessObject> rawValues, boolean needsBlank, PersistableBusinessObject bo) {
453 		Collection maintCollection = (Collection) ObjectUtils.getPropertyValue(bo, collectionName);
454 		String docTypeName = document.getDocumentHeader().getWorkflowDocument().getDocumentTypeName();
455 
456 		List<String> duplicateIdentifierFieldsFromDataDictionary = getDuplicateIdentifierFieldsFromDataDictionary(
457 				docTypeName, collectionName);
458 
459 		List<String> existingIdentifierList = getMultiValueIdentifierList(maintCollection,
460 				duplicateIdentifierFieldsFromDataDictionary);
461 
462 		Class collectionClass = getMaintenanceDocumentDictionaryService().getCollectionBusinessObjectClass(docTypeName,
463 				collectionName);
464 
465 		List<MaintainableSectionDefinition> sections = getMaintenanceDocumentDictionaryService()
466 				.getMaintainableSections(docTypeName);
467 		Map<String, String> template = MaintenanceUtils.generateMultipleValueLookupBOTemplate(sections, collectionName);
468 		try {
469 			for (PersistableBusinessObject nextBo : rawValues) {
470 				PersistableBusinessObject templatedBo;
471 				if (needsBlank) {
472 					templatedBo = (PersistableBusinessObject) collectionClass.newInstance();
473 				}
474 				else {
475 					// templatedBo = (PersistableBusinessObject)
476 					// ObjectUtils.createHybridBusinessObject(collectionClass,
477 					// nextBo, template);
478 					try {
479 						ModuleService moduleService = KRADServiceLocatorWeb.getKualiModuleService()
480 								.getResponsibleModuleService(collectionClass);
481 						if (moduleService != null && moduleService.isExternalizable(collectionClass))
482 							templatedBo = (PersistableBusinessObject) moduleService
483 									.createNewObjectFromExternalizableClass(collectionClass);
484 						else
485 							templatedBo = (PersistableBusinessObject) collectionClass.newInstance();
486 					}
487 					catch (Exception e) {
488 						throw new RuntimeException("Cannot instantiate " + collectionClass.getName(), e);
489 					}
490 					// first set the default values specified in the DD
491 					setNewCollectionLineDefaultValues(collectionName, templatedBo);
492 					// then set the values from the multiple value lookup result
493 					ObjectUtils.createHybridBusinessObject(templatedBo, nextBo, template);
494 
495 					prepareBusinessObjectForAdditionFromMultipleValueLookup(collectionName, templatedBo);
496 				}
497 				templatedBo.setNewCollectionRecord(true);
498 
499 				if (!hasBusinessObjectExisted(templatedBo, existingIdentifierList,
500 						duplicateIdentifierFieldsFromDataDictionary)) {
501 					maintCollection.add(templatedBo);
502 
503 				}
504 			}
505 		}
506 		catch (Exception e) {
507 			LOG.error("Unable to add multiple value lookup results " + e.getMessage());
508 			throw new RuntimeException("Unable to add multiple value lookup results " + e.getMessage());
509 		}
510 	}
511 
512 	/**
513 	 * This method is to retrieve a List of fields which are specified in the
514 	 * maintenance document data dictionary as the
515 	 * duplicateIdentificationFields. This List is used to determine whether the
516 	 * new entry being added to the collection is a duplicate entry and if so,
517 	 * we should not add the new entry to the existing collection
518 	 * 
519 	 * @param docTypeName
520 	 * @param collectionName
521 	 */
522 	public List<String> getDuplicateIdentifierFieldsFromDataDictionary(String docTypeName, String collectionName) {
523 		List<String> duplicateIdentifierFieldNames = new ArrayList<String>();
524 		MaintainableCollectionDefinition collDef = getMaintenanceDocumentDictionaryService().getMaintainableCollection(
525 				docTypeName, collectionName);
526 		Collection<MaintainableFieldDefinition> fieldDef = collDef.getDuplicateIdentificationFields();
527 		for (MaintainableFieldDefinition eachFieldDef : fieldDef) {
528 			duplicateIdentifierFieldNames.add(eachFieldDef.getName());
529 		}
530 		return duplicateIdentifierFieldNames;
531 	}
532 
533 	public List<String> getMultiValueIdentifierList(Collection maintCollection, List<String> duplicateIdentifierFields) {
534 		List<String> identifierList = new ArrayList<String>();
535 		for (PersistableBusinessObject bo : (Collection<PersistableBusinessObject>) maintCollection) {
536 			String uniqueIdentifier = new String();
537 			for (String identifierField : duplicateIdentifierFields) {
538 				uniqueIdentifier = uniqueIdentifier + identifierField + "-"
539 						+ ObjectUtils.getPropertyValue(bo, identifierField);
540 			}
541 			if (StringUtils.isNotEmpty(uniqueIdentifier)) {
542 				identifierList.add(uniqueIdentifier);
543 			}
544 		}
545 		return identifierList;
546 	}
547 
548 	public boolean hasBusinessObjectExisted(BusinessObject bo, List<String> existingIdentifierList,
549 			List<String> duplicateIdentifierFields) {
550 		String uniqueIdentifier = new String();
551 		for (String identifierField : duplicateIdentifierFields) {
552 			uniqueIdentifier = uniqueIdentifier + identifierField + "-"
553 					+ ObjectUtils.getPropertyValue(bo, identifierField);
554 		}
555 		if (existingIdentifierList.contains(uniqueIdentifier)) {
556 			return true;
557 		}
558 		else {
559 			return false;
560 		}
561 	}
562 
563 	public void prepareBusinessObjectForAdditionFromMultipleValueLookup(String collectionName, BusinessObject bo) {
564 		// default implementation does nothing
565 	}
566 
567 	/**
568 	 * Set the new collection records back to true so they can be deleted (copy
569 	 * should act like new)
570 	 * 
571 	 * @see KualiMaintainableImpl#processAfterCopy()
572 	 */
573 	public void processAfterCopy(MaintenanceDocument document, Map<String, String[]> parameters) {
574 		try {
575 			ObjectUtils.setObjectPropertyDeep(businessObject, KRADPropertyConstants.NEW_COLLECTION_RECORD,
576 					boolean.class, true, 2);
577 		}
578 		catch (Exception e) {
579 			LOG.error("unable to set newCollectionRecord property: " + e.getMessage(), e);
580 			throw new RuntimeException("unable to set newCollectionRecord property: " + e.getMessage(), e);
581 		}
582 	}
583 
584     @Override
585     public void processAfterEdit(MaintenanceDocument document, Map<String, String[]> requestParameters) {
586 
587     }
588 
589     @Override
590     public void processAfterNew(MaintenanceDocument document, Map<String, String[]> requestParameters) {
591 
592     }
593 
594     @Override
595     public void processAfterPost(MaintenanceDocument document, Map<String, String[]> requestParameters) {
596 
597     }
598 
599     @Override
600     public void setDataObject(Object object) {
601         super.setDataObject(object);
602         
603         if(object instanceof PersistableBusinessObject) {
604             this.businessObject = (PersistableBusinessObject)object;
605         }
606     }
607 
608     @Override
609     public String getDocumentTitle(MaintenanceDocument document) {
610         return super.getDocumentTitle((org.kuali.rice.krad.document.MaintenanceDocument) document);
611     }
612 
613     /**
614      * @return Returns the instance of the business object being maintained.
615      */
616     public PersistableBusinessObject getBusinessObject() {
617         return businessObject;
618     }
619 
620     /**
621      * @param businessObject
622      *            Sets the instance of a business object that will be
623      *            maintained.
624      */
625     public void setBusinessObject(PersistableBusinessObject businessObject) {
626         this.businessObject = businessObject;
627         setDataObject(businessObject);
628     }
629 
630 	/**
631 	 * @return Returns the boClass.
632 	 */
633 	public Class getBoClass() {
634 		return super.getDataObjectClass();
635 	}
636 
637 	/**
638 	 * @param boClass
639 	 *            The boClass to set.
640 	 */
641 	public void setBoClass(Class boClass) {
642 		setDataObjectClass(boClass);
643 	}
644 
645 	/**
646 	 * 
647 	 * @see Maintainable#setGenerateDefaultValues()
648 	 */
649 	public void setGenerateDefaultValues(String docTypeName) {
650 		List<MaintainableSectionDefinition> sectionDefinitions = getMaintenanceDocumentDictionaryService()
651 				.getMaintainableSections(docTypeName);
652 		Map defaultValues = new HashMap();
653 
654 		try {
655 			// iterate through section definitions
656 			for (Iterator iter = sectionDefinitions.iterator(); iter.hasNext();) {
657 
658 				MaintainableSectionDefinition maintSectionDef = (MaintainableSectionDefinition) iter.next();
659 				Collection maintItems = maintSectionDef.getMaintainableItems();
660 				for (Iterator iterator = maintItems.iterator(); iterator.hasNext();) {
661 					MaintainableItemDefinition item = (MaintainableItemDefinition) iterator.next();
662 
663 					if (item instanceof MaintainableFieldDefinition) {
664 						MaintainableFieldDefinition maintainableFieldDefinition = (MaintainableFieldDefinition) item;
665 
666 						String defaultValue = maintainableFieldDefinition.getDefaultValue();
667 						if (defaultValue != null) {
668 							if (defaultValue.equals("true")) {
669 								defaultValue = "Yes";
670 							}
671 							else if (defaultValue.equals("false")) {
672 								defaultValue = "No";
673 							}
674 						}
675 
676 						Class defaultValueFinderClass = maintainableFieldDefinition.getDefaultValueFinderClass();
677 						if (defaultValueFinderClass != null) {
678 							defaultValue = ((ValueFinder) defaultValueFinderClass.newInstance()).getValue();
679 
680 						}
681 						if (defaultValue != null) {
682 							defaultValues.put(item.getName(), defaultValue);
683 						}
684 					}
685 				}
686 			}
687 			Map cachedValues = FieldUtils.populateBusinessObjectFromMap(getBusinessObject(), defaultValues);
688 		}
689 		catch (Exception e) {
690 			LOG.error("Unable to set default value " + e.getMessage(), e);
691 			throw new RuntimeException("Unable to set default value" + e.getMessage(), e);
692 		}
693 
694 	}
695 
696 	/**
697 	 * 
698 	 * @see Maintainable#setGenerateBlankRequiredValues()
699 	 */
700 	public void setGenerateBlankRequiredValues(String docTypeName) {
701 		try {
702 			List<MaintainableSectionDefinition> sectionDefinitions = getMaintenanceDocumentDictionaryService()
703 					.getMaintainableSections(docTypeName);
704 			Map<String, String> defaultValues = new HashMap<String, String>();
705 
706 			for (MaintainableSectionDefinition maintSectionDef : sectionDefinitions) {
707 				for (MaintainableItemDefinition item : maintSectionDef.getMaintainableItems()) {
708 					if (item instanceof MaintainableFieldDefinition) {
709 						MaintainableFieldDefinition maintainableFieldDefinition = (MaintainableFieldDefinition) item;
710 						if (maintainableFieldDefinition.isRequired()
711 								&& maintainableFieldDefinition.isUnconditionallyReadOnly()) {
712 							Object currPropVal = ObjectUtils.getPropertyValue(this.getBusinessObject(), item.getName());
713 							if (currPropVal == null
714 									|| (currPropVal instanceof String && StringUtils.isBlank((String) currPropVal))) {
715 								Class<? extends ValueFinder> defaultValueFinderClass = maintainableFieldDefinition
716 										.getDefaultValueFinderClass();
717 								if (defaultValueFinderClass != null) {
718 									String defaultValue = defaultValueFinderClass.newInstance().getValue();
719 									if (defaultValue != null) {
720 										defaultValues.put(item.getName(), defaultValue);
721 									}
722 								}
723 							}
724 						}
725 					}
726 				}
727 			}
728 			FieldUtils.populateBusinessObjectFromMap(getBusinessObject(), defaultValues);
729 		}
730 		catch (Exception e) {
731 			LOG.error("Unable to set blank required value " + e.getMessage(), e);
732 			throw new RuntimeException("Unable to set blank required value" + e.getMessage(), e);
733 		}
734 	}
735 
736 	@Deprecated
737 	public void processAfterAddLine(String colName, Class colClass) {
738 	}
739 
740 	/**
741 	 * @see Maintainable#processBeforeAddLine(java.lang.String,
742 	 *      java.lang.Class, org.kuali.rice.krad.bo.BusinessObject)
743 	 */
744 	public void processBeforeAddLine(String colName, Class colClass, BusinessObject addBO) {
745 	}
746 
747 	/**
748 	 * @see Maintainable#getShowInactiveRecords(java.lang.String)
749 	 */
750 	public boolean getShowInactiveRecords(String collectionName) {
751 		return InactiveRecordsHidingUtils.getShowInactiveRecords(inactiveRecordDisplay, collectionName);
752 	}
753 
754 	/**
755 	 * @see Maintainable#setShowInactiveRecords(java.lang.String,
756 	 *      boolean)
757 	 */
758 	public void setShowInactiveRecords(String collectionName, boolean showInactive) {
759 		InactiveRecordsHidingUtils.setShowInactiveRecords(inactiveRecordDisplay, collectionName, showInactive);
760 	}
761 
762 	/**
763 	 * @return the inactiveRecordDisplay
764 	 */
765 	public Map<String, Boolean> getInactiveRecordDisplay() {
766 		return inactiveRecordDisplay;
767 	}
768 
769 	public void addNewLineToCollection(String collectionName) {
770 
771 		if (LOG.isDebugEnabled()) {
772 			LOG.debug("addNewLineToCollection( " + collectionName + " )");
773 		}
774 		// get the new line from the map
775 		PersistableBusinessObject addLine = newCollectionLines.get(collectionName);
776 		if (addLine != null) {
777 			// mark the isNewCollectionRecord so the option to delete this line
778 			// will be presented
779 			addLine.setNewCollectionRecord(true);
780 
781 			// if we add back add button on sub collection of an "add line" we
782 			// may need extra logic here
783 
784 			// get the collection from the business object
785 			Collection maintCollection = (Collection) ObjectUtils.getPropertyValue(getBusinessObject(), collectionName);
786 			// add the line to the collection
787 			maintCollection.add(addLine);
788 			// refresh parent object since attributes could of changed prior to
789 			// user clicking add
790 
791 			String referencesToRefresh = LookupUtils
792 					.convertReferencesToSelectCollectionToString(getAllRefreshableReferences(getBusinessObject()
793 							.getClass()));
794 			if (LOG.isInfoEnabled()) {
795 				LOG.info("References to refresh for adding line to collection " + collectionName + ": "
796 						+ referencesToRefresh);
797 			}
798 			refreshReferences(referencesToRefresh);
799 		}
800 
801 		initNewCollectionLine(collectionName);
802 
803 	}
804 
805 	public PersistableBusinessObject getNewCollectionLine(String collectionName) {
806 		if (LOG.isDebugEnabled()) {
807 			// LOG.debug( this + ") getNewCollectionLine( " + collectionName +
808 			// ")", new Exception( "tracing exception") );
809 			LOG.debug("newCollectionLines: " + newCollectionLines);
810 		}
811 		PersistableBusinessObject addLine = newCollectionLines.get(collectionName);
812 		if (addLine == null) {
813 			addLine = initNewCollectionLine(collectionName);
814 		}
815 		return addLine;
816 	}
817 
818 	public PersistableBusinessObject initNewCollectionLine(String collectionName) {
819 		if (LOG.isDebugEnabled()) {
820 			LOG.debug("initNewCollectionLine( " + collectionName + " )");
821 		}
822 		// try to get the object from the map
823 		// BusinessObject addLine = newCollectionLines.get( collectionName );
824 		// if ( addLine == null ) {
825 		// if not there, instantiate a new one
826 		PersistableBusinessObject addLine;
827 		try {
828 			addLine = (PersistableBusinessObject) getMaintenanceDocumentDictionaryService()
829 					.getCollectionBusinessObjectClass(getDocumentTypeName(), collectionName).newInstance();
830 		}
831 		catch (Exception ex) {
832 			LOG.error("unable to instantiate new collection line", ex);
833 			throw new RuntimeException("unable to instantiate new collection line", ex);
834 		}
835 		// and add it to the map
836 		newCollectionLines.put(collectionName, addLine);
837 		// }
838 		// set its values to the defaults
839 		setNewCollectionLineDefaultValues(collectionName, addLine);
840 		return addLine;
841 	}
842 
843 	/**
844 	 * 
845 	 * @see Maintainable#populateNewCollectionLines(java.util.Map)
846 	 */
847 	public Map<String, String> populateNewCollectionLines(Map<String, String> fieldValues,
848 			MaintenanceDocument maintenanceDocument, String methodToCall) {
849 		if (LOG.isDebugEnabled()) {
850 			LOG.debug("populateNewCollectionLines: " + fieldValues);
851 		}
852 		fieldValues = decryptEncryptedData(fieldValues, maintenanceDocument, methodToCall);
853 
854 		Map<String, String> cachedValues = new HashMap<String, String>();
855 
856 		// loop over all collections with an enabled add line
857 		List<MaintainableCollectionDefinition> collections = getMaintenanceDocumentDictionaryService()
858 				.getMaintainableCollections(getDocumentTypeName());
859 
860 		for (MaintainableCollectionDefinition coll : collections) {
861 			// get the collection name
862 			String collName = coll.getName();
863 			if (LOG.isDebugEnabled()) {
864 				LOG.debug("checking for collection: " + collName);
865 			}
866 			// build a map for that collection
867 			Map<String, String> collectionValues = new HashMap<String, String>();
868 			Map<String, String> subCollectionValues = new HashMap<String, String>();
869 			// loop over the collection, extracting entries with a matching
870 			// prefix
871 			for (Map.Entry<String, String> entry : fieldValues.entrySet()) {
872 				String key = entry.getKey();
873 				if (key.startsWith(collName)) {
874 					String subStrKey = key.substring(collName.length() + 1);
875 					// check for subcoll w/ '[', set collName to propername and
876 					// put in correct name for collection values (i.e. strip
877 					// '*[x].')
878 					if (key.contains("[")) {
879 
880 						// collName = StringUtils.substringBeforeLast(key,"[");
881 
882 						// need the whole thing if subcollection
883 						subCollectionValues.put(key, entry.getValue());
884 					}
885 					else {
886 						collectionValues.put(subStrKey, entry.getValue());
887 					}
888 				}
889 			}
890 			// send those values to the business object
891 			if (LOG.isDebugEnabled()) {
892 				LOG.debug("values for collection: " + collectionValues);
893 			}
894 			cachedValues.putAll(FieldUtils.populateBusinessObjectFromMap(getNewCollectionLine(collName),
895 					collectionValues, KRADConstants.MAINTENANCE_ADD_PREFIX + collName + "."));
896 			performFieldForceUpperCase(getNewCollectionLine(collName), collectionValues);
897 
898 			cachedValues.putAll(populateNewSubCollectionLines(coll, subCollectionValues));
899 		}
900 
901 		// cachedValues.putAll( FieldUtils.populateBusinessObjectFromMap( ))
902 		return cachedValues;
903 	}
904 
905 	/*
906 	 * Yes, I think this could be merged with the above code - I'm leaving it
907 	 * separate until I figure out of there are any issues which would reqire
908 	 * that it be separated.
909 	 */
910 	protected Map populateNewSubCollectionLines(MaintainableCollectionDefinition parentCollection, Map fieldValues) {
911 		if (LOG.isDebugEnabled()) {
912 			LOG.debug("populateNewSubCollectionLines: " + fieldValues);
913 		}
914 		Map cachedValues = new HashMap();
915 
916 		for (MaintainableCollectionDefinition coll : parentCollection.getMaintainableCollections()) {
917 			// get the collection name
918 			String collName = coll.getName();
919 
920 			if (LOG.isDebugEnabled()) {
921 				LOG.debug("checking for sub collection: " + collName);
922 			}
923 			Map<String, String> parents = new HashMap<String, String>();
924 			// get parents from list
925 			for (Object entry : fieldValues.entrySet()) {
926 				String key = (String) ((Map.Entry) entry).getKey();
927 				if (key.contains(collName)) {
928 					parents.put(StringUtils.substringBefore(key, "."), "");
929 				}
930 			}
931 
932 			for (String parent : parents.keySet()) {
933 				// build a map for that collection
934 				Map<String, Object> collectionValues = new HashMap<String, Object>();
935 				// loop over the collection, extracting entries with a matching
936 				// prefix
937 				for (Object entry : fieldValues.entrySet()) {
938 					String key = (String) ((Map.Entry) entry).getKey();
939 					if (key.contains(parent)) {
940 						String substr = StringUtils.substringAfterLast(key, ".");
941 						collectionValues.put(substr, ((Map.Entry) entry).getValue());
942 					}
943 				}
944 				// send those values to the business object
945 				if (LOG.isDebugEnabled()) {
946 					LOG.debug("values for sub collection: " + collectionValues);
947 				}
948 				GlobalVariables.getMessageMap().addToErrorPath(
949 						KRADConstants.MAINTENANCE_ADD_PREFIX + parent + "." + collName);
950 				cachedValues.putAll(FieldUtils.populateBusinessObjectFromMap(getNewCollectionLine(parent + "."
951 						+ collName), collectionValues, KRADConstants.MAINTENANCE_ADD_PREFIX + parent + "." + collName
952 						+ "."));
953 				performFieldForceUpperCase(getNewCollectionLine(parent + "." + collName), collectionValues);
954 				GlobalVariables.getMessageMap().removeFromErrorPath(
955 						KRADConstants.MAINTENANCE_ADD_PREFIX + parent + "." + collName);
956 			}
957 
958 			cachedValues.putAll(populateNewSubCollectionLines(coll, fieldValues));
959 		}
960 
961 		return cachedValues;
962 	}
963 
964 	public Collection<String> getAffectedReferencesFromLookup(BusinessObject baseBO, String attributeName,
965 			String collectionPrefix) {
966 		PersistenceStructureService pss = getPersistenceStructureService();
967 		String nestedBOPrefix = "";
968 		if (ObjectUtils.isNestedAttribute(attributeName)) {
969 			// if we're performing a lookup on a nested attribute, we need to
970 			// use the nested BO all the way down the chain
971 			nestedBOPrefix = ObjectUtils.getNestedAttributePrefix(attributeName);
972 
973 			// renormalize the base BO so that the attribute name is not nested
974 			// anymore
975 			Class reference = ObjectUtils.getPropertyType(baseBO, nestedBOPrefix, pss);
976 			if (!(PersistableBusinessObject.class.isAssignableFrom(reference))) {
977 				return new ArrayList<String>();
978 			}
979 
980 			try {
981 				baseBO = (PersistableBusinessObject) reference.newInstance();
982 			}
983 			catch (InstantiationException e) {
984 				LOG.error(e);
985 			}
986 			catch (IllegalAccessException e) {
987 				LOG.error(e);
988 			}
989 			attributeName = ObjectUtils.getNestedAttributePrimitive(attributeName);
990 		}
991 
992 		if (baseBO == null) {
993 			return new ArrayList<String>();
994 		}
995 
996 		Map<String, Class> referenceNameToClassFromPSS = LookupUtils.getPrimitiveReference(baseBO, attributeName);
997 		if (referenceNameToClassFromPSS.size() > 1) {
998 			LOG.error("LookupUtils.getPrimitiveReference return results should only have at most one element");
999 		}
1000 
1001 		BusinessObjectMetaDataService businessObjectMetaDataService = getBusinessObjectMetaDataService();
1002 		DataObjectRelationship relationship = businessObjectMetaDataService.getBusinessObjectRelationship(baseBO,
1003 				attributeName);
1004 		if (relationship == null) {
1005 			return new ArrayList<String>();
1006 		}
1007 
1008 		Map<String, String> fkToPkMappings = relationship.getParentToChildReferences();
1009 
1010 		Collection<String> affectedReferences = generateAllAffectedReferences(baseBO.getClass(), fkToPkMappings,
1011 				nestedBOPrefix, collectionPrefix);
1012 		if (LOG.isDebugEnabled()) {
1013 			LOG.debug("References affected by a lookup on BO attribute \"" + collectionPrefix + nestedBOPrefix + "."
1014 					+ attributeName + ": " + affectedReferences);
1015 		}
1016 
1017 		return affectedReferences;
1018 	}
1019 
1020 	protected boolean isRelationshipRefreshable(Class boClass, String relationshipName) {
1021 		if (getPersistenceStructureService().isPersistable(boClass)) {
1022 			if (getPersistenceStructureService().hasCollection(boClass, relationshipName)) {
1023 				return !getPersistenceStructureService().isCollectionUpdatable(boClass, relationshipName);
1024 			}
1025 			else if (getPersistenceStructureService().hasReference(boClass, relationshipName)) {
1026 				return !getPersistenceStructureService().isReferenceUpdatable(boClass, relationshipName);
1027 			}
1028 			// else, assume that the relationship is defined in the DD
1029 		}
1030 
1031 		return true;
1032 	}
1033 
1034 	protected Collection<String> generateAllAffectedReferences(Class boClass, Map<String, String> fkToPkMappings,
1035 			String nestedBOPrefix, String collectionPrefix) {
1036 		Set<String> allAffectedReferences = new HashSet<String>();
1037 		DataDictionaryService dataDictionaryService = getDataDictionaryService();
1038 		PersistenceStructureService pss = getPersistenceStructureService();
1039 
1040 		collectionPrefix = StringUtils.isBlank(collectionPrefix) ? "" : collectionPrefix;
1041 
1042 		// retrieve the attributes that are affected by a lookup on
1043 		// attributeName.
1044 		Collection<String> attributeReferenceFKAttributes = fkToPkMappings.keySet();
1045 
1046 		// a lookup on an attribute may cause other attributes to be updated
1047 		// (e.g. account code lookup would also affect chart code)
1048 		// build a list of all affected FK values via mapKeyFields above, and
1049 		// for each FK, see if there are any non-updatable references with that
1050 		// FK
1051 
1052 		// deal with regular simple references (<reference-descriptor>s in OJB)
1053 		for (String fkAttribute : attributeReferenceFKAttributes) {
1054 			for (String affectedReference : pss.getReferencesForForeignKey(boClass, fkAttribute).keySet()) {
1055 				if (isRelationshipRefreshable(boClass, affectedReference)) {
1056 					if (StringUtils.isBlank(nestedBOPrefix)) {
1057 						allAffectedReferences.add(collectionPrefix + affectedReference);
1058 					}
1059 					else {
1060 						allAffectedReferences.add(collectionPrefix + nestedBOPrefix + "." + affectedReference);
1061 					}
1062 				}
1063 			}
1064 		}
1065 
1066 		// now with collection references (<collection-descriptor>s in OJB)
1067 		for (String collectionName : pss.listCollectionObjectTypes(boClass).keySet()) {
1068 			if (isRelationshipRefreshable(boClass, collectionName)) {
1069 				Map<String, String> keyMappingsForCollection = pss.getInverseForeignKeysForCollection(boClass,
1070 						collectionName);
1071 				for (String collectionForeignKey : keyMappingsForCollection.keySet()) {
1072 					if (attributeReferenceFKAttributes.contains(collectionForeignKey)) {
1073 						if (StringUtils.isBlank(nestedBOPrefix)) {
1074 							allAffectedReferences.add(collectionPrefix + collectionName);
1075 						}
1076 						else {
1077 							allAffectedReferences.add(collectionPrefix + nestedBOPrefix + "." + collectionName);
1078 						}
1079 					}
1080 				}
1081 			}
1082 		}
1083 
1084 		// now use the DD to compute more affected references
1085 		List<String> ddDefinedRelationships = dataDictionaryService.getRelationshipNames(boClass.getName());
1086 		for (String ddRelationship : ddDefinedRelationships) {
1087 			// note that this map is PK (key/target) => FK (value/source)
1088 			Map<String, String> referencePKtoFKmappings = dataDictionaryService.getRelationshipAttributeMap(
1089 					boClass.getName(), ddRelationship);
1090 			for (String sourceAttribute : referencePKtoFKmappings.values()) {
1091 				// the sourceAttribute is the FK pointing to the target
1092 				// attribute (PK)
1093 				if (attributeReferenceFKAttributes.contains(sourceAttribute)) {
1094 					for (String affectedReference : dataDictionaryService.getRelationshipEntriesForSourceAttribute(
1095 							boClass.getName(), sourceAttribute)) {
1096 						if (isRelationshipRefreshable(boClass, ddRelationship)) {
1097 							if (StringUtils.isBlank(nestedBOPrefix)) {
1098 								allAffectedReferences.add(affectedReference);
1099 							}
1100 							else {
1101 								allAffectedReferences.add(nestedBOPrefix + "." + affectedReference);
1102 							}
1103 						}
1104 					}
1105 				}
1106 			}
1107 		}
1108 		return allAffectedReferences;
1109 	}
1110 
1111 	protected Collection<String> getAllRefreshableReferences(Class boClass) {
1112 		HashSet<String> references = new HashSet<String>();
1113 		for (String referenceName : getPersistenceStructureService().listReferenceObjectFields(boClass).keySet()) {
1114 			if (isRelationshipRefreshable(boClass, referenceName)) {
1115 				references.add(referenceName);
1116 			}
1117 		}
1118 		for (String collectionName : getPersistenceStructureService().listCollectionObjectTypes(boClass).keySet()) {
1119 			if (isRelationshipRefreshable(boClass, collectionName)) {
1120 				references.add(collectionName);
1121 			}
1122 		}
1123 		for (String relationshipName : getDataDictionaryService().getRelationshipNames(boClass.getName())) {
1124 			if (isRelationshipRefreshable(boClass, relationshipName)) {
1125 				references.add(relationshipName);
1126 			}
1127 		}
1128 		return references;
1129 	}
1130 
1131 	protected void setNewCollectionLineDefaultValues(String collectionName, PersistableBusinessObject addLine) {
1132 		PropertyDescriptor[] descriptors = PropertyUtils.getPropertyDescriptors(addLine);
1133 		for (int i = 0; i < descriptors.length; ++i) {
1134 			PropertyDescriptor propertyDescriptor = descriptors[i];
1135 
1136 			String fieldName = propertyDescriptor.getName();
1137 			Class propertyType = propertyDescriptor.getPropertyType();
1138 			String value = getMaintenanceDocumentDictionaryService().getCollectionFieldDefaultValue(getDocumentTypeName(),
1139 					collectionName, fieldName);
1140 
1141 			if (value != null) {
1142 				try {
1143 					ObjectUtils.setObjectProperty(addLine, fieldName, propertyType, value);
1144 				}
1145 				catch (Exception ex) {
1146 					LOG.error("Unable to set default property of collection object: " + "\nobject: " + addLine
1147 							+ "\nfieldName=" + fieldName + "\npropertyType=" + propertyType + "\nvalue=" + value, ex);
1148 				}
1149 			}
1150 
1151 		}
1152 	}
1153 
1154 	/**
1155 	 * @see Maintainable#clearBusinessObjectOfRestrictedValues(org.kuali.rice.kns.document.authorization.MaintenanceDocumentRestrictions)
1156 	 */
1157 	public void clearBusinessObjectOfRestrictedValues(MaintenanceDocumentRestrictions maintenanceDocumentRestrictions) {
1158 		List<MaintainableSectionDefinition> sections = getMaintenanceDocumentDictionaryService()
1159 				.getMaintainableSections(getDocumentTypeName());
1160 		for (MaintainableSectionDefinition sectionDefinition : sections) {
1161 			for (MaintainableItemDefinition itemDefinition : sectionDefinition.getMaintainableItems()) {
1162 				if (itemDefinition instanceof MaintainableFieldDefinition) {
1163 					clearFieldRestrictedValues("", businessObject, (MaintainableFieldDefinition) itemDefinition,
1164 							maintenanceDocumentRestrictions);
1165 				}
1166 				else if (itemDefinition instanceof MaintainableCollectionDefinition) {
1167 					clearCollectionRestrictedValues("", businessObject,
1168 							(MaintainableCollectionDefinition) itemDefinition, maintenanceDocumentRestrictions);
1169 				}
1170 			}
1171 		}
1172 	}
1173 
1174 	protected void clearCollectionRestrictedValues(String fieldNamePrefix, BusinessObject businessObject,
1175 			MaintainableCollectionDefinition collectionDefinition,
1176 			MaintenanceDocumentRestrictions maintenanceDocumentRestrictions) {
1177 		String collectionName = fieldNamePrefix + collectionDefinition.getName();
1178 		Collection<BusinessObject> collection = (Collection<BusinessObject>) ObjectUtils.getPropertyValue(
1179 				businessObject, collectionDefinition.getName());
1180 
1181 		if (collection != null) {
1182 			int i = 0;
1183 			// even though it's technically a Collection, we're going to index
1184 			// it like a list
1185 			for (BusinessObject collectionItem : collection) {
1186 				String collectionItemNamePrefix = collectionName + "[" + i + "].";
1187 				for (MaintainableCollectionDefinition subCollectionDefinition : collectionDefinition
1188 						.getMaintainableCollections()) {
1189 					clearCollectionRestrictedValues(collectionItemNamePrefix, collectionItem, subCollectionDefinition,
1190 							maintenanceDocumentRestrictions);
1191 				}
1192 				for (MaintainableFieldDefinition fieldDefinition : collectionDefinition.getMaintainableFields()) {
1193 					clearFieldRestrictedValues(collectionItemNamePrefix, collectionItem, fieldDefinition,
1194 							maintenanceDocumentRestrictions);
1195 				}
1196 				i++;
1197 			}
1198 		}
1199 	}
1200 
1201 	protected void clearFieldRestrictedValues(String fieldNamePrefix, BusinessObject businessObject,
1202 			MaintainableFieldDefinition fieldDefinition, MaintenanceDocumentRestrictions maintenanceDocumentRestrictions) {
1203 		String fieldName = fieldNamePrefix + fieldDefinition.getName();
1204 
1205 		FieldRestriction fieldRestriction = maintenanceDocumentRestrictions.getFieldRestriction(fieldName);
1206 		if (fieldRestriction.isRestricted()) {
1207 			String defaultValue = null;
1208 			if (StringUtils.isNotBlank(fieldDefinition.getDefaultValue())) {
1209 				defaultValue = fieldDefinition.getDefaultValue();
1210 			}
1211 			else if (fieldDefinition.getDefaultValueFinderClass() != null) {
1212 				try {
1213 					defaultValue = ((ValueFinder) fieldDefinition.getDefaultValueFinderClass().newInstance())
1214 							.getValue();
1215 				}
1216 				catch (Exception e) {
1217 					defaultValue = null;
1218 					LOG.error("Error trying to instantiate ValueFinder or to determine ValueFinder for doc type: "
1219 							+ getDocumentTypeName() + " field name " + fieldDefinition.getName() + " with field prefix: "
1220 							+ fieldNamePrefix, e);
1221 				}
1222 			}
1223 			try {
1224 				ObjectUtils.setObjectProperty(businessObject, fieldDefinition.getName(), defaultValue);
1225 			}
1226 			catch (Exception e) {
1227 				// throw an exception, because we don't want users to be able to
1228 				// see the restricted value
1229 				LOG.error("Unable to clear maintenance document values for field name: " + fieldName
1230 						+ " default value: " + defaultValue, e);
1231 				throw new RuntimeException("Unable to clear maintenance document values for field name: " + fieldName,
1232 						e);
1233 			}
1234 		}
1235 	}
1236 
1237 	protected void performForceUpperCase(Map fieldValues) {
1238 		List<MaintainableSectionDefinition> sections = getMaintenanceDocumentDictionaryService()
1239 				.getMaintainableSections(getDocumentTypeName());
1240 		for (MaintainableSectionDefinition sectionDefinition : sections) {
1241 			for (MaintainableItemDefinition itemDefinition : sectionDefinition.getMaintainableItems()) {
1242 				if (itemDefinition instanceof MaintainableFieldDefinition) {
1243 					performFieldForceUpperCase("", businessObject, (MaintainableFieldDefinition) itemDefinition,
1244 							fieldValues);
1245 				}
1246 				else if (itemDefinition instanceof MaintainableCollectionDefinition) {
1247 					performCollectionForceUpperCase("", businessObject,
1248 							(MaintainableCollectionDefinition) itemDefinition, fieldValues);
1249 
1250 				}
1251 			}
1252 		}
1253 	}
1254 
1255 	protected void performFieldForceUpperCase(String fieldNamePrefix, BusinessObject bo,
1256 			MaintainableFieldDefinition fieldDefinition, Map fieldValues) {
1257 		MessageMap errorMap = GlobalVariables.getMessageMap();
1258 		String fieldName = fieldDefinition.getName();
1259 		String mapKey = fieldNamePrefix + fieldName;
1260 		if (fieldValues != null && fieldValues.get(mapKey) != null) {
1261 			if (PropertyUtils.isWriteable(bo, fieldName) && ObjectUtils.getNestedValue(bo, fieldName) != null) {
1262 
1263 				try {
1264 					Class type = ObjectUtils.easyGetPropertyType(bo, fieldName);
1265 					// convert to upperCase based on data dictionary
1266 					Class businessObjectClass = bo.getClass();
1267 					boolean upperCase = false;
1268 					try {
1269 						upperCase = getDataDictionaryService().getAttributeForceUppercase(businessObjectClass,
1270 								fieldName);
1271 					}
1272 					catch (UnknownBusinessClassAttributeException t) {
1273 						boolean catchme = true;
1274 						// throw t;
1275 					}
1276 
1277 					Object fieldValue = ObjectUtils.getNestedValue(bo, fieldName);
1278 
1279 					if (upperCase && fieldValue instanceof String) {
1280 						fieldValue = ((String) fieldValue).toUpperCase();
1281 					}
1282 					ObjectUtils.setObjectProperty(bo, fieldName, type, fieldValue);
1283 				}
1284 				catch (FormatException e) {
1285 					errorMap.putError(fieldName, e.getErrorKey(), e.getErrorArgs());
1286 				}
1287 				catch (IllegalAccessException e) {
1288 					LOG.error("unable to populate business object" + e.getMessage());
1289 					throw new RuntimeException(e.getMessage(), e);
1290 				}
1291 				catch (InvocationTargetException e) {
1292 					LOG.error("unable to populate business object" + e.getMessage());
1293 					throw new RuntimeException(e.getMessage(), e);
1294 				}
1295 				catch (NoSuchMethodException e) {
1296 					LOG.error("unable to populate business object" + e.getMessage());
1297 					throw new RuntimeException(e.getMessage(), e);
1298 				}
1299 			}
1300 		}
1301 	}
1302 
1303 	protected void performCollectionForceUpperCase(String fieldNamePrefix, BusinessObject bo,
1304 			MaintainableCollectionDefinition collectionDefinition, Map fieldValues) {
1305 		String collectionName = fieldNamePrefix + collectionDefinition.getName();
1306 		Collection<BusinessObject> collection = (Collection<BusinessObject>) ObjectUtils.getPropertyValue(bo,
1307 				collectionDefinition.getName());
1308 		if (collection != null) {
1309 			int i = 0;
1310 			// even though it's technically a Collection, we're going to index
1311 			// it like a list
1312 			for (BusinessObject collectionItem : collection) {
1313 				String collectionItemNamePrefix = collectionName + "[" + i + "].";
1314 				// String collectionItemNamePrefix = "";
1315 				for (MaintainableFieldDefinition fieldDefinition : collectionDefinition.getMaintainableFields()) {
1316 					performFieldForceUpperCase(collectionItemNamePrefix, collectionItem, fieldDefinition, fieldValues);
1317 				}
1318 				for (MaintainableCollectionDefinition subCollectionDefinition : collectionDefinition
1319 						.getMaintainableCollections()) {
1320 					performCollectionForceUpperCase(collectionItemNamePrefix, collectionItem, subCollectionDefinition,
1321 							fieldValues);
1322 				}
1323 				i++;
1324 			}
1325 		}
1326 	}
1327 
1328 	protected void performFieldForceUpperCase(BusinessObject bo, Map fieldValues) {
1329 		MessageMap errorMap = GlobalVariables.getMessageMap();
1330 
1331 		try {
1332 			for (Iterator iter = fieldValues.keySet().iterator(); iter.hasNext();) {
1333 				String propertyName = (String) iter.next();
1334 
1335 				if (PropertyUtils.isWriteable(bo, propertyName) && fieldValues.get(propertyName) != null) {
1336 					// if the field propertyName is a valid property on the bo
1337 					// class
1338 					Class type = ObjectUtils.easyGetPropertyType(bo, propertyName);
1339 					try {
1340 						// Keep the convert to upperCase logic here. It will be
1341 						// used in populateNewCollectionLines,
1342 						// populateNewSubCollectionLines
1343 						// convert to upperCase based on data dictionary
1344 						Class businessObjectClass = bo.getClass();
1345 						boolean upperCase = false;
1346 						try {
1347 							upperCase = getDataDictionaryService().getAttributeForceUppercase(businessObjectClass,
1348 									propertyName);
1349 						}
1350 						catch (UnknownBusinessClassAttributeException t) {
1351 							boolean catchme = true;
1352 							// throw t;
1353 						}
1354 
1355 						Object fieldValue = fieldValues.get(propertyName);
1356 
1357 						if (upperCase && fieldValue instanceof String) {
1358 							fieldValue = ((String) fieldValue).toUpperCase();
1359 						}
1360 						ObjectUtils.setObjectProperty(bo, propertyName, type, fieldValue);
1361 					}
1362 					catch (FormatException e) {
1363 						errorMap.putError(propertyName, e.getErrorKey(), e.getErrorArgs());
1364 					}
1365 				}
1366 			}
1367 		}
1368 		catch (IllegalAccessException e) {
1369 			LOG.error("unable to populate business object" + e.getMessage());
1370 			throw new RuntimeException(e.getMessage(), e);
1371 		}
1372 		catch (InvocationTargetException e) {
1373 			LOG.error("unable to populate business object" + e.getMessage());
1374 			throw new RuntimeException(e.getMessage(), e);
1375 		}
1376 		catch (NoSuchMethodException e) {
1377 			LOG.error("unable to populate business object" + e.getMessage());
1378 			throw new RuntimeException(e.getMessage(), e);
1379 		}
1380 
1381 	}
1382 
1383 	/**
1384 	 * By default a maintainable is not external
1385 	 * 
1386 	 * @see Maintainable#isExternalBusinessObject()
1387 	 */
1388 	public boolean isExternalBusinessObject() {
1389 		return false;
1390 	}
1391 
1392 	/**
1393 	 * @see Maintainable#getExternalBusinessObject()
1394 	 */
1395 	public void prepareBusinessObject(BusinessObject businessObject) {
1396 		// Do nothing by default
1397 	}
1398 
1399 	// 3070
1400 	public void deleteBusinessObject() {
1401 		if (businessObject == null)
1402 			return;
1403 
1404 		KRADServiceLocator.getBusinessObjectService().delete(businessObject);
1405 		businessObject = null;
1406 	}
1407 
1408 	public boolean isOldBusinessObjectInDocument() {
1409 		return super.isOldDataObjectInDocument();
1410 	}
1411 
1412 	protected PersistenceStructureService getPersistenceStructureService() {
1413 		if (persistenceStructureService == null) {
1414 			persistenceStructureService = KRADServiceLocator.getPersistenceStructureService();
1415 		}
1416 		return persistenceStructureService;
1417 	}
1418 
1419 	protected BusinessObjectDictionaryService getBusinessObjectDictionaryService() {
1420 		if (businessObjectDictionaryService == null) {
1421 			businessObjectDictionaryService = KRADServiceLocatorWeb.getBusinessObjectDictionaryService();
1422 		}
1423 		return businessObjectDictionaryService;
1424 	}
1425 
1426 	protected PersonService getPersonService() {
1427 		if (personService == null) {
1428 			personService = KimApiServiceLocator.getPersonService();
1429 		}
1430 		return personService;
1431 	}
1432 
1433 	protected BusinessObjectMetaDataService getBusinessObjectMetaDataService() {
1434 		if (businessObjectMetaDataService == null) {
1435 			businessObjectMetaDataService = KNSServiceLocator.getBusinessObjectMetaDataService();
1436 		}
1437 		return businessObjectMetaDataService;
1438 	}
1439 
1440 	protected BusinessObjectAuthorizationService getBusinessObjectAuthorizationService() {
1441 		if (businessObjectAuthorizationService == null) {
1442 			businessObjectAuthorizationService = KNSServiceLocator.getBusinessObjectAuthorizationService();
1443 		}
1444 		return businessObjectAuthorizationService;
1445 	}
1446 
1447 	protected DocumentHelperService getDocumentHelperService() {
1448 		if (documentHelperService == null) {
1449 			documentHelperService = KRADServiceLocatorWeb.getDocumentHelperService();
1450 		}
1451 		return documentHelperService;
1452 	}
1453 
1454 	public void setPersistenceStructureService(PersistenceStructureService persistenceStructureService) {
1455 		this.persistenceStructureService = persistenceStructureService;
1456 	}
1457 
1458 	public void setBusinessObjectDictionaryService(BusinessObjectDictionaryService businessObjectDictionaryService) {
1459 		this.businessObjectDictionaryService = businessObjectDictionaryService;
1460 	}
1461 
1462 	public void setPersonService(PersonService personService) {
1463 		this.personService = personService;
1464 	}
1465 
1466 	public void setBusinessObjectMetaDataService(BusinessObjectMetaDataService businessObjectMetaDataService) {
1467 		this.businessObjectMetaDataService = businessObjectMetaDataService;
1468 	}
1469 
1470 	public void setBusinessObjectAuthorizationService(
1471 			BusinessObjectAuthorizationService businessObjectAuthorizationService) {
1472 		this.businessObjectAuthorizationService = businessObjectAuthorizationService;
1473 	}
1474 
1475 	public void setDocumentHelperService(DocumentHelperService documentHelperService) {
1476 		this.documentHelperService = documentHelperService;
1477 	}
1478 
1479     public MaintenanceDocumentDictionaryService getMaintenanceDocumentDictionaryService() {
1480         if (maintenanceDocumentDictionaryService == null) {
1481             this.maintenanceDocumentDictionaryService = KNSServiceLocator.getMaintenanceDocumentDictionaryService();
1482         }
1483         return maintenanceDocumentDictionaryService;
1484     }
1485 
1486     public void setMaintenanceDocumentDictionaryService(
1487             MaintenanceDocumentDictionaryService maintenanceDocumentDictionaryService) {
1488         this.maintenanceDocumentDictionaryService = maintenanceDocumentDictionaryService;
1489     }
1490 }