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