001/**
002 * Copyright 2005-2016 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.rice.kns.web.struts.action;
017
018import org.apache.commons.beanutils.PropertyUtils;
019import org.apache.commons.collections.CollectionUtils;
020import org.apache.commons.lang.StringUtils;
021import org.apache.ojb.broker.metadata.ClassNotPersistenceCapableException;
022import org.apache.struts.action.ActionForm;
023import org.apache.struts.action.ActionForward;
024import org.apache.struts.action.ActionMapping;
025import org.apache.struts.upload.FormFile;
026import org.kuali.rice.core.api.CoreApiServiceLocator;
027import org.kuali.rice.core.api.encryption.EncryptionService;
028import org.kuali.rice.core.api.util.ClassLoaderUtils;
029import org.kuali.rice.core.api.util.RiceConstants;
030import org.kuali.rice.core.api.util.RiceKeyConstants;
031import org.kuali.rice.core.api.util.io.SerializationUtils;
032import org.kuali.rice.core.web.format.Formatter;
033import org.kuali.rice.kew.api.KewApiConstants;
034import org.kuali.rice.kim.api.identity.Person;
035import org.kuali.rice.kns.datadictionary.MaintainableCollectionDefinition;
036import org.kuali.rice.kns.datadictionary.MaintenanceDocumentEntry;
037import org.kuali.rice.kns.document.MaintenanceDocument;
038import org.kuali.rice.kns.document.MaintenanceDocumentBase;
039import org.kuali.rice.kns.document.authorization.MaintenanceDocumentAuthorizer;
040import org.kuali.rice.kns.document.authorization.MaintenanceDocumentRestrictions;
041import org.kuali.rice.kns.lookup.LookupResultsService;
042import org.kuali.rice.kns.maintenance.Maintainable;
043import org.kuali.rice.kns.rule.event.KualiAddLineEvent;
044import org.kuali.rice.kns.service.KNSServiceLocator;
045import org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService;
046import org.kuali.rice.kns.util.KNSGlobalVariables;
047import org.kuali.rice.kns.util.MaintenanceUtils;
048import org.kuali.rice.kns.util.WebUtils;
049import org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase;
050import org.kuali.rice.kns.web.struts.form.KualiForm;
051import org.kuali.rice.kns.web.struts.form.KualiMaintenanceForm;
052import org.kuali.rice.krad.bo.DocumentAttachment;
053import org.kuali.rice.krad.bo.MultiDocumentAttachment;
054import org.kuali.rice.krad.bo.PersistableAttachment;
055import org.kuali.rice.krad.bo.PersistableAttachmentList;
056import org.kuali.rice.krad.bo.PersistableBusinessObject;
057import org.kuali.rice.krad.exception.DocumentTypeAuthorizationException;
058import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
059import org.kuali.rice.krad.service.LookupService;
060import org.kuali.rice.krad.util.GlobalVariables;
061import org.kuali.rice.krad.util.KRADConstants;
062import org.kuali.rice.krad.util.KRADPropertyConstants;
063import org.kuali.rice.krad.util.ObjectUtils;
064
065import javax.servlet.http.HttpServletRequest;
066import javax.servlet.http.HttpServletResponse;
067import java.lang.reflect.InvocationTargetException;
068import java.lang.reflect.Method;
069import java.security.GeneralSecurityException;
070import java.util.ArrayList;
071import java.util.Collection;
072import java.util.Enumeration;
073import java.util.HashMap;
074import java.util.Iterator;
075import java.util.List;
076import java.util.Map;
077
078/**
079 * This class handles actions for maintenance documents. These include creating new edit, and copying of maintenance records.
080 *
081 * @deprecated Use {@link org.kuali.rice.krad.maintenance.MaintenanceDocumentController}.
082 */
083@Deprecated
084public class KualiMaintenanceDocumentAction extends KualiDocumentActionBase {
085    protected static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(KualiMaintenanceDocumentAction.class);
086
087    protected MaintenanceDocumentDictionaryService maintenanceDocumentDictionaryService = null;
088    protected EncryptionService encryptionService;
089    protected LookupService lookupService;
090    protected LookupResultsService lookupResultsService;
091
092        public KualiMaintenanceDocumentAction() {
093                super();
094                maintenanceDocumentDictionaryService = KNSServiceLocator.getMaintenanceDocumentDictionaryService();
095                encryptionService = CoreApiServiceLocator.getEncryptionService();
096        }
097
098        @Override
099        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                        maintenanceForm.setDocument(document);
191                }
192                else {
193                        document = (MaintenanceDocument) maintenanceForm.getDocument();
194                }
195
196                // retrieve business object from request parameters
197                if (!(KRADConstants.MAINTENANCE_NEW_ACTION.equals(maintenanceAction))
198                && !(KRADConstants.MAINTENANCE_NEWWITHEXISTING_ACTION.equals(maintenanceAction))) {
199                        Map requestParameters = buildKeyMapFromRequest(document.getNewMaintainableObject(), request);
200            PersistableBusinessObject oldBusinessObject = null;
201            try {
202                oldBusinessObject = (PersistableBusinessObject) getLookupService().findObjectBySearch(Class.forName(maintenanceForm.getBusinessObjectClassName()), requestParameters);
203            } catch ( ClassNotPersistenceCapableException ex ) {
204                if ( !document.getOldMaintainableObject().isExternalBusinessObject() ) {
205                        throw new RuntimeException( "BO Class: " + maintenanceForm.getBusinessObjectClassName() + " is not persistable and is not externalizable - configuration error" );
206                }
207                // otherwise, let fall through
208            }
209                        if (oldBusinessObject == null && !document.getOldMaintainableObject().isExternalBusinessObject()) {
210                throw new RuntimeException("Cannot retrieve old record for maintenance document, incorrect parameters passed on maint url: " + requestParameters );
211                        } 
212
213                        if(document.getOldMaintainableObject().isExternalBusinessObject()){
214                if ( oldBusinessObject == null ) {
215                        try {
216                                oldBusinessObject = (PersistableBusinessObject)document.getOldMaintainableObject().getBoClass().newInstance();
217                        } catch ( Exception ex ) {
218                                throw new RuntimeException( "External BO maintainable was null and unable to instantiate for old maintainable object.", ex );
219                        }
220                }
221                                populateBOWithCopyKeyValues(request, oldBusinessObject, document.getOldMaintainableObject());
222                                document.getOldMaintainableObject().prepareBusinessObject(oldBusinessObject);
223                oldBusinessObject = document.getOldMaintainableObject().getBusinessObject();
224                        }
225
226                        PersistableBusinessObject newBusinessObject = (PersistableBusinessObject) SerializationUtils.deepCopy(
227                    oldBusinessObject);
228
229                        // set business object instance for editing
230                        Class<? extends PersistableBusinessObject> businessObjectClass = ClassLoaderUtils.getClass(maintenanceForm.getBusinessObjectClassName(), PersistableBusinessObject.class); 
231                        document.getOldMaintainableObject().setBusinessObject(oldBusinessObject);
232                        document.getOldMaintainableObject().setBoClass(businessObjectClass);
233                        document.getNewMaintainableObject().setBusinessObject(newBusinessObject);
234                        document.getNewMaintainableObject().setBoClass(businessObjectClass);
235
236
237                        // on a COPY, clear any fields that this user isnt authorized for, and also
238                        // clear the primary key fields and the version number and objectId
239                        if (KRADConstants.MAINTENANCE_COPY_ACTION.equals(maintenanceAction)) {
240                                if (!document.isFieldsClearedOnCopy()) {
241                                        //for issue KULRice 3072
242                                        Class boClass = maintenanceDocumentDictionaryService.getDataObjectClass(
243                            maintenanceForm.getDocTypeName());
244                    if (!maintenanceDocumentDictionaryService.getPreserveLockingKeysOnCopy(boClass)) {
245                        clearPrimaryKeyFields(document);
246                    }
247
248                                        clearUnauthorizedNewFields(document);
249
250                                        Maintainable maintainable = document.getNewMaintainableObject();
251
252                                        maintainable.processAfterCopy( document, request.getParameterMap() );
253
254                                        // mark so that this clearing doesnt happen again
255                                        document.setFieldsClearedOnCopy(true);
256
257                                        // mark so that blank required fields will be populated with default values
258                                        maintainable.setGenerateBlankRequiredValues(maintenanceForm.getDocTypeName());
259                                }
260                        }
261                        else if (KRADConstants.MAINTENANCE_EDIT_ACTION.equals(maintenanceAction)) {
262                                boolean allowsEdit = getBusinessObjectAuthorizationService().canMaintain(oldBusinessObject, GlobalVariables.getUserSession().getPerson(), document.getDocumentHeader().getWorkflowDocument().getDocumentTypeName());
263                                if (!allowsEdit) {
264                                        LOG.error("Document type " + document.getDocumentHeader().getWorkflowDocument().getDocumentTypeName() + " does not allow edit actions.");
265                                        throw  new DocumentTypeAuthorizationException(GlobalVariables.getUserSession().getPerson().getPrincipalId(), "edit", document.getDocumentHeader().getWorkflowDocument().getDocumentTypeName());
266                                }
267                                document.getNewMaintainableObject().processAfterEdit( document, request.getParameterMap() );
268                        }
269                        //3070
270                        else if (KRADConstants.MAINTENANCE_DELETE_ACTION.equals(maintenanceAction)) {
271                                boolean allowsDelete = getBusinessObjectAuthorizationService().canMaintain(oldBusinessObject, GlobalVariables.getUserSession().getPerson(), document.getDocumentHeader().getWorkflowDocument().getDocumentTypeName());
272                                if (!allowsDelete) {
273                                        LOG.error("Document type " + document.getDocumentHeader().getWorkflowDocument().getDocumentTypeName() + " does not allow delete actions.");
274                                        throw  new DocumentTypeAuthorizationException(GlobalVariables.getUserSession().getPerson().getPrincipalId(), "delete", document.getDocumentHeader().getWorkflowDocument().getDocumentTypeName());
275                                }       
276                                //document.getNewMaintainableObject().processAfterEdit( document, request.getParameterMap() );
277                        }
278                }
279                // if new with existing we need to populate we need to populate with passed in parameters
280                if (KRADConstants.MAINTENANCE_NEWWITHEXISTING_ACTION.equals(maintenanceAction)) {
281                        // TODO: this code should be abstracted out into a helper
282                        // also is it a problem that we're not calling setGenerateDefaultValues? it blanked out the below values when I did
283                        // maybe we need a new generateDefaultValues that doesn't overwrite?
284                        PersistableBusinessObject newBO = document.getNewMaintainableObject().getBusinessObject();
285                        Map<String, String> parameters = buildKeyMapFromRequest(document.getNewMaintainableObject(), request);
286                        copyParametersToBO(parameters, newBO);
287                        newBO.refresh();
288                        document.getNewMaintainableObject().setupNewFromExisting( document, request.getParameterMap() );
289                }
290
291                // for new maintainble need to pick up default values
292                if (KRADConstants.MAINTENANCE_NEW_ACTION.equals(maintenanceAction)) {
293                        document.getNewMaintainableObject().setGenerateDefaultValues(maintenanceForm.getDocTypeName());
294                        document.getNewMaintainableObject().processAfterNew( document, request.getParameterMap() );
295
296                        // If a maintenance lock exists, warn the user.
297                        MaintenanceUtils.checkForLockingDocument(document.getNewMaintainableObject(), false);
298                }
299
300                // set maintenance action state
301                document.getNewMaintainableObject().setMaintenanceAction(maintenanceAction);
302                maintenanceForm.setMaintenanceAction(maintenanceAction);
303
304                // attach any extra JS from the data dictionary
305        MaintenanceDocumentEntry entry =  maintenanceDocumentDictionaryService.getMaintenanceDocumentEntry(document.getDocumentHeader().getWorkflowDocument().getDocumentTypeName());
306                if (LOG.isDebugEnabled()) {
307                        LOG.debug("maintenanceForm.getAdditionalScriptFiles(): " + maintenanceForm.getAdditionalScriptFiles());
308                }
309                if (maintenanceForm.getAdditionalScriptFiles().isEmpty()) {
310                        maintenanceForm.getAdditionalScriptFiles().addAll(entry.getWebScriptFiles());
311                }
312
313                // Retrieve notes topic display flag from data dictionary and add to document
314                document.setDisplayTopicFieldInNotes(entry.getDisplayTopicFieldInNotes());
315
316                return mapping.findForward(RiceConstants.MAPPING_BASIC);
317        }
318
319    protected void populateBOWithCopyKeyValues(HttpServletRequest request, PersistableBusinessObject oldBusinessObject, Maintainable oldMaintainableObject) throws Exception{
320                List keyFieldNamesToCopy = new ArrayList();
321                Map<String, String> parametersToCopy;
322                if (!StringUtils.isBlank(request.getParameter(KRADConstants.COPY_KEYS))) {
323                        String[] copyKeys = request.getParameter(KRADConstants.COPY_KEYS).split(KRADConstants.FIELD_CONVERSIONS_SEPARATOR);
324                        for (String copyKey: copyKeys) {
325                                keyFieldNamesToCopy.add(copyKey);
326                        }
327                }
328                parametersToCopy = getRequestParameters(keyFieldNamesToCopy, oldMaintainableObject, request);
329                if(parametersToCopy!=null && parametersToCopy.size()>0){
330                        copyParametersToBO(parametersToCopy, oldBusinessObject);
331                }
332        }
333
334    protected void copyParametersToBO(Map<String, String> parameters, PersistableBusinessObject newBO) throws Exception{
335                for (String parmName : parameters.keySet()) {
336                        String propertyValue = parameters.get(parmName);
337
338                        if (StringUtils.isNotBlank(propertyValue)) {
339                                String propertyName = parmName;
340                                // set value of property in bo
341                                if (PropertyUtils.isWriteable(newBO, propertyName)) {
342                                        Class type = ObjectUtils.easyGetPropertyType(newBO, propertyName);
343                                        if (type != null && Formatter.getFormatter(type) != null) {
344                                                Formatter formatter = Formatter.getFormatter(type);
345                                                Object obj = formatter.convertFromPresentationFormat(propertyValue);
346                                                ObjectUtils.setObjectProperty(newBO, propertyName, obj.getClass(), obj);
347                                        }
348                                        else {
349                                                ObjectUtils.setObjectProperty(newBO, propertyName, String.class, propertyValue);
350                                        }
351                                }
352                        }
353                }
354        }
355
356        /**
357         * Downloads the attachment to the user's browser
358         *
359         * @param mapping
360         * @param form
361         * @param request
362         * @param response
363         * @return ActionForward
364         * @throws Exception
365         */
366        public ActionForward downloadAttachment(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
367                KualiDocumentFormBase documentForm = (KualiDocumentFormBase) form;
368                MaintenanceDocumentBase document = (MaintenanceDocumentBase) documentForm.getDocument();
369
370        int line = getSelectedLine(request);
371        if (line < 0) {
372            DocumentAttachment documentAttachment = document.getAttachment();
373            if (documentAttachment != null
374                    && documentAttachment.getAttachmentContent() != null) {
375
376                streamToResponse(documentAttachment.getAttachmentContent(), documentAttachment.getFileName(), documentAttachment.getContentType(), response);
377                return null;
378            }
379            PersistableAttachment attachment = (PersistableAttachment) document.getNewMaintainableObject().getBusinessObject();
380            String attachmentPropNm = document.getAttachmentPropertyName();
381            FormFile attachmentFromBusinessObject = null;
382            byte[] attachmentContent;
383            String fileName = attachment.getFileName();
384            String contentType = attachment.getContentType();
385            if (StringUtils.isNotBlank(attachmentPropNm)) {
386                String attachmentPropNmSetter = "get" + attachmentPropNm.substring(0, 1).toUpperCase() + attachmentPropNm.substring(1, attachmentPropNm.length());
387                attachmentFromBusinessObject = (FormFile)(attachment.getClass().getDeclaredMethod(attachmentPropNmSetter).invoke(attachment));
388            }
389            if (attachmentFromBusinessObject != null
390                    && attachmentFromBusinessObject.getInputStream() != null) {
391                attachmentContent = attachmentFromBusinessObject.getFileData();
392                fileName = attachmentFromBusinessObject.getFileName();
393                contentType = attachmentFromBusinessObject.getContentType();
394            } else {
395                attachmentContent = attachment.getAttachmentContent();
396            }
397            if (StringUtils.isNotBlank(fileName)
398                    && contentType != null
399                    && attachmentContent != null) {
400                streamToResponse(attachmentContent, fileName, contentType, response);
401            }
402        } else {
403
404            // attachment is part of a collection
405            PersistableAttachmentList<PersistableAttachment> attachmentsBo = (PersistableAttachmentList<PersistableAttachment>) document.getNewMaintainableObject().getBusinessObject();
406            if (CollectionUtils.isEmpty(attachmentsBo.getAttachments())) {
407                document.populateAttachmentListForBO();
408            }
409
410            List<? extends PersistableAttachment> attachments = attachmentsBo.getAttachments();
411            if (CollectionUtils.isNotEmpty(attachments)
412                    && attachments.size() > line) {
413                PersistableAttachment attachment = attachmentsBo.getAttachments().get(line);
414
415                //it is possible that document hasn't been saved (attachment just added) and the attachment content is still in the FormFile
416                //need to grab it if that is the case.
417                byte[] attachmentContent; // = attachment.getAttachmentContent();
418                String fileName = attachment.getFileName();
419                String contentType = attachment.getContentType();
420                String attachmentPropNm = document.getAttachmentListPropertyName();
421                FormFile attachmentFromBusinessObject = null;
422                if (StringUtils.isNotBlank(attachmentPropNm)) {
423                    String attachmentPropNmSetter = "get" + attachmentPropNm.substring(0, 1).toUpperCase() + attachmentPropNm.substring(1, attachmentPropNm.length());
424                    attachmentFromBusinessObject = (FormFile)(attachment.getClass().getDeclaredMethod(attachmentPropNmSetter).invoke(attachment));
425                }
426                //Use form file data if it exists
427                //if (attachmentContent == null) {
428
429                if (attachmentFromBusinessObject != null
430                    && attachmentFromBusinessObject.getInputStream() != null) {
431                    attachmentContent = attachmentFromBusinessObject.getFileData();
432                    fileName = attachmentFromBusinessObject.getFileName();
433                    contentType = attachmentFromBusinessObject.getContentType();
434                } else {
435                    attachmentContent = attachment.getAttachmentContent();
436                }
437
438                if (attachmentContent != null) {
439                    streamToResponse(attachmentContent, fileName, contentType, response);
440                } else {
441                    // last ditch effort to find the correct attachment
442                    //check to see if attachment is populated on document first, so no copying done unless necessary
443                    List<MultiDocumentAttachment> multiDocumentAttachs = document.getAttachments();
444                    if (CollectionUtils.isNotEmpty(multiDocumentAttachs)) {
445                        for (MultiDocumentAttachment multiAttach : multiDocumentAttachs) {
446                            if (multiAttach.getFileName().equals(fileName)
447                                    && multiAttach.getContentType().equals(contentType)) {
448                                streamToResponse(multiAttach.getAttachmentContent(), multiAttach.getFileName(), multiAttach.getContentType(), response);
449                                break;
450                            }
451                        }
452                    }
453                }
454            }
455        }
456                return null;
457        }
458
459
460        /**
461         * 
462         * This method used to replace the attachment
463         * @param mapping
464         * @param form
465         * @param request
466         * @param response
467         * @return
468         * @throws Exception
469         */
470        public ActionForward replaceAttachment(ActionMapping mapping, ActionForm form, HttpServletRequest request,
471                        HttpServletResponse response) throws Exception {
472                KualiDocumentFormBase documentForm = (KualiDocumentFormBase) form;
473                MaintenanceDocumentBase document = (MaintenanceDocumentBase) documentForm.getDocument();
474
475        int lineNum = getSelectedLine(request);
476
477        if (lineNum < 0) {
478
479            document.refreshReferenceObject("attachment");
480            documentForm.setAttachmentFile(null);
481            document.setFileAttachment(null);
482            getBusinessObjectService().delete(document.getAttachment());
483            document.setAttachment(null);
484
485            PersistableAttachment attachment = (PersistableAttachment) document.getNewMaintainableObject().getBusinessObject();
486
487            attachment.setAttachmentContent(null);
488            attachment.setContentType(null);
489            attachment.setFileName(null);
490            //pBo.setAttachmentFile(null);
491
492            String attachmentPropNm = document.getAttachmentPropertyName();
493            String attachmentPropNmSetter = "set" + attachmentPropNm.substring(0, 1).toUpperCase() + attachmentPropNm.substring(1, attachmentPropNm.length());
494            Class propNameSetterSig = null;
495
496            try {
497                Method[] methods = attachment.getClass().getMethods();
498                for (Method method : methods) {
499                    if (method.getName().equals(attachmentPropNmSetter)) {
500                        propNameSetterSig = method.getParameterTypes()[0];
501                        attachment.getClass().getDeclaredMethod(attachmentPropNmSetter, propNameSetterSig).invoke(attachment, (Object) null);
502                        break;
503                    }
504                }
505            } catch (Exception e) {
506                LOG.error("Not able to get the attachment " + e.getMessage());
507                throw new RuntimeException(
508                        "Not able to get the attachment  " + e.getMessage());
509            }
510        } else {
511            document.refreshReferenceObject("attachments");
512            getBusinessObjectService().delete(document.getAttachment());
513
514            PersistableAttachmentList<PersistableAttachment> attachmentListBo = (PersistableAttachmentList<PersistableAttachment>) document.getNewMaintainableObject().getBusinessObject();
515
516            PersistableAttachment attachment = (PersistableAttachment)attachmentListBo.getAttachments().get(lineNum);
517            attachment.setAttachmentContent(null);
518            attachment.setContentType(null);
519            attachment.setFileName(null);
520
521            String attachmentPropNm = document.getAttachmentListPropertyName();
522            String attachmentPropNmSetter = "set" + attachmentPropNm.substring(0, 1).toUpperCase() + attachmentPropNm.substring(1, attachmentPropNm.length());
523            Class propNameSetterSig = null;
524
525            try {
526                Method[] methods = attachment.getClass().getMethods();
527                for (Method method : methods) {
528                    if (method.getName().equals(attachmentPropNmSetter)) {
529                        propNameSetterSig = method.getParameterTypes()[0];
530                        attachment.getClass().getDeclaredMethod(attachmentPropNmSetter, propNameSetterSig).invoke(attachment, (Object) null);
531                        break;
532                    }
533                }
534            } catch (Exception e) {
535                LOG.error("Not able to get the attachment " + e.getMessage());
536                throw new RuntimeException(
537                        "Not able to get the attachment  " + e.getMessage());
538            }
539        }
540
541            return mapping.findForward(RiceConstants.MAPPING_BASIC);
542        }
543
544        /**
545         * route the document using the document service
546         * 
547         * @param mapping
548         * @param form
549         * @param request
550         * @param response
551         * @return ActionForward
552         * @throws Exception
553         */
554        @Override
555        public ActionForward route(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
556                KualiDocumentFormBase documentForm = (KualiDocumentFormBase) form;
557                MaintenanceDocumentBase document = (MaintenanceDocumentBase) documentForm.getDocument();
558
559                ActionForward forward = super.route(mapping, form, request, response);
560                PersistableBusinessObject businessObject = document.getNewMaintainableObject().getBusinessObject();
561                if(businessObject instanceof PersistableAttachment) {
562                        document.populateAttachmentForBO();
563                        String fileName = ((PersistableAttachment) businessObject).getFileName();
564                        if(StringUtils.isEmpty(fileName)) {
565                                PersistableAttachment existingBO = (PersistableAttachment) getBusinessObjectService().retrieve(document.getNewMaintainableObject().getBusinessObject());
566                                if (existingBO == null) {
567                                        if (document.getAttachment() != null) {
568                                                fileName = document.getAttachment().getFileName();
569                                        } else {
570                                                fileName = "";
571                                        }
572                                } else {
573                                        fileName = (existingBO != null ? existingBO.getFileName() : "");
574                                }
575                                request.setAttribute("fileName", fileName);
576                        }
577                }
578                return forward;
579        }
580
581        /**
582         * Handles creating and loading of documents.
583         */
584        @Override
585        public ActionForward docHandler(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
586                ActionForward af = super.docHandler(mapping, form, request, response);
587        if (af.getName().equals(KRADConstants.KRAD_INITIATED_DOCUMENT_VIEW_NAME))
588        {
589            return af;
590        }
591                KualiMaintenanceForm kualiMaintenanceForm = (KualiMaintenanceForm) form;
592
593                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) {
594                        if (kualiMaintenanceForm.getDocument() instanceof MaintenanceDocument) {
595                                kualiMaintenanceForm.setReadOnly(true);
596                                kualiMaintenanceForm.setMaintenanceAction(((MaintenanceDocument) kualiMaintenanceForm.getDocument()).getNewMaintainableObject().getMaintenanceAction());
597
598                                //Retrieving the FileName from BO table
599                                Maintainable tmpMaintainable = ((MaintenanceDocument) kualiMaintenanceForm.getDocument()).getNewMaintainableObject();
600                                if(tmpMaintainable.getBusinessObject() instanceof PersistableAttachment) {
601                                        PersistableAttachment bo = (PersistableAttachment) getBusinessObjectService().retrieve(tmpMaintainable.getBusinessObject());
602                    if (bo != null) {
603                        request.setAttribute("fileName", bo.getFileName());
604                    }
605                                }
606                        }
607                        else {
608                                LOG.error("Illegal State: document is not a maintenance document");
609                                throw new IllegalArgumentException("Document is not a maintenance document");
610                        }
611                }
612                else if (KewApiConstants.INITIATE_COMMAND.equals(kualiMaintenanceForm.getCommand())) {
613                        kualiMaintenanceForm.setReadOnly(false);
614                        return setupMaintenance(mapping, form, request, response, KRADConstants.MAINTENANCE_NEW_ACTION);
615                }
616                else {
617                        LOG.error("We should never have gotten to here");
618                        throw new IllegalArgumentException("docHandler called with invalid parameters");
619                }
620                return mapping.findForward(RiceConstants.MAPPING_BASIC);
621        }
622
623        /**
624         * Called on return from a lookup.
625         */
626        @Override
627        public ActionForward refresh(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
628                KualiMaintenanceForm maintenanceForm = (KualiMaintenanceForm) form;
629
630                WebUtils.reuseErrorMapFromPreviousRequest(maintenanceForm);
631                maintenanceForm.setDerivedValuesOnForm(request);
632
633                refreshAdHocRoutingWorkgroupLookups(request, maintenanceForm);
634                MaintenanceDocument document = (MaintenanceDocument) maintenanceForm.getDocument();
635
636                // call refresh on new maintainable
637                Map<String, String> requestParams = new HashMap<String, String>();
638                for (Enumeration i = request.getParameterNames(); i.hasMoreElements();) {
639                        String requestKey = (String) i.nextElement();
640                        String requestValue = request.getParameter(requestKey);
641                        requestParams.put(requestKey, requestValue);
642                }
643
644                // Add multiple values from Lookup
645                Collection<PersistableBusinessObject> rawValues = null;
646                if (StringUtils.equals(KRADConstants.MULTIPLE_VALUE, maintenanceForm.getRefreshCaller())) {
647                        String lookupResultsSequenceNumber = maintenanceForm.getLookupResultsSequenceNumber();
648                        if (StringUtils.isNotBlank(lookupResultsSequenceNumber)) {
649                                // actually returning from a multiple value lookup
650                                String lookupResultsBOClassName = maintenanceForm.getLookupResultsBOClassName();
651                                Class lookupResultsBOClass = Class.forName(lookupResultsBOClassName);
652
653                                rawValues = getLookupResultsService().retrieveSelectedResultBOs(lookupResultsSequenceNumber, lookupResultsBOClass, GlobalVariables.getUserSession().getPerson().getPrincipalId());
654                        }
655                }
656
657                if (rawValues != null) { // KULCOA-1073 - caused by this block running unnecessarily?
658                        // we need to run the business rules on all the newly added items to the collection
659                        // KULCOA-1000, KULCOA-1004 removed business rule validation on multiple value return
660                        // (this was running before the objects were added anyway)
661                        // getKualiRuleService().applyRules(new SaveDocumentEvent(document));
662                        String collectionName = maintenanceForm.getLookedUpCollectionName();
663                        //TODO: Cathy remember to delete this block of comments after I've tested.            
664                        //            PersistableBusinessObject bo = document.getNewMaintainableObject().getBusinessObject();
665                        //            Collection maintCollection = this.extractCollection(bo, collectionName);
666                        //            String docTypeName = ((MaintenanceDocument) maintenanceForm.getDocument()).getDocumentHeader().getWorkflowDocument().getDocumentType();
667                        //            Class collectionClass = extractCollectionClass(docTypeName, collectionName);
668                        //
669                        //            List<MaintainableSectionDefinition> sections = maintenanceDocumentDictionaryService.getMaintainableSections(docTypeName);
670                        //            Map<String, String> template = MaintenanceUtils.generateMultipleValueLookupBOTemplate(sections, collectionName);
671                        //            for (PersistableBusinessObject nextBo : rawValues) {
672                        //                PersistableBusinessObject templatedBo = (PersistableBusinessObject) ObjectUtils.createHybridBusinessObject(collectionClass, nextBo, template);
673                        //                templatedBo.setNewCollectionRecord(true);
674                        //                maintCollection.add(templatedBo);
675                        //            }
676                        document.getNewMaintainableObject().addMultipleValueLookupResults(document, collectionName, rawValues, false, document.getNewMaintainableObject().getBusinessObject());
677                        if (LOG.isInfoEnabled()) {
678                                LOG.info("********************doing editing 3 in refersh()***********************.");
679                        }
680                        boolean isEdit = KRADConstants.MAINTENANCE_EDIT_ACTION.equals(maintenanceForm.getMaintenanceAction());
681                        boolean isCopy = KRADConstants.MAINTENANCE_COPY_ACTION.equals(maintenanceForm.getMaintenanceAction());
682
683                        if (isEdit || isCopy) {
684                                document.getOldMaintainableObject().addMultipleValueLookupResults(document, collectionName, rawValues, true, document.getOldMaintainableObject().getBusinessObject());
685                                document.getOldMaintainableObject().refresh(maintenanceForm.getRefreshCaller(), requestParams, document);
686                        }
687                }
688
689                document.getNewMaintainableObject().refresh(maintenanceForm.getRefreshCaller(), requestParams, document);
690
691                //pass out customAction from methodToCall parameter. Call processAfterPost
692                String fullParameter = (String) request.getAttribute(KRADConstants.METHOD_TO_CALL_ATTRIBUTE);
693                if(StringUtils.contains(fullParameter, KRADConstants.CUSTOM_ACTION)){
694                        String customAction = StringUtils.substringBetween(fullParameter, KRADConstants.METHOD_TO_CALL_PARM1_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM1_RIGHT_DEL);
695                        String[] actionValue = new String[1];
696                        actionValue[0]= StringUtils.substringAfter(customAction, ".");
697                        Map<String,String[]> paramMap = new HashMap<String,String[]>(request.getParameterMap());
698                        paramMap.put(KRADConstants.CUSTOM_ACTION, actionValue);
699                        doProcessingAfterPost( (KualiMaintenanceForm) form, paramMap );
700                }
701
702                return mapping.findForward(RiceConstants.MAPPING_BASIC);
703        }
704
705        /**
706         * Gets keys for the maintainable business object from the persistence metadata explorer. Checks for existence of key property
707         * names as request parameters, if found adds them to the returned hash map.
708         */
709    protected Map buildKeyMapFromRequest(Maintainable maintainable, HttpServletRequest request) {
710                List keyFieldNames = null;
711                // are override keys listed in the request? If so, then those need to be our keys,
712                // not the primary keye fields for the BO
713                if (!StringUtils.isBlank(request.getParameter(KRADConstants.OVERRIDE_KEYS))) {
714                        String[] overrideKeys = request.getParameter(KRADConstants.OVERRIDE_KEYS).split(KRADConstants.FIELD_CONVERSIONS_SEPARATOR);
715                        keyFieldNames = new ArrayList();
716                        for (String overrideKey : overrideKeys) {
717                                keyFieldNames.add(overrideKey);
718                        }
719                }
720                else {
721                        keyFieldNames = KRADServiceLocatorWeb.getLegacyDataAdapter().listPrimaryKeyFieldNames(maintainable.getBusinessObject().getClass());
722                }
723                return getRequestParameters(keyFieldNames, maintainable, request);
724        }
725
726    protected Map<String, String> getRequestParameters(List keyFieldNames, Maintainable maintainable, HttpServletRequest request){
727
728                Map<String, String> requestParameters = new HashMap<String, String>();
729
730
731                for (Iterator iter = keyFieldNames.iterator(); iter.hasNext();) {
732                        String keyPropertyName = (String) iter.next();
733
734                        if (request.getParameter(keyPropertyName) != null) {
735                                String keyValue = request.getParameter(keyPropertyName);
736
737                                // Check if this element was encrypted, if it was decrypt it
738                if (getBusinessObjectAuthorizationService().attributeValueNeedsToBeEncryptedOnFormsAndLinks(maintainable.getBoClass(), keyPropertyName)) {
739                                        try {
740                        keyValue = StringUtils.removeEnd(keyValue, EncryptionService.ENCRYPTION_POST_PREFIX);
741                        if(CoreApiServiceLocator.getEncryptionService().isEnabled()) {
742                                                    keyValue = encryptionService.decrypt(keyValue);
743                        }
744                                        }
745                                        catch (GeneralSecurityException e) {
746                                                throw new RuntimeException(e);
747                                        }
748                                }
749
750
751                                requestParameters.put(keyPropertyName, keyValue);
752                        }
753                }
754
755                return requestParameters;
756
757        }
758
759        /**
760         * Convert a Request into a Map<String,String>. Technically, Request parameters do not neatly translate into a Map of Strings,
761         * because a given parameter may legally appear more than once (so a Map of String[] would be more accurate.) This method should
762         * be safe for business objects, but may not be reliable for more general uses.
763         */
764        String extractCollectionName(HttpServletRequest request, String methodToCall) {
765                // collection name and underlying object type from request parameter
766                String parameterName = (String) request.getAttribute(KRADConstants.METHOD_TO_CALL_ATTRIBUTE);
767                String collectionName = null;
768                if (StringUtils.isNotBlank(parameterName)) {
769                        collectionName = StringUtils.substringBetween(parameterName, methodToCall + ".", ".(");
770                }
771                return collectionName;
772        }
773
774        Collection extractCollection(Object bo, String collectionName) {
775                // retrieve the collection from the business object
776                Collection maintCollection = (Collection) ObjectUtils.getPropertyValue(bo, collectionName);
777                return maintCollection;
778        }
779
780        Class extractCollectionClass(String docTypeName, String collectionName) {
781                return maintenanceDocumentDictionaryService.getCollectionBusinessObjectClass(docTypeName, collectionName);
782        }
783
784        /**
785         * Adds a line to a collection being maintained in a many section.
786         */
787        public ActionForward addLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
788                KualiMaintenanceForm maintenanceForm = (KualiMaintenanceForm) form;
789                MaintenanceDocument document = (MaintenanceDocument) maintenanceForm.getDocument();
790                Maintainable oldMaintainable = document.getOldMaintainableObject();
791                Maintainable newMaintainable = document.getNewMaintainableObject();
792
793                String collectionName = extractCollectionName(request, KRADConstants.ADD_LINE_METHOD);
794                if (collectionName == null) {
795                        LOG.error("Unable to get find collection name and class in request.");
796                        throw new RuntimeException("Unable to get find collection name and class in request.");
797                }
798
799                // if dealing with sub collection it will have a "["
800                if ((StringUtils.lastIndexOf(collectionName, "]") + 1) == collectionName.length()) {
801                        collectionName = StringUtils.substringBeforeLast(collectionName, "[");
802                }
803
804                Object bo = newMaintainable.getBusinessObject();
805                Collection maintCollection = extractCollection(bo, collectionName);
806                Class collectionClass = extractCollectionClass(((MaintenanceDocument) maintenanceForm.getDocument()).getDocumentHeader().getWorkflowDocument().getDocumentTypeName(), collectionName);
807
808                // TODO: sort of collection, new instance should be first
809
810                // get the BO from the new collection line holder
811                PersistableBusinessObject addBO = newMaintainable.getNewCollectionLine(collectionName);
812                if (LOG.isDebugEnabled()) {
813                        LOG.debug("obtained addBO from newCollectionLine: " + addBO);
814                }
815
816                // link up the user fields, if any
817                getBusinessObjectService().linkUserFields(addBO);
818
819                //KULRICE-4264 - a hook to change the state of the business object, which is the "new line" of a collection, before it is validated
820                newMaintainable.processBeforeAddLine(collectionName, collectionClass, addBO);
821                
822                // apply rules to the addBO
823                boolean rulePassed = false;
824                if (LOG.isDebugEnabled()) {
825                        LOG.debug("about to call AddLineEvent applyRules: document=" + document + "\ncollectionName=" + collectionName + "\nBO=" + addBO);
826                }
827                rulePassed = getKualiRuleService().applyRules(new KualiAddLineEvent(document, collectionName, addBO));
828
829                // if the rule evaluation passed, let's add it
830                if (rulePassed) {
831                        if (LOG.isInfoEnabled()) {
832                                LOG.info("********************doing editing 4 in addline()***********************.");
833                        }
834                        // if edit or copy action, just add empty instance to old maintainable
835                        boolean isEdit = KRADConstants.MAINTENANCE_EDIT_ACTION.equals(maintenanceForm.getMaintenanceAction());
836                        boolean isCopy = KRADConstants.MAINTENANCE_COPY_ACTION.equals(maintenanceForm.getMaintenanceAction());
837
838
839                        if (isEdit || isCopy) {
840                                Object oldBo = oldMaintainable.getBusinessObject();
841                                Collection oldMaintCollection = (Collection) ObjectUtils.getPropertyValue(oldBo, collectionName);
842
843                                if (oldMaintCollection == null) {
844                                        oldMaintCollection = new ArrayList();
845                                }
846                                if (PersistableBusinessObject.class.isAssignableFrom(collectionClass)) {
847                                        PersistableBusinessObject placeholder = (PersistableBusinessObject) collectionClass.newInstance();
848                                        // KULRNE-4538: must set it as a new collection record, because the maintainable will set the BO that gets added
849                                        // to the new maintainable as a new collection record
850
851                                        // if not set, then the subcollections of the newly added object will appear as read only
852                                        // see FieldUtils.getContainerRows on how the delete button is rendered
853                                        placeholder.setNewCollectionRecord(true);
854                                        ((List) oldMaintCollection).add(placeholder);
855                                }
856                                else {
857                                        LOG.warn("Should be a instance of PersistableBusinessObject");
858                                        ((List) oldMaintCollection).add(collectionClass.newInstance());
859                                }
860                                // update collection in maintenance business object
861                                ObjectUtils.setObjectProperty(oldBo, collectionName, List.class, oldMaintCollection);
862                        }
863
864                        newMaintainable.addNewLineToCollection(collectionName);
865                        int subCollectionIndex = 0;
866                        for (Object aSubCollection : maintCollection) {
867                                subCollectionIndex += getSubCollectionIndex(aSubCollection, maintenanceForm.getDocTypeName());
868                        }
869                        //TODO: Should we keep this logic and continue using currentTabIndex as the key in the tabStates HashMap ?
870                        //            
871                        //            String parameter = (String) request.getAttribute(Constants.METHOD_TO_CALL_ATTRIBUTE);
872                        //            String indexStr = StringUtils.substringBetween(parameter, Constants.METHOD_TO_CALL_PARM13_LEFT_DEL, Constants.METHOD_TO_CALL_PARM13_RIGHT_DEL);
873                        //            // + 1 is for the fact that the first element of a collection is on the next tab
874                        //            int index = Integer.parseInt(indexStr) + subCollectionIndex + 1;
875                        //            Map<String, String> tabStates = maintenanceForm.getTabStates();
876                        //            Map<String, String> copyOfTabStates = new HashMap<String, String>();
877                        //
878                        //            int incrementor = 0;
879                        //            for (String tabState : tabStates.keySet()) {
880                        //              String originalValue = maintenanceForm.getTabState(Integer.toString(incrementor));
881                        //                copyOfTabStates.put(Integer.toString(incrementor), originalValue);
882                        //                incrementor++;
883                        //            }
884                        //
885                        //            int i = index;
886                        //              if (tabStates.containsKey(Integer.toString(i-1))) {
887                        //                      tabStates.remove(Integer.toString(i-1));
888                        //              }
889                        //            while (i < copyOfTabStates.size() + 1) {
890                        //                String originalValue = copyOfTabStates.get(Integer.toString(i-1));
891                        //                if (tabStates.containsKey(Integer.toString(i))) {
892                        //                    tabStates.remove(Integer.toString(i));
893                        //                }
894                        //                tabStates.put(Integer.toString(i), originalValue);
895                        //                i++;
896                        //            }
897
898
899                        // End of whether we should continue to keep this logic and use currentTabIndex as the key            
900                }
901                doProcessingAfterPost( (KualiMaintenanceForm) form, request );
902
903                return mapping.findForward(RiceConstants.MAPPING_BASIC);
904        }
905
906    protected int getSubCollectionIndex(Object object, String documentTypeName) {
907                int index = 1;
908                MaintainableCollectionDefinition theCollectionDefinition = null;
909                for (MaintainableCollectionDefinition maintainableCollectionDefinition : maintenanceDocumentDictionaryService.getMaintainableCollections(documentTypeName)) {
910                        if (maintainableCollectionDefinition.getBusinessObjectClass().equals(object.getClass())) {
911                                // we've found the collection we were looking for, so let's find all of its subcollections
912                                theCollectionDefinition = maintainableCollectionDefinition;
913                                break;
914                        }
915                }
916                if (theCollectionDefinition != null) {
917                        for (MaintainableCollectionDefinition subCollDef : theCollectionDefinition.getMaintainableCollections()) {
918                                String name = subCollDef.getName();
919                                String capitalFirst = name.substring(0, 1).toUpperCase();
920                                String methodName = "get" + capitalFirst + name.substring(1);
921                                List subCollectionList = new ArrayList();
922                                try {
923                                        subCollectionList = (List) object.getClass().getMethod(methodName).invoke(object);
924                                }
925                                catch (InvocationTargetException ite) {
926                                        // this shouldn't happen
927                                }
928                                catch (IllegalAccessException iae) {
929                                        // this shouldn't happen
930                                }
931                                catch (NoSuchMethodException nme) {
932                                        // this shouldn't happen
933                                }
934                                index += subCollectionList.size();
935                        }
936                }
937                return index;
938        }
939
940        /**
941         * Deletes a collection line that is pending by this document. The collection name and the index to delete is embedded into the
942         * delete button name. These parameters are extracted, the collection pulled out of the parent business object, and finally the
943         * collection record at the specified index is removed for the new maintainable, and the old if we are dealing with an edit.
944         */
945        public ActionForward deleteLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
946                KualiMaintenanceForm maintenanceForm = (KualiMaintenanceForm) form;
947                MaintenanceDocument document = (MaintenanceDocument) maintenanceForm.getDocument();
948                Maintainable oldMaintainable = document.getOldMaintainableObject();
949                Maintainable newMaintainable = document.getNewMaintainableObject();
950
951                String collectionName = extractCollectionName(request, KRADConstants.DELETE_LINE_METHOD);
952                if (collectionName == null) {
953                        LOG.error("Unable to get find collection name in request.");
954                        throw new RuntimeException("Unable to get find collection class in request.");
955                }
956
957                PersistableBusinessObject bo = newMaintainable.getBusinessObject();
958                Collection maintCollection = extractCollection(bo, collectionName);
959                if (collectionName == null) {
960                        LOG.error("Collection is null in parent business object.");
961                        throw new RuntimeException("Collection is null in parent business object.");
962                }
963
964                int deleteRecordIndex = getLineToDelete(request);
965                if (deleteRecordIndex < 0 || deleteRecordIndex > maintCollection.size() - 1) {
966                        if (collectionName == null) {
967                                LOG.error("Invalid index for deletion of collection record: " + deleteRecordIndex);
968                                throw new RuntimeException("Invalid index for deletion of collection record: " + deleteRecordIndex);
969                        }
970                }
971
972                ((List) maintCollection).remove(deleteRecordIndex);
973
974                // if it's either an edit or a copy, need to remove the collection from the old maintainable as well
975                if (KRADConstants.MAINTENANCE_EDIT_ACTION.equals(maintenanceForm.getMaintenanceAction()) ||
976                                KRADConstants.MAINTENANCE_COPY_ACTION.equals(maintenanceForm.getMaintenanceAction())) {
977                        bo = oldMaintainable.getBusinessObject();
978                        maintCollection = extractCollection(bo, collectionName);
979
980                        if (collectionName == null) {
981                                LOG.error("Collection is null in parent business object.");
982                                throw new RuntimeException("Collection is null in parent business object.");
983                        }
984
985                        ((List) maintCollection).remove(deleteRecordIndex);
986                }
987
988                // remove the tab state information of the tab that the deleted element originally occupied, so that it will keep tab states
989                // consistent
990                //        String parameter = (String) request.getAttribute(Constants.METHOD_TO_CALL_ATTRIBUTE);
991                //        String indexStr = StringUtils.substringBetween(parameter, Constants.METHOD_TO_CALL_PARM13_LEFT_DEL, Constants.METHOD_TO_CALL_PARM13_RIGHT_DEL);
992                //        int index = Integer.parseInt(indexStr);
993                //        maintenanceForm.removeTabState(index);
994
995
996                //      TODO: Should we keep this logic and continue using currentTabIndex as the key in the tabStates HashMap ?        
997                //        
998                //        String parameter = (String) request.getAttribute(Constants.METHOD_TO_CALL_ATTRIBUTE);
999                //        String indexStr = StringUtils.substringBetween(parameter, Constants.METHOD_TO_CALL_PARM13_LEFT_DEL, Constants.METHOD_TO_CALL_PARM13_RIGHT_DEL);
1000                //        // + 1 is for the fact that the first element of a collection is on the next tab
1001                //        int index = Integer.parseInt(indexStr) +  1;
1002                //        Map<String, String> tabStates = maintenanceForm.getTabStates();
1003                //        Map<String, String> copyOfTabStates = new HashMap<String, String>();
1004                //
1005                //        int incrementor = 0;
1006                //        for (String tabState : tabStates.keySet()) {
1007                //              String originalValue = maintenanceForm.getTabState(Integer.toString(incrementor));
1008                //            copyOfTabStates.put(Integer.toString(incrementor), originalValue);
1009                //            incrementor++;
1010                //        }
1011                //
1012                //        int i = index;
1013                //
1014                //        while (i < copyOfTabStates.size() ) {
1015                //            String originalValue = copyOfTabStates.get(Integer.toString(i));
1016                //            if (tabStates.containsKey(Integer.toString(i-1))) {
1017                //                tabStates.remove(Integer.toString(i-1));
1018                //            }
1019                //            tabStates.put(Integer.toString(i-1), originalValue);
1020                //            i++;
1021                //        }
1022                //
1023                //        
1024                //End of whether we should continue to keep this logic and use currentTabIndex as the key            
1025
1026                doProcessingAfterPost( (KualiMaintenanceForm) form, request );
1027
1028                return mapping.findForward(RiceConstants.MAPPING_BASIC);
1029        }
1030
1031        /**
1032         * Turns on (or off) the inactive record display for a maintenance collection.
1033         */
1034        public ActionForward toggleInactiveRecordDisplay(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1035                KualiMaintenanceForm maintenanceForm = (KualiMaintenanceForm) form;
1036                MaintenanceDocument document = (MaintenanceDocument) maintenanceForm.getDocument();
1037                Maintainable oldMaintainable = document.getOldMaintainableObject();
1038                Maintainable newMaintainable = document.getNewMaintainableObject();
1039
1040                String collectionName = extractCollectionName(request, KRADConstants.TOGGLE_INACTIVE_METHOD);
1041                if (collectionName == null) {
1042                        LOG.error("Unable to get find collection name in request.");
1043                        throw new RuntimeException("Unable to get find collection class in request.");
1044                }  
1045
1046                String parameterName = (String) request.getAttribute(KRADConstants.METHOD_TO_CALL_ATTRIBUTE);
1047                boolean showInactive = Boolean.parseBoolean(StringUtils.substringBetween(parameterName, KRADConstants.METHOD_TO_CALL_BOPARM_LEFT_DEL, "."));
1048
1049                oldMaintainable.setShowInactiveRecords(collectionName, showInactive);
1050                newMaintainable.setShowInactiveRecords(collectionName, showInactive);
1051
1052                return mapping.findForward(RiceConstants.MAPPING_BASIC);
1053        }
1054
1055        /**
1056         * This method clears the value of the primary key fields on a Business Object.
1057         * 
1058         * @param document - document to clear the pk fields on
1059         */
1060    protected void clearPrimaryKeyFields(MaintenanceDocument document) {
1061                // get business object being maintained and its keys
1062                PersistableBusinessObject bo = document.getNewMaintainableObject().getBusinessObject();
1063                List<String> keyFieldNames = KRADServiceLocatorWeb.getLegacyDataAdapter().listPrimaryKeyFieldNames(bo.getClass());
1064
1065                for (String keyFieldName : keyFieldNames) {
1066                        try {
1067                                ObjectUtils.setObjectProperty(bo, keyFieldName, null);
1068                        }
1069                        catch (Exception e) {
1070                                LOG.error("Unable to clear primary key field: " + e.getMessage());
1071                                throw new RuntimeException("Unable to clear primary key field: " + e.getMessage());
1072                        }
1073                }
1074        bo.setObjectId(null);
1075        bo.setVersionNumber(new Long(1));
1076        }
1077
1078        /**
1079         * This method is used as part of the Copy functionality, to clear any field values that the user making the copy does not have
1080         * permissions to modify. This will prevent authorization errors on a copy.
1081         * 
1082         * @param document - document to be adjusted
1083         */
1084    protected void clearUnauthorizedNewFields(MaintenanceDocument document) {
1085                // get a reference to the current user
1086                Person user = GlobalVariables.getUserSession().getPerson();
1087
1088                // get a new instance of MaintenanceDocumentAuthorizations for this context
1089                MaintenanceDocumentRestrictions maintenanceDocumentRestrictions = getBusinessObjectAuthorizationService().getMaintenanceDocumentRestrictions(document, user);
1090
1091                document.getNewMaintainableObject().clearBusinessObjectOfRestrictedValues(maintenanceDocumentRestrictions);
1092        }
1093
1094        /**
1095         * This method does all special processing on a document that should happen on each HTTP post (ie, save, route, approve, etc).
1096         * 
1097         * @param form
1098         */
1099        @SuppressWarnings("unchecked")
1100        protected void doProcessingAfterPost( KualiForm form, HttpServletRequest request ) {
1101                MaintenanceDocument document = (MaintenanceDocument) ((KualiMaintenanceForm)form).getDocument();
1102                Maintainable maintainable = document.getNewMaintainableObject();
1103                Object bo = maintainable.getBusinessObject();
1104
1105                getBusinessObjectService().linkUserFields(bo);
1106
1107                maintainable.processAfterPost(document, request.getParameterMap() );
1108        }
1109
1110        protected void doProcessingAfterPost( KualiForm form, Map<String,String[]> parameters ) {
1111                MaintenanceDocument document = (MaintenanceDocument) ((KualiMaintenanceForm)form).getDocument();
1112                Maintainable maintainable = document.getNewMaintainableObject();
1113                Object bo = maintainable.getBusinessObject();
1114
1115                getBusinessObjectService().linkUserFields(bo);
1116
1117                maintainable.processAfterPost(document, parameters );
1118        }
1119
1120        protected void populateAuthorizationFields(KualiDocumentFormBase formBase){
1121                super.populateAuthorizationFields(formBase);
1122
1123                KualiMaintenanceForm maintenanceForm = (KualiMaintenanceForm) formBase;
1124                MaintenanceDocument maintenanceDocument = (MaintenanceDocument) maintenanceForm.getDocument();
1125                MaintenanceDocumentAuthorizer maintenanceDocumentAuthorizer = (MaintenanceDocumentAuthorizer) getDocumentHelperService().getDocumentAuthorizer(maintenanceDocument);
1126                Person user = GlobalVariables.getUserSession().getPerson();
1127                maintenanceForm.setReadOnly(!formBase.getDocumentActions().containsKey(KRADConstants.KUALI_ACTION_CAN_EDIT));
1128                MaintenanceDocumentRestrictions maintenanceDocumentAuthorizations = getBusinessObjectAuthorizationService().getMaintenanceDocumentRestrictions(maintenanceDocument, user);
1129                maintenanceForm.setAuthorizations(maintenanceDocumentAuthorizations);
1130        }
1131
1132        public LookupService getLookupService() {
1133                if ( lookupService == null ) {
1134                        lookupService = KRADServiceLocatorWeb.getLookupService();
1135                }
1136                return this.lookupService;
1137        }
1138
1139        public LookupResultsService getLookupResultsService() {
1140                if ( lookupResultsService == null ) {
1141                        lookupResultsService = KNSServiceLocator.getLookupResultsService();
1142                }
1143                return this.lookupResultsService;
1144        }
1145
1146}