View Javadoc

1   
2   /*
3    * Copyright 2005-2007 The Kuali Foundation
4    * 
5    * Licensed under the Educational Community License, Version 2.0 (the "License");
6    * you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    * 
9    * http://www.opensource.org/licenses/ecl2.php
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.kuali.rice.kns.web.struts.action;
18  
19  import org.apache.commons.beanutils.PropertyUtils;
20  import org.apache.commons.lang.StringUtils;
21  import org.apache.ojb.broker.metadata.ClassNotPersistenceCapableException;
22  import org.apache.struts.action.ActionForm;
23  import org.apache.struts.action.ActionForward;
24  import org.apache.struts.action.ActionMapping;
25  import org.kuali.rice.core.api.CoreApiServiceLocator;
26  import org.kuali.rice.core.api.encryption.EncryptionService;
27  import org.kuali.rice.core.api.util.ClassLoaderUtils;
28  import org.kuali.rice.core.api.util.RiceConstants;
29  import org.kuali.rice.core.api.util.RiceKeyConstants;
30  import org.kuali.rice.core.framework.persistence.jpa.OrmUtils;
31  import org.kuali.rice.core.framework.persistence.jpa.criteria.Criteria;
32  import org.kuali.rice.core.framework.persistence.jpa.criteria.QueryByCriteria;
33  import org.kuali.rice.core.framework.persistence.jpa.metadata.EntityDescriptor;
34  import org.kuali.rice.core.framework.persistence.jpa.metadata.FieldDescriptor;
35  import org.kuali.rice.core.framework.persistence.jpa.metadata.MetadataManager;
36  import org.kuali.rice.core.web.format.Formatter;
37  import org.kuali.rice.kew.util.KEWConstants;
38  import org.kuali.rice.kim.api.identity.Person;
39  import org.kuali.rice.kns.datadictionary.MaintainableCollectionDefinition;
40  import org.kuali.rice.kns.datadictionary.MaintenanceDocumentEntry;
41  import org.kuali.rice.kns.document.MaintenanceDocument;
42  import org.kuali.rice.kns.document.MaintenanceDocumentBase;
43  import org.kuali.rice.kns.document.authorization.MaintenanceDocumentRestrictions;
44  import org.kuali.rice.kns.lookup.LookupResultsService;
45  import org.kuali.rice.kns.maintenance.Maintainable;
46  import org.kuali.rice.kns.rule.event.KualiAddLineEvent;
47  import org.kuali.rice.kns.service.KNSServiceLocator;
48  import org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService;
49  import org.kuali.rice.kns.util.KNSGlobalVariables;
50  import org.kuali.rice.kns.util.MaintenanceUtils;
51  import org.kuali.rice.kns.util.WebUtils;
52  import org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase;
53  import org.kuali.rice.kns.web.struts.form.KualiForm;
54  import org.kuali.rice.kns.web.struts.form.KualiMaintenanceForm;
55  import org.kuali.rice.krad.bo.DocumentAttachment;
56  import org.kuali.rice.krad.bo.PersistableAttachment;
57  import org.kuali.rice.krad.bo.PersistableBusinessObject;
58  import org.kuali.rice.krad.bo.PersistableBusinessObjectExtension;
59  import org.kuali.rice.krad.document.authorization.MaintenanceDocumentAuthorizer;
60  import org.kuali.rice.krad.exception.DocumentTypeAuthorizationException;
61  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
62  import org.kuali.rice.krad.service.LookupService;
63  import org.kuali.rice.krad.util.GlobalVariables;
64  import org.kuali.rice.krad.util.KRADConstants;
65  import org.kuali.rice.krad.util.KRADPropertyConstants;
66  import org.kuali.rice.krad.util.ObjectUtils;
67  
68  import javax.persistence.PersistenceException;
69  import javax.servlet.http.HttpServletRequest;
70  import javax.servlet.http.HttpServletResponse;
71  import java.lang.reflect.Field;
72  import java.lang.reflect.InvocationTargetException;
73  import java.security.GeneralSecurityException;
74  import java.util.ArrayList;
75  import java.util.Collection;
76  import java.util.Enumeration;
77  import java.util.HashMap;
78  import java.util.Iterator;
79  import java.util.List;
80  import java.util.Map;
81  
82  /**
83   * This class handles actions for maintenance documents. These include creating new edit, and copying of maintenance records.
84   */
85  public class KualiMaintenanceDocumentAction extends KualiDocumentActionBase {
86      protected static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(KualiMaintenanceDocumentAction.class);
87  
88      protected MaintenanceDocumentDictionaryService maintenanceDocumentDictionaryService = null;
89      protected EncryptionService encryptionService;
90      protected LookupService lookupService;
91      protected LookupResultsService lookupResultsService;
92  
93  	public KualiMaintenanceDocumentAction() {
94  		super();
95  		maintenanceDocumentDictionaryService = KNSServiceLocator.getMaintenanceDocumentDictionaryService();
96  		encryptionService = CoreApiServiceLocator.getEncryptionService();
97  	}
98  
99  	@Override
100 	public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {	
101 		request.setAttribute(KRADConstants.PARAM_MAINTENANCE_VIEW_MODE, KRADConstants.PARAM_MAINTENANCE_VIEW_MODE_MAINTENANCE);
102 		return super.execute(mapping, form, request, response);
103 	}
104 
105 	/**
106 	 * Calls setup Maintenance for new action.
107 	 */
108 	public ActionForward start(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
109 		request.setAttribute(KRADConstants.MAINTENANCE_ACTN, KRADConstants.MAINTENANCE_NEW_ACTION);
110 		return setupMaintenance(mapping, form, request, response, KRADConstants.MAINTENANCE_NEW_ACTION);
111 	}
112 
113 	/**
114 	 * Calls setupMaintenance for copy action.
115 	 */
116 	public ActionForward copy(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
117 		// check for copy document number
118 		if (request.getParameter("document." + KRADPropertyConstants.DOCUMENT_NUMBER) == null) { // object copy
119 			return setupMaintenance(mapping, form, request, response, KRADConstants.MAINTENANCE_COPY_ACTION);
120 		}
121 		else { // document copy
122 			throw new UnsupportedOperationException("System does not support copying of maintenance documents.");
123 		}
124 	}
125 
126 	/**
127 	 * Calls setupMaintenance for edit action.
128 	 */
129 	public ActionForward edit(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
130 	
131 		return setupMaintenance(mapping, form, request, response, KRADConstants.MAINTENANCE_EDIT_ACTION);
132 	}
133 
134 	/**
135 	 * KUALRice 3070 Calls setupMaintenance for delete action.
136 	 */
137 	public ActionForward delete(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
138 		if (isFormRepresentingLockObject((KualiDocumentFormBase)form)) {
139 			 return super.delete(mapping, form, request, response);
140 		}
141 		KNSGlobalVariables.getMessageList().add(RiceKeyConstants.MESSAGE_DELETE);
142 		return setupMaintenance(mapping, form, request, response, KRADConstants.MAINTENANCE_DELETE_ACTION);
143 	}
144 	
145 	/**
146 	 * Calls setupMaintenance for new object that have existing objects attributes.
147 	 */
148 	public ActionForward newWithExisting(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
149 		return setupMaintenance(mapping, form, request, response, KRADConstants.MAINTENANCE_NEWWITHEXISTING_ACTION);
150 	}
151 
152 	/**
153 	 * Gets a new document for a maintenance record. The maintainable is specified with the documentTypeName or business object
154 	 * class request parameter and request parameters are parsed for key values for retrieving the business object. Forward to the
155 	 * maintenance jsp which renders the page based on the maintainable's field specifications. Retrieves an existing business
156 	 * object for edit and copy. Checks locking on edit.
157 	 */
158     protected ActionForward setupMaintenance(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, String maintenanceAction) throws Exception {
159 		KualiMaintenanceForm maintenanceForm = (KualiMaintenanceForm) form;
160 		MaintenanceDocument document = null;
161 
162 		// create a new document object, if required (on NEW object, or other reasons)
163 		if (maintenanceForm.getDocument() == null) {
164 			if (StringUtils.isEmpty(maintenanceForm.getBusinessObjectClassName()) && StringUtils.isEmpty(maintenanceForm.getDocTypeName())) {
165 				throw new IllegalArgumentException("Document type name or bo class not given!");
166 			}
167 
168 			String documentTypeName = maintenanceForm.getDocTypeName();
169 			// get document type if not passed
170 			if (StringUtils.isEmpty(documentTypeName)) {
171 				documentTypeName = maintenanceDocumentDictionaryService.getDocumentTypeName(Class.forName(maintenanceForm.getBusinessObjectClassName()));
172 				maintenanceForm.setDocTypeName(documentTypeName);
173 			}
174 
175 			if (StringUtils.isEmpty(documentTypeName)) {
176 				throw new RuntimeException("documentTypeName is empty; does this Business Object have a maintenance document definition? " + maintenanceForm.getBusinessObjectClassName());
177 			}
178 
179 			// check doc type allows new or copy if that action was requested
180 			if (KRADConstants.MAINTENANCE_NEW_ACTION.equals(maintenanceAction) || KRADConstants.MAINTENANCE_COPY_ACTION.equals(maintenanceAction)) {
181 				Class boClass = maintenanceDocumentDictionaryService.getDataObjectClass(documentTypeName);
182 				boolean allowsNewOrCopy = getBusinessObjectAuthorizationService().canCreate(boClass, GlobalVariables.getUserSession().getPerson(), documentTypeName);
183 				if (!allowsNewOrCopy) {
184 					LOG.error("Document type " + documentTypeName + " does not allow new or copy actions.");
185 					throw new DocumentTypeAuthorizationException(GlobalVariables.getUserSession().getPerson().getPrincipalId(), "newOrCopy", documentTypeName);
186 				}
187 			}
188 
189 			// get new document from service
190 			document = (MaintenanceDocument) getDocumentService().getNewDocument(maintenanceForm.getDocTypeName());
191 			// Check for an auto-incrementing PK and set it if needed
192 			//            if (document.getNewMaintainableObject().getBoClass().isAnnotationPresent(Sequence.class)) {
193 			//    			Sequence sequence = (Sequence) document.getNewMaintainableObject().getBoClass().getAnnotation(Sequence.class);
194 			//    			Long pk = OrmUtils.getNextAutoIncValue(sequence);
195 			//    			OrmUtils.populateAutoIncValue(document.getOldMaintainableObject().getBusinessObject(), pk);
196 			//    			OrmUtils.populateAutoIncValue(document.getNewMaintainableObject().getBusinessObject(), pk);
197 			//    			document.getOldMaintainableObject().getBusinessObject().setAutoIncrementSet(true);
198 			//    			document.getNewMaintainableObject().getBusinessObject().setAutoIncrementSet(true);
199 			//            }
200 			maintenanceForm.setDocument(document);
201 		}
202 		else {
203 			document = (MaintenanceDocument) maintenanceForm.getDocument();
204 		}
205 
206 		// retrieve business object from request parameters
207 		if (!(KRADConstants.MAINTENANCE_NEW_ACTION.equals(maintenanceAction)) && !(KRADConstants.MAINTENANCE_NEWWITHEXISTING_ACTION.equals(maintenanceAction))) {
208 			Map requestParameters = buildKeyMapFromRequest(document.getNewMaintainableObject(), request);
209             PersistableBusinessObject oldBusinessObject = null;
210             try {
211             	oldBusinessObject = (PersistableBusinessObject) getLookupService().findObjectBySearch(Class.forName(maintenanceForm.getBusinessObjectClassName()), requestParameters);
212             } catch ( ClassNotPersistenceCapableException ex ) {
213             	if ( !document.getOldMaintainableObject().isExternalBusinessObject() ) {
214             		throw new RuntimeException( "BO Class: " + maintenanceForm.getBusinessObjectClassName() + " is not persistable and is not externalizable - configuration error" );
215             	}
216             	// otherwise, let fall through
217             }
218 			if (oldBusinessObject == null && !document.getOldMaintainableObject().isExternalBusinessObject()) {
219                 throw new RuntimeException("Cannot retrieve old record for maintenance document, incorrect parameters passed on maint url: " + requestParameters );
220 			} 
221 
222 			if(document.getOldMaintainableObject().isExternalBusinessObject()){
223             	if ( oldBusinessObject == null ) {
224             		try {
225             			oldBusinessObject = (PersistableBusinessObject)document.getOldMaintainableObject().getBoClass().newInstance();
226             		} catch ( Exception ex ) {
227             			throw new RuntimeException( "External BO maintainable was null and unable to instantiate for old maintainable object.", ex );
228             		}
229             	}
230 				populateBOWithCopyKeyValues(request, oldBusinessObject, document.getOldMaintainableObject());
231 				document.getOldMaintainableObject().prepareBusinessObject(oldBusinessObject);
232             	oldBusinessObject = document.getOldMaintainableObject().getBusinessObject();
233 			}
234 
235 			// Temp solution for loading extension objects - need to find a better way
236 			final String TMP_NM = oldBusinessObject.getClass().getName();
237 			final int START_INDEX = TMP_NM.indexOf('.', TMP_NM.indexOf('.') + 1) + 1;
238 			if ( ( OrmUtils.isJpaEnabled() || OrmUtils.isJpaEnabled(TMP_NM.substring(START_INDEX, TMP_NM.indexOf('.', TMP_NM.indexOf('.', START_INDEX) + 1))) ) &&
239 					OrmUtils.isJpaAnnotated(oldBusinessObject.getClass()) && oldBusinessObject.getExtension() != null && OrmUtils.isJpaAnnotated(oldBusinessObject.getExtension().getClass())) {
240 				if (oldBusinessObject.getExtension() != null) {
241 					PersistableBusinessObjectExtension boe = oldBusinessObject.getExtension();
242 					EntityDescriptor entity = MetadataManager.getEntityDescriptor(oldBusinessObject.getExtension().getClass());
243 					Criteria extensionCriteria = new Criteria(boe.getClass().getName());
244 					for (FieldDescriptor fieldDescriptor : entity.getPrimaryKeys()) {
245 						try {
246 							Field field = oldBusinessObject.getClass().getDeclaredField(fieldDescriptor.getName());
247 							field.setAccessible(true);
248 							extensionCriteria.eq(fieldDescriptor.getName(), field.get(oldBusinessObject));
249 						} catch (Exception e) {
250 							LOG.error(e.getMessage(),e);
251 						}
252 					}				
253 					try {
254 						boe = (PersistableBusinessObjectExtension) new QueryByCriteria(getEntityManagerFactory().createEntityManager(), extensionCriteria).toQuery().getSingleResult();
255 					} catch (PersistenceException e) {}
256 					oldBusinessObject.setExtension(boe);
257 				}
258 			}
259 
260 			PersistableBusinessObject newBusinessObject = (PersistableBusinessObject) ObjectUtils.deepCopy(oldBusinessObject);
261 
262 			// set business object instance for editing
263 			Class<? extends PersistableBusinessObject> businessObjectClass = ClassLoaderUtils.getClass(maintenanceForm.getBusinessObjectClassName(), PersistableBusinessObject.class); 
264 			document.getOldMaintainableObject().setBusinessObject(oldBusinessObject);
265 			document.getOldMaintainableObject().setBoClass(businessObjectClass);
266 			document.getNewMaintainableObject().setBusinessObject(newBusinessObject);
267 			document.getNewMaintainableObject().setBoClass(businessObjectClass);
268 
269 			// on a COPY, clear any fields that this user isnt authorized for, and also
270 			// clear the primary key fields
271 			if (KRADConstants.MAINTENANCE_COPY_ACTION.equals(maintenanceAction)) {
272 				if (!document.isFieldsClearedOnCopy()) {
273 					//for issue KULRice 3072
274 					Class boClass = maintenanceDocumentDictionaryService.getDataObjectClass(
275                             maintenanceForm.getDocTypeName());
276 					if(!maintenanceDocumentDictionaryService.getPreserveLockingKeysOnCopy(boClass))
277 						clearPrimaryKeyFields(document);
278 
279 					clearUnauthorizedNewFields(document);
280 
281 					Maintainable maintainable = document.getNewMaintainableObject();
282 
283 					maintainable.processAfterCopy( document, request.getParameterMap() );
284 
285 					// mark so that this clearing doesnt happen again
286 					document.setFieldsClearedOnCopy(true);
287 
288 					// mark so that blank required fields will be populated with default values
289 					maintainable.setGenerateBlankRequiredValues(maintenanceForm.getDocTypeName());
290 				}
291 			}
292 			else if (KRADConstants.MAINTENANCE_EDIT_ACTION.equals(maintenanceAction)) {
293 				boolean allowsEdit = getBusinessObjectAuthorizationService().canMaintain(oldBusinessObject, GlobalVariables.getUserSession().getPerson(), document.getDocumentHeader().getWorkflowDocument().getDocumentTypeName());
294 				if (!allowsEdit) {
295 					LOG.error("Document type " + document.getDocumentHeader().getWorkflowDocument().getDocumentTypeName() + " does not allow edit actions.");
296 					throw  new DocumentTypeAuthorizationException(GlobalVariables.getUserSession().getPerson().getPrincipalId(), "edit", document.getDocumentHeader().getWorkflowDocument().getDocumentTypeName());
297 				}
298 				document.getNewMaintainableObject().processAfterEdit( document, request.getParameterMap() );
299 			}
300 			//3070
301 			else if (KRADConstants.MAINTENANCE_DELETE_ACTION.equals(maintenanceAction)) {
302 				boolean allowsDelete = getBusinessObjectAuthorizationService().canMaintain(oldBusinessObject, GlobalVariables.getUserSession().getPerson(), document.getDocumentHeader().getWorkflowDocument().getDocumentTypeName());
303 				if (!allowsDelete) {
304 					LOG.error("Document type " + document.getDocumentHeader().getWorkflowDocument().getDocumentTypeName() + " does not allow delete actions.");
305 					throw  new DocumentTypeAuthorizationException(GlobalVariables.getUserSession().getPerson().getPrincipalId(), "delete", document.getDocumentHeader().getWorkflowDocument().getDocumentTypeName());
306 				}	
307 				//document.getNewMaintainableObject().processAfterEdit( document, request.getParameterMap() );
308 			}
309 			// Check for an auto-incrementing PK and set it if needed
310 			//            if (document.getNewMaintainableObject().getBoClass().isAnnotationPresent(Sequence.class)) {
311 			//    			Sequence sequence = (Sequence) document.getNewMaintainableObject().getBoClass().getAnnotation(Sequence.class);
312 			//    			Long pk = OrmUtils.getNextAutoIncValue(sequence);
313 			//    			OrmUtils.populateAutoIncValue(document.getNewMaintainableObject().getBusinessObject(), pk);
314 			//    			document.getNewMaintainableObject().getBusinessObject().setAutoIncrementSet(true);
315 			//            }
316 		}
317 		// if new with existing we need to populate we need to populate with passed in parameters
318 		if (KRADConstants.MAINTENANCE_NEWWITHEXISTING_ACTION.equals(maintenanceAction)) {
319 			// TODO: this code should be abstracted out into a helper
320 			// also is it a problem that we're not calling setGenerateDefaultValues? it blanked out the below values when I did
321 			// maybe we need a new generateDefaultValues that doesn't overwrite?
322 			PersistableBusinessObject newBO = document.getNewMaintainableObject().getBusinessObject();
323 			Map<String, String> parameters = buildKeyMapFromRequest(document.getNewMaintainableObject(), request);
324 			copyParametersToBO(parameters, newBO);
325 			newBO.refresh();
326 			document.getNewMaintainableObject().setupNewFromExisting( document, request.getParameterMap() );
327 		}
328 
329 		// for new maintainble need to pick up default values
330 		if (KRADConstants.MAINTENANCE_NEW_ACTION.equals(maintenanceAction)) {
331 			document.getNewMaintainableObject().setGenerateDefaultValues(maintenanceForm.getDocTypeName());
332 			document.getNewMaintainableObject().processAfterNew( document, request.getParameterMap() );
333 
334 			// If a maintenance lock exists, warn the user.
335 			MaintenanceUtils.checkForLockingDocument(document.getNewMaintainableObject(), false);
336 		}
337 
338 		// set maintenance action state
339 		document.getNewMaintainableObject().setMaintenanceAction(maintenanceAction);
340 		maintenanceForm.setMaintenanceAction(maintenanceAction);
341 
342 		// attach any extra JS from the data dictionary
343         MaintenanceDocumentEntry entry =  maintenanceDocumentDictionaryService.getMaintenanceDocumentEntry(document.getDocumentHeader().getWorkflowDocument().getDocumentTypeName());
344 		if (LOG.isDebugEnabled()) {
345 			LOG.debug("maintenanceForm.getAdditionalScriptFiles(): " + maintenanceForm.getAdditionalScriptFiles());
346 		}
347 		if (maintenanceForm.getAdditionalScriptFiles().isEmpty()) {
348 			maintenanceForm.getAdditionalScriptFiles().addAll(entry.getWebScriptFiles());
349 		}
350 
351 		// Retrieve notes topic display flag from data dictionary and add to document
352 		document.setDisplayTopicFieldInNotes(entry.getDisplayTopicFieldInNotes());
353 
354 		return mapping.findForward(RiceConstants.MAPPING_BASIC);
355 	}
356 
357     protected void populateBOWithCopyKeyValues(HttpServletRequest request, PersistableBusinessObject oldBusinessObject, Maintainable oldMaintainableObject) throws Exception{
358 		List keyFieldNamesToCopy = new ArrayList();
359 		Map<String, String> parametersToCopy;
360 		if (!StringUtils.isBlank(request.getParameter(KRADConstants.COPY_KEYS))) {
361 			String[] copyKeys = request.getParameter(KRADConstants.COPY_KEYS).split(KRADConstants.FIELD_CONVERSIONS_SEPARATOR);
362 			for (String copyKey: copyKeys) {
363 				keyFieldNamesToCopy.add(copyKey);
364 			}
365 		}
366 		parametersToCopy = getRequestParameters(keyFieldNamesToCopy, oldMaintainableObject, request);
367 		if(parametersToCopy!=null && parametersToCopy.size()>0){
368 			copyParametersToBO(parametersToCopy, oldBusinessObject);
369 		}
370 	}
371 
372     protected void copyParametersToBO(Map<String, String> parameters, PersistableBusinessObject newBO) throws Exception{
373 		for (String parmName : parameters.keySet()) {
374 			String propertyValue = parameters.get(parmName);
375 
376 			if (StringUtils.isNotBlank(propertyValue)) {
377 				String propertyName = parmName;
378 				// set value of property in bo
379 				if (PropertyUtils.isWriteable(newBO, propertyName)) {
380 					Class type = ObjectUtils.easyGetPropertyType(newBO, propertyName);
381 					if (type != null && Formatter.getFormatter(type) != null) {
382 						Formatter formatter = Formatter.getFormatter(type);
383 						Object obj = formatter.convertFromPresentationFormat(propertyValue);
384 						ObjectUtils.setObjectProperty(newBO, propertyName, obj.getClass(), obj);
385 					}
386 					else {
387 						ObjectUtils.setObjectProperty(newBO, propertyName, String.class, propertyValue);
388 					}
389 				}
390 			}
391 		}
392 	}
393 
394 	/**
395 	 * Downloads the attachment to the user's browser
396 	 *
397 	 * @param mapping
398 	 * @param form
399 	 * @param request
400 	 * @param response
401 	 * @return ActionForward
402 	 * @throws Exception
403 	 */
404 	public ActionForward downloadAttachment(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
405 		KualiDocumentFormBase documentForm = (KualiDocumentFormBase) form;
406 		MaintenanceDocumentBase document = (MaintenanceDocumentBase) documentForm.getDocument();
407 		document.refreshReferenceObject("attachment");
408 		DocumentAttachment attachment = document.getAttachment();
409 		if(attachment != null) {
410 			streamToResponse(attachment.getAttachmentContent(), attachment.getFileName(), attachment.getContentType(), response); 
411 		}
412 		return null;
413 	}
414 
415 
416 	/**
417 	 * 
418 	 * This method used to replace the attachment
419 	 * @param mapping
420 	 * @param form
421 	 * @param request
422 	 * @param response
423 	 * @return
424 	 * @throws Exception
425 	 */
426 	public ActionForward replaceAttachment(ActionMapping mapping, ActionForm form, HttpServletRequest request,
427 			HttpServletResponse response) throws Exception {
428 		KualiDocumentFormBase documentForm = (KualiDocumentFormBase) form;
429 		MaintenanceDocumentBase document = (MaintenanceDocumentBase) documentForm.getDocument();
430 		document.refreshReferenceObject("attachment");
431 		getBusinessObjectService().delete(document.getAttachment());
432 		return mapping.findForward(RiceConstants.MAPPING_BASIC);
433 	}
434 
435 	/**
436 	 * route the document using the document service
437 	 * 
438 	 * @param mapping
439 	 * @param form
440 	 * @param request
441 	 * @param response
442 	 * @return ActionForward
443 	 * @throws Exception
444 	 */
445 	@Override
446 	public ActionForward route(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
447 		KualiDocumentFormBase documentForm = (KualiDocumentFormBase) form;
448 		MaintenanceDocumentBase document = (MaintenanceDocumentBase) documentForm.getDocument();
449 
450 		ActionForward forward = super.route(mapping, form, request, response);
451 		if(document.getNewMaintainableObject().getBusinessObject() instanceof PersistableAttachment) {
452 			PersistableAttachment bo = (PersistableAttachment) getBusinessObjectService().retrieve(document.getNewMaintainableObject().getBusinessObject());
453 			request.setAttribute("fileName", bo.getFileName());
454 		}
455 
456 		return forward;
457 	}
458 
459 	/**
460 	 * Handles creating and loading of documents.
461 	 */
462 	@Override
463 	public ActionForward docHandler(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
464 		super.docHandler(mapping, form, request, response);
465 		KualiMaintenanceForm kualiMaintenanceForm = (KualiMaintenanceForm) form;
466 
467 		if (KEWConstants.ACTIONLIST_COMMAND.equals(kualiMaintenanceForm.getCommand()) || KEWConstants.DOCSEARCH_COMMAND.equals(kualiMaintenanceForm.getCommand()) || KEWConstants.SUPERUSER_COMMAND.equals(kualiMaintenanceForm.getCommand()) || KEWConstants.HELPDESK_ACTIONLIST_COMMAND.equals(kualiMaintenanceForm.getCommand()) && kualiMaintenanceForm.getDocId() != null) {
468 			if (kualiMaintenanceForm.getDocument() instanceof MaintenanceDocument) {
469 				kualiMaintenanceForm.setReadOnly(true);
470 				kualiMaintenanceForm.setMaintenanceAction(((MaintenanceDocument) kualiMaintenanceForm.getDocument()).getNewMaintainableObject().getMaintenanceAction());
471 
472 				//Retrieving the FileName from BO table
473 				Maintainable tmpMaintainable = ((MaintenanceDocument) kualiMaintenanceForm.getDocument()).getNewMaintainableObject();
474 				if(tmpMaintainable.getBusinessObject() instanceof PersistableAttachment) {
475 					PersistableAttachment bo = (PersistableAttachment) getBusinessObjectService().retrieve(tmpMaintainable.getBusinessObject());
476 					if(bo != null)
477 						request.setAttribute("fileName", bo.getFileName());
478 				}
479 			}
480 			else {
481 				LOG.error("Illegal State: document is not a maintenance document");
482 				throw new IllegalStateException("Document is not a maintenance document");
483 			}
484 		}
485 		else if (KEWConstants.INITIATE_COMMAND.equals(kualiMaintenanceForm.getCommand())) {
486 			kualiMaintenanceForm.setReadOnly(false);
487 			return setupMaintenance(mapping, form, request, response, KRADConstants.MAINTENANCE_NEW_ACTION);
488 		}
489 		else {
490 			LOG.error("We should never have gotten to here");
491 			throw new IllegalStateException("docHandler called with invalid parameters");
492 		}
493 		return mapping.findForward(RiceConstants.MAPPING_BASIC);
494 	}
495 
496 	/**
497 	 * Called on return from a lookup.
498 	 */
499 	@Override
500 	public ActionForward refresh(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
501 		KualiMaintenanceForm maintenanceForm = (KualiMaintenanceForm) form;
502 
503 		WebUtils.reuseErrorMapFromPreviousRequest(maintenanceForm);
504 		maintenanceForm.setDerivedValuesOnForm(request);
505 
506 		refreshAdHocRoutingWorkgroupLookups(request, maintenanceForm);
507 		MaintenanceDocument document = (MaintenanceDocument) maintenanceForm.getDocument();
508 
509 		// call refresh on new maintainable
510 		Map<String, String> requestParams = new HashMap<String, String>();
511 		for (Enumeration i = request.getParameterNames(); i.hasMoreElements();) {
512 			String requestKey = (String) i.nextElement();
513 			String requestValue = request.getParameter(requestKey);
514 			requestParams.put(requestKey, requestValue);
515 		}
516 
517 		// Add multiple values from Lookup
518 		Collection<PersistableBusinessObject> rawValues = null;
519 		if (StringUtils.equals(KRADConstants.MULTIPLE_VALUE, maintenanceForm.getRefreshCaller())) {
520 			String lookupResultsSequenceNumber = maintenanceForm.getLookupResultsSequenceNumber();
521 			if (StringUtils.isNotBlank(lookupResultsSequenceNumber)) {
522 				// actually returning from a multiple value lookup
523 				String lookupResultsBOClassName = maintenanceForm.getLookupResultsBOClassName();
524 				Class lookupResultsBOClass = Class.forName(lookupResultsBOClassName);
525 
526 				rawValues = getLookupResultsService().retrieveSelectedResultBOs(lookupResultsSequenceNumber, lookupResultsBOClass, GlobalVariables.getUserSession().getPerson().getPrincipalId());
527 			}
528 		}
529 
530 		if (rawValues != null) { // KULCOA-1073 - caused by this block running unnecessarily?
531 			// we need to run the business rules on all the newly added items to the collection
532 			// KULCOA-1000, KULCOA-1004 removed business rule validation on multiple value return
533 			// (this was running before the objects were added anyway)
534 			// getKualiRuleService().applyRules(new SaveDocumentEvent(document));
535 			String collectionName = maintenanceForm.getLookedUpCollectionName();
536 			//TODO: Cathy remember to delete this block of comments after I've tested.            
537 			//            PersistableBusinessObject bo = document.getNewMaintainableObject().getBusinessObject();
538 			//            Collection maintCollection = this.extractCollection(bo, collectionName);
539 			//            String docTypeName = ((MaintenanceDocument) maintenanceForm.getDocument()).getDocumentHeader().getWorkflowDocument().getDocumentType();
540 			//            Class collectionClass = extractCollectionClass(docTypeName, collectionName);
541 			//
542 			//            List<MaintainableSectionDefinition> sections = maintenanceDocumentDictionaryService.getMaintainableSections(docTypeName);
543 			//            Map<String, String> template = MaintenanceUtils.generateMultipleValueLookupBOTemplate(sections, collectionName);
544 			//            for (PersistableBusinessObject nextBo : rawValues) {
545 			//                PersistableBusinessObject templatedBo = (PersistableBusinessObject) ObjectUtils.createHybridBusinessObject(collectionClass, nextBo, template);
546 			//                templatedBo.setNewCollectionRecord(true);
547 			//                maintCollection.add(templatedBo);
548 			//            }
549 			document.getNewMaintainableObject().addMultipleValueLookupResults(document, collectionName, rawValues, false, document.getNewMaintainableObject().getBusinessObject());
550 			if (LOG.isInfoEnabled()) {
551 				LOG.info("********************doing editing 3 in refersh()***********************.");
552 			}
553 			boolean isEdit = KRADConstants.MAINTENANCE_EDIT_ACTION.equals(maintenanceForm.getMaintenanceAction());
554 			boolean isCopy = KRADConstants.MAINTENANCE_COPY_ACTION.equals(maintenanceForm.getMaintenanceAction());
555 
556 			if (isEdit || isCopy) {
557 				document.getOldMaintainableObject().addMultipleValueLookupResults(document, collectionName, rawValues, true, document.getOldMaintainableObject().getBusinessObject());
558 				document.getOldMaintainableObject().refresh(maintenanceForm.getRefreshCaller(), requestParams, document);
559 			}
560 		}
561 
562 		document.getNewMaintainableObject().refresh(maintenanceForm.getRefreshCaller(), requestParams, document);
563 
564 		//pass out customAction from methodToCall parameter. Call processAfterPost
565 		String fullParameter = (String) request.getAttribute(KRADConstants.METHOD_TO_CALL_ATTRIBUTE);
566 		if(StringUtils.contains(fullParameter, KRADConstants.CUSTOM_ACTION)){
567 			String customAction = StringUtils.substringBetween(fullParameter, KRADConstants.METHOD_TO_CALL_PARM1_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM1_RIGHT_DEL);
568 			String[] actionValue = new String[1];
569 			actionValue[0]= StringUtils.substringAfter(customAction, ".");
570 			Map<String,String[]> paramMap = request.getParameterMap();
571 			paramMap.put(KRADConstants.CUSTOM_ACTION, actionValue);
572 			doProcessingAfterPost( (KualiMaintenanceForm) form, paramMap );
573 		}
574 
575 		return mapping.findForward(RiceConstants.MAPPING_BASIC);
576 	}
577 
578 	/**
579 	 * Gets keys for the maintainable business object from the persistence metadata explorer. Checks for existence of key property
580 	 * names as request parameters, if found adds them to the returned hash map.
581 	 */
582     protected Map buildKeyMapFromRequest(Maintainable maintainable, HttpServletRequest request) {
583 		List keyFieldNames = null;
584 		// are override keys listed in the request? If so, then those need to be our keys,
585 		// not the primary keye fields for the BO
586 		if (!StringUtils.isBlank(request.getParameter(KRADConstants.OVERRIDE_KEYS))) {
587 			String[] overrideKeys = request.getParameter(KRADConstants.OVERRIDE_KEYS).split(KRADConstants.FIELD_CONVERSIONS_SEPARATOR);
588 			keyFieldNames = new ArrayList();
589 			for (String overrideKey : overrideKeys) {
590 				keyFieldNames.add(overrideKey);
591 			}
592 		}
593 		else {
594 			keyFieldNames = getBusinessObjectMetaDataService().listPrimaryKeyFieldNames(maintainable.getBusinessObject().getClass());
595 		}
596 		return getRequestParameters(keyFieldNames, maintainable, request);
597 	}
598 
599     protected Map<String, String> getRequestParameters(List keyFieldNames, Maintainable maintainable, HttpServletRequest request){
600 
601 		Map<String, String> requestParameters = new HashMap<String, String>();
602 
603 
604 		for (Iterator iter = keyFieldNames.iterator(); iter.hasNext();) {
605 			String keyPropertyName = (String) iter.next();
606 
607 			if (request.getParameter(keyPropertyName) != null) {
608 				String keyValue = request.getParameter(keyPropertyName);
609 
610 				// Check if this element was encrypted, if it was decrypt it
611                 if (getBusinessObjectAuthorizationService().attributeValueNeedsToBeEncryptedOnFormsAndLinks(maintainable.getBoClass(), keyPropertyName)) {
612 					try {
613                     	keyValue = StringUtils.removeEnd(keyValue, EncryptionService.ENCRYPTION_POST_PREFIX);
614 						keyValue = encryptionService.decrypt(keyValue);
615 					}
616 					catch (GeneralSecurityException e) {
617 						throw new RuntimeException(e);
618 					}
619 				}
620 
621 
622 				requestParameters.put(keyPropertyName, keyValue);
623 			}
624 		}
625 
626 		return requestParameters;
627 
628 	}
629 
630 	/**
631 	 * Convert a Request into a Map<String,String>. Technically, Request parameters do not neatly translate into a Map of Strings,
632 	 * because a given parameter may legally appear more than once (so a Map of String[] would be more accurate.) This method should
633 	 * be safe for business objects, but may not be reliable for more general uses.
634 	 */
635 	String extractCollectionName(HttpServletRequest request, String methodToCall) {
636 		// collection name and underlying object type from request parameter
637 		String parameterName = (String) request.getAttribute(KRADConstants.METHOD_TO_CALL_ATTRIBUTE);
638 		String collectionName = null;
639 		if (StringUtils.isNotBlank(parameterName)) {
640 			collectionName = StringUtils.substringBetween(parameterName, methodToCall + ".", ".(");
641 		}
642 		return collectionName;
643 	}
644 
645 	Collection extractCollection(PersistableBusinessObject bo, String collectionName) {
646 		// retrieve the collection from the business object
647 		Collection maintCollection = (Collection) ObjectUtils.getPropertyValue(bo, collectionName);
648 		return maintCollection;
649 	}
650 
651 	Class extractCollectionClass(String docTypeName, String collectionName) {
652 		return maintenanceDocumentDictionaryService.getCollectionBusinessObjectClass(docTypeName, collectionName);
653 	}
654 
655 	/**
656 	 * Adds a line to a collection being maintained in a many section.
657 	 */
658 	public ActionForward addLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
659 		KualiMaintenanceForm maintenanceForm = (KualiMaintenanceForm) form;
660 		MaintenanceDocument document = (MaintenanceDocument) maintenanceForm.getDocument();
661 		Maintainable oldMaintainable = document.getOldMaintainableObject();
662 		Maintainable newMaintainable = document.getNewMaintainableObject();
663 
664 		String collectionName = extractCollectionName(request, KRADConstants.ADD_LINE_METHOD);
665 		if (collectionName == null) {
666 			LOG.error("Unable to get find collection name and class in request.");
667 			throw new RuntimeException("Unable to get find collection name and class in request.");
668 		}
669 
670 		// if dealing with sub collection it will have a "["
671 		if ((StringUtils.lastIndexOf(collectionName, "]") + 1) == collectionName.length()) {
672 			collectionName = StringUtils.substringBeforeLast(collectionName, "[");
673 		}
674 
675 		PersistableBusinessObject bo = newMaintainable.getBusinessObject();
676 		Collection maintCollection = extractCollection(bo, collectionName);
677 		Class collectionClass = extractCollectionClass(((MaintenanceDocument) maintenanceForm.getDocument()).getDocumentHeader().getWorkflowDocument().getDocumentTypeName(), collectionName);
678 
679 		// TODO: sort of collection, new instance should be first
680 
681 		// get the BO from the new collection line holder
682 		PersistableBusinessObject addBO = newMaintainable.getNewCollectionLine(collectionName);
683 		if (LOG.isDebugEnabled()) {
684 			LOG.debug("obtained addBO from newCollectionLine: " + addBO);
685 		}
686 
687 		// link up the user fields, if any
688 		getBusinessObjectService().linkUserFields(addBO);
689 
690 		//KULRICE-4264 - a hook to change the state of the business object, which is the "new line" of a collection, before it is validated
691 		newMaintainable.processBeforeAddLine(collectionName, collectionClass, addBO);
692 		
693 		// apply rules to the addBO
694 		boolean rulePassed = false;
695 		if (LOG.isDebugEnabled()) {
696 			LOG.debug("about to call AddLineEvent applyRules: document=" + document + "\ncollectionName=" + collectionName + "\nBO=" + addBO);
697 		}
698 		rulePassed = getKualiRuleService().applyRules(new KualiAddLineEvent(document, collectionName, addBO));
699 
700 		// if the rule evaluation passed, let's add it
701 		if (rulePassed) {
702 			if (LOG.isInfoEnabled()) {
703 				LOG.info("********************doing editing 4 in addline()***********************.");
704 			}
705 			// if edit or copy action, just add empty instance to old maintainable
706 			boolean isEdit = KRADConstants.MAINTENANCE_EDIT_ACTION.equals(maintenanceForm.getMaintenanceAction());
707 			boolean isCopy = KRADConstants.MAINTENANCE_COPY_ACTION.equals(maintenanceForm.getMaintenanceAction());
708 
709 
710 			if (isEdit || isCopy) {
711 				PersistableBusinessObject oldBo = oldMaintainable.getBusinessObject();
712 				Collection oldMaintCollection = (Collection) ObjectUtils.getPropertyValue(oldBo, collectionName);
713 
714 				if (oldMaintCollection == null) {
715 					oldMaintCollection = new ArrayList();
716 				}
717 				if (PersistableBusinessObject.class.isAssignableFrom(collectionClass)) {
718 					PersistableBusinessObject placeholder = (PersistableBusinessObject) collectionClass.newInstance();
719 					// KULRNE-4538: must set it as a new collection record, because the maintainable will set the BO that gets added
720 					// to the new maintainable as a new collection record
721 
722 					// if not set, then the subcollections of the newly added object will appear as read only
723 					// see FieldUtils.getContainerRows on how the delete button is rendered
724 					placeholder.setNewCollectionRecord(true);
725 					((List) oldMaintCollection).add(placeholder);
726 				}
727 				else {
728 					LOG.warn("Should be a instance of PersistableBusinessObject");
729 					((List) oldMaintCollection).add(collectionClass.newInstance());
730 				}
731 				// update collection in maintenance business object
732 				ObjectUtils.setObjectProperty(oldBo, collectionName, List.class, oldMaintCollection);
733 			}
734 
735 			newMaintainable.addNewLineToCollection(collectionName);
736 			int subCollectionIndex = 0;
737 			for (Object aSubCollection : maintCollection) {
738 				subCollectionIndex += getSubCollectionIndex(aSubCollection, maintenanceForm.getDocTypeName());
739 			}
740 			//TODO: Should we keep this logic and continue using currentTabIndex as the key in the tabStates HashMap ?
741 			//            
742 			//            String parameter = (String) request.getAttribute(Constants.METHOD_TO_CALL_ATTRIBUTE);
743 			//            String indexStr = StringUtils.substringBetween(parameter, Constants.METHOD_TO_CALL_PARM13_LEFT_DEL, Constants.METHOD_TO_CALL_PARM13_RIGHT_DEL);
744 			//            // + 1 is for the fact that the first element of a collection is on the next tab
745 			//            int index = Integer.parseInt(indexStr) + subCollectionIndex + 1;
746 			//            Map<String, String> tabStates = maintenanceForm.getTabStates();
747 			//            Map<String, String> copyOfTabStates = new HashMap<String, String>();
748 			//
749 			//            int incrementor = 0;
750 			//            for (String tabState : tabStates.keySet()) {
751 			//            	String originalValue = maintenanceForm.getTabState(Integer.toString(incrementor));
752 			//                copyOfTabStates.put(Integer.toString(incrementor), originalValue);
753 			//                incrementor++;
754 			//            }
755 			//
756 			//            int i = index;
757 			//        	if (tabStates.containsKey(Integer.toString(i-1))) {
758 			//        		tabStates.remove(Integer.toString(i-1));
759 			//        	}
760 			//            while (i < copyOfTabStates.size() + 1) {
761 			//                String originalValue = copyOfTabStates.get(Integer.toString(i-1));
762 			//                if (tabStates.containsKey(Integer.toString(i))) {
763 			//                    tabStates.remove(Integer.toString(i));
764 			//                }
765 			//                tabStates.put(Integer.toString(i), originalValue);
766 			//                i++;
767 			//            }
768 
769 
770 			// End of whether we should continue to keep this logic and use currentTabIndex as the key            
771 		}
772 		doProcessingAfterPost( (KualiMaintenanceForm) form, request );
773 
774 		return mapping.findForward(RiceConstants.MAPPING_BASIC);
775 	}
776 
777     protected int getSubCollectionIndex(Object object, String documentTypeName) {
778 		int index = 1;
779 		MaintainableCollectionDefinition theCollectionDefinition = null;
780 		for (MaintainableCollectionDefinition maintainableCollectionDefinition : maintenanceDocumentDictionaryService.getMaintainableCollections(documentTypeName)) {
781 			if (maintainableCollectionDefinition.getBusinessObjectClass().equals(object.getClass())) {
782 				// we've found the collection we were looking for, so let's find all of its subcollections
783 				theCollectionDefinition = maintainableCollectionDefinition;
784 				break;
785 			}
786 		}
787 		if (theCollectionDefinition != null) {
788 			for (MaintainableCollectionDefinition subCollDef : theCollectionDefinition.getMaintainableCollections()) {
789 				String name = subCollDef.getName();
790 				String capitalFirst = name.substring(0, 1).toUpperCase();
791 				String methodName = "get" + capitalFirst + name.substring(1);
792 				List subCollectionList = new ArrayList();
793 				try {
794 					subCollectionList = (List) object.getClass().getMethod(methodName).invoke(object);
795 				}
796 				catch (InvocationTargetException ite) {
797 					// this shouldn't happen
798 				}
799 				catch (IllegalAccessException iae) {
800 					// this shouldn't happen
801 				}
802 				catch (NoSuchMethodException nme) {
803 					// this shouldn't happen
804 				}
805 				index += subCollectionList.size();
806 			}
807 		}
808 		return index;
809 	}
810 
811 	/**
812 	 * Deletes a collection line that is pending by this document. The collection name and the index to delete is embedded into the
813 	 * delete button name. These parameters are extracted, the collection pulled out of the parent business object, and finally the
814 	 * collection record at the specified index is removed for the new maintainable, and the old if we are dealing with an edit.
815 	 */
816 	public ActionForward deleteLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
817 		KualiMaintenanceForm maintenanceForm = (KualiMaintenanceForm) form;
818 		MaintenanceDocument document = (MaintenanceDocument) maintenanceForm.getDocument();
819 		Maintainable oldMaintainable = document.getOldMaintainableObject();
820 		Maintainable newMaintainable = document.getNewMaintainableObject();
821 
822 		String collectionName = extractCollectionName(request, KRADConstants.DELETE_LINE_METHOD);
823 		if (collectionName == null) {
824 			LOG.error("Unable to get find collection name in request.");
825 			throw new RuntimeException("Unable to get find collection class in request.");
826 		}
827 
828 		PersistableBusinessObject bo = newMaintainable.getBusinessObject();
829 		Collection maintCollection = extractCollection(bo, collectionName);
830 		if (collectionName == null) {
831 			LOG.error("Collection is null in parent business object.");
832 			throw new RuntimeException("Collection is null in parent business object.");
833 		}
834 
835 		int deleteRecordIndex = getLineToDelete(request);
836 		if (deleteRecordIndex < 0 || deleteRecordIndex > maintCollection.size() - 1) {
837 			if (collectionName == null) {
838 				LOG.error("Invalid index for deletion of collection record: " + deleteRecordIndex);
839 				throw new RuntimeException("Invalid index for deletion of collection record: " + deleteRecordIndex);
840 			}
841 		}
842 
843 		((List) maintCollection).remove(deleteRecordIndex);
844 
845 		// if it's either an edit or a copy, need to remove the collection from the old maintainable as well
846 		if (KRADConstants.MAINTENANCE_EDIT_ACTION.equals(maintenanceForm.getMaintenanceAction()) ||
847 				KRADConstants.MAINTENANCE_COPY_ACTION.equals(maintenanceForm.getMaintenanceAction())) {
848 			bo = oldMaintainable.getBusinessObject();
849 			maintCollection = extractCollection(bo, collectionName);
850 
851 			if (collectionName == null) {
852 				LOG.error("Collection is null in parent business object.");
853 				throw new RuntimeException("Collection is null in parent business object.");
854 			}
855 
856 			((List) maintCollection).remove(deleteRecordIndex);
857 		}
858 
859 		// remove the tab state information of the tab that the deleted element originally occupied, so that it will keep tab states
860 		// consistent
861 		//        String parameter = (String) request.getAttribute(Constants.METHOD_TO_CALL_ATTRIBUTE);
862 		//        String indexStr = StringUtils.substringBetween(parameter, Constants.METHOD_TO_CALL_PARM13_LEFT_DEL, Constants.METHOD_TO_CALL_PARM13_RIGHT_DEL);
863 		//        int index = Integer.parseInt(indexStr);
864 		//        maintenanceForm.removeTabState(index);
865 
866 
867 		//      TODO: Should we keep this logic and continue using currentTabIndex as the key in the tabStates HashMap ?        
868 		//        
869 		//        String parameter = (String) request.getAttribute(Constants.METHOD_TO_CALL_ATTRIBUTE);
870 		//        String indexStr = StringUtils.substringBetween(parameter, Constants.METHOD_TO_CALL_PARM13_LEFT_DEL, Constants.METHOD_TO_CALL_PARM13_RIGHT_DEL);
871 		//        // + 1 is for the fact that the first element of a collection is on the next tab
872 		//        int index = Integer.parseInt(indexStr) +  1;
873 		//        Map<String, String> tabStates = maintenanceForm.getTabStates();
874 		//        Map<String, String> copyOfTabStates = new HashMap<String, String>();
875 		//
876 		//        int incrementor = 0;
877 		//        for (String tabState : tabStates.keySet()) {
878 		//        	String originalValue = maintenanceForm.getTabState(Integer.toString(incrementor));
879 		//            copyOfTabStates.put(Integer.toString(incrementor), originalValue);
880 		//            incrementor++;
881 		//        }
882 		//
883 		//        int i = index;
884 		//
885 		//        while (i < copyOfTabStates.size() ) {
886 		//            String originalValue = copyOfTabStates.get(Integer.toString(i));
887 		//            if (tabStates.containsKey(Integer.toString(i-1))) {
888 		//                tabStates.remove(Integer.toString(i-1));
889 		//            }
890 		//            tabStates.put(Integer.toString(i-1), originalValue);
891 		//            i++;
892 		//        }
893 		//
894 		//        
895 		//End of whether we should continue to keep this logic and use currentTabIndex as the key            
896 
897 		doProcessingAfterPost( (KualiMaintenanceForm) form, request );
898 
899 		return mapping.findForward(RiceConstants.MAPPING_BASIC);
900 	}
901 
902 	/**
903 	 * Turns on (or off) the inactive record display for a maintenance collection.
904 	 */
905 	public ActionForward toggleInactiveRecordDisplay(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
906 		KualiMaintenanceForm maintenanceForm = (KualiMaintenanceForm) form;
907 		MaintenanceDocument document = (MaintenanceDocument) maintenanceForm.getDocument();
908 		Maintainable oldMaintainable = document.getOldMaintainableObject();
909 		Maintainable newMaintainable = document.getNewMaintainableObject();
910 
911 		String collectionName = extractCollectionName(request, KRADConstants.TOGGLE_INACTIVE_METHOD);
912 		if (collectionName == null) {
913 			LOG.error("Unable to get find collection name in request.");
914 			throw new RuntimeException("Unable to get find collection class in request.");
915 		}  
916 
917 		String parameterName = (String) request.getAttribute(KRADConstants.METHOD_TO_CALL_ATTRIBUTE);
918 		boolean showInactive = Boolean.parseBoolean(StringUtils.substringBetween(parameterName, KRADConstants.METHOD_TO_CALL_BOPARM_LEFT_DEL, "."));
919 
920 		oldMaintainable.setShowInactiveRecords(collectionName, showInactive);
921 		newMaintainable.setShowInactiveRecords(collectionName, showInactive);
922 
923 		return mapping.findForward(RiceConstants.MAPPING_BASIC);
924 	}
925 
926 
927 
928 	/**
929 	 * This method clears the value of the primary key fields on a Business Object.
930 	 * 
931 	 * @param document - document to clear the pk fields on
932 	 */
933     protected void clearPrimaryKeyFields(MaintenanceDocument document) {
934 		// get business object being maintained and its keys
935 		PersistableBusinessObject bo = document.getNewMaintainableObject().getBusinessObject();
936 		List<String> keyFieldNames = getBusinessObjectMetaDataService().listPrimaryKeyFieldNames(bo.getClass());
937 
938 		for (String keyFieldName : keyFieldNames) {
939 			try {
940 				ObjectUtils.setObjectProperty(bo, keyFieldName, null);
941 			}
942 			catch (Exception e) {
943 				LOG.error("Unable to clear primary key field: " + e.getMessage());
944 				throw new RuntimeException("Unable to clear primary key field: " + e.getMessage());
945 			}
946 		}
947 	}
948 
949 	/**
950 	 * This method is used as part of the Copy functionality, to clear any field values that the user making the copy does not have
951 	 * permissions to modify. This will prevent authorization errors on a copy.
952 	 * 
953 	 * @param document - document to be adjusted
954 	 */
955     protected void clearUnauthorizedNewFields(MaintenanceDocument document) {
956 		// get a reference to the current user
957 		Person user = GlobalVariables.getUserSession().getPerson();
958 
959 		// get the correct documentAuthorizer for this document
960 		MaintenanceDocumentAuthorizer documentAuthorizer = (MaintenanceDocumentAuthorizer) getDocumentHelperService().getDocumentAuthorizer(document);
961 
962 		// get a new instance of MaintenanceDocumentAuthorizations for this context
963 		MaintenanceDocumentRestrictions maintenanceDocumentRestrictions = getBusinessObjectAuthorizationService().getMaintenanceDocumentRestrictions(document, user);
964 
965 		// get a reference to the newBo
966 		PersistableBusinessObject newBo = document.getNewMaintainableObject().getBusinessObject();
967 
968 		document.getNewMaintainableObject().clearBusinessObjectOfRestrictedValues(maintenanceDocumentRestrictions);
969 	}
970 
971 	/**
972 	 * This method does all special processing on a document that should happen on each HTTP post (ie, save, route, approve, etc).
973 	 * 
974 	 * @param form
975 	 */
976 	@SuppressWarnings("unchecked")
977 	protected void doProcessingAfterPost( KualiForm form, HttpServletRequest request ) {
978 		MaintenanceDocument document = (MaintenanceDocument) ((KualiMaintenanceForm)form).getDocument();
979 		Maintainable maintainable = document.getNewMaintainableObject();
980 		PersistableBusinessObject bo = maintainable.getBusinessObject();
981 
982 		getBusinessObjectService().linkUserFields(bo);
983 
984 		maintainable.processAfterPost(document, request.getParameterMap() );
985 	}
986 
987 	protected void doProcessingAfterPost( KualiForm form, Map<String,String[]> parameters ) {
988 		MaintenanceDocument document = (MaintenanceDocument) ((KualiMaintenanceForm)form).getDocument();
989 		Maintainable maintainable = document.getNewMaintainableObject();
990 		PersistableBusinessObject bo = maintainable.getBusinessObject();
991 
992 		getBusinessObjectService().linkUserFields(bo);
993 
994 		maintainable.processAfterPost(document, parameters );
995 	}
996 
997 	protected void populateAuthorizationFields(KualiDocumentFormBase formBase){
998 		super.populateAuthorizationFields(formBase);
999 
1000 		KualiMaintenanceForm maintenanceForm = (KualiMaintenanceForm) formBase;
1001 		MaintenanceDocument maintenanceDocument = (MaintenanceDocument) maintenanceForm.getDocument();
1002 		MaintenanceDocumentAuthorizer maintenanceDocumentAuthorizer = (MaintenanceDocumentAuthorizer) getDocumentHelperService().getDocumentAuthorizer(maintenanceDocument);
1003 		Person user = GlobalVariables.getUserSession().getPerson();
1004 		maintenanceForm.setReadOnly(!formBase.getDocumentActions().containsKey(KRADConstants.KUALI_ACTION_CAN_EDIT));
1005 		MaintenanceDocumentRestrictions maintenanceDocumentAuthorizations = getBusinessObjectAuthorizationService().getMaintenanceDocumentRestrictions(maintenanceDocument, user);
1006 		maintenanceForm.setAuthorizations(maintenanceDocumentAuthorizations);
1007 	}
1008 
1009 	public LookupService getLookupService() {
1010 		if ( lookupService == null ) {
1011 			lookupService = KRADServiceLocatorWeb.getLookupService();
1012 		}
1013 		return this.lookupService;
1014 	}
1015 
1016 	public LookupResultsService getLookupResultsService() {
1017 		if ( lookupResultsService == null ) {
1018 			lookupResultsService = KNSServiceLocator.getLookupResultsService();
1019 		}
1020 		return this.lookupResultsService;
1021 	}
1022 
1023 }