View Javadoc

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