Coverage Report - org.kuali.rice.kns.document.MaintenanceDocumentBase
 
Classes in this File Line Coverage Branch Coverage Complexity
MaintenanceDocumentBase
0%
0/330
0%
0/130
2.61
 
 1  
 /*
 2  
  * Copyright 2005-2007 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.document;
 17  
 
 18  
 import java.io.FileNotFoundException;
 19  
 import java.io.IOException;
 20  
 import java.io.StringReader;
 21  
 import java.util.Collections;
 22  
 import java.util.LinkedHashMap;
 23  
 import java.util.List;
 24  
 
 25  
 import javax.persistence.Column;
 26  
 import javax.persistence.Entity;
 27  
 import javax.persistence.Table;
 28  
 import javax.persistence.Transient;
 29  
 import javax.xml.parsers.DocumentBuilder;
 30  
 import javax.xml.parsers.DocumentBuilderFactory;
 31  
 import javax.xml.parsers.ParserConfigurationException;
 32  
 
 33  
 import org.apache.commons.lang.StringUtils;
 34  
 import org.apache.ojb.broker.PersistenceBroker;
 35  
 import org.apache.ojb.broker.PersistenceBrokerException;
 36  
 import org.apache.ojb.broker.core.proxy.ProxyHelper;
 37  
 import org.apache.struts.upload.FormFile;
 38  
 import org.kuali.rice.kew.dto.DocumentRouteStatusChangeDTO;
 39  
 import org.kuali.rice.kim.bo.Person;
 40  
 import org.kuali.rice.kns.bo.BusinessObject;
 41  
 import org.kuali.rice.kns.bo.DocumentAttachment;
 42  
 import org.kuali.rice.kns.bo.DocumentHeader;
 43  
 import org.kuali.rice.kns.bo.GlobalBusinessObject;
 44  
 import org.kuali.rice.kns.bo.PersistableAttachment;
 45  
 import org.kuali.rice.kns.bo.PersistableBusinessObject;
 46  
 import org.kuali.rice.kns.datadictionary.DocumentEntry;
 47  
 import org.kuali.rice.kns.datadictionary.WorkflowAttributes;
 48  
 import org.kuali.rice.kns.datadictionary.WorkflowProperties;
 49  
 import org.kuali.rice.kns.exception.PessimisticLockingException;
 50  
 import org.kuali.rice.kns.exception.ValidationException;
 51  
 import org.kuali.rice.kns.maintenance.Maintainable;
 52  
 import org.kuali.rice.kns.rule.event.KualiDocumentEvent;
 53  
 import org.kuali.rice.kns.rule.event.SaveDocumentEvent;
 54  
 import org.kuali.rice.kns.service.DocumentHeaderService;
 55  
 import org.kuali.rice.kns.service.KNSServiceLocator;
 56  
 import org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService;
 57  
 import org.kuali.rice.kns.service.MaintenanceDocumentService;
 58  
 import org.kuali.rice.kns.util.GlobalVariables;
 59  
 import org.kuali.rice.kns.util.KNSConstants;
 60  
 import org.kuali.rice.kns.util.MaintenanceUtils;
 61  
 import org.kuali.rice.kns.util.ObjectUtils;
 62  
 import org.kuali.rice.kns.util.RiceKeyConstants;
 63  
 import org.kuali.rice.kns.util.documentserializer.PropertySerializabilityEvaluator;
 64  
 import org.kuali.rice.kns.workflow.service.KualiWorkflowDocument;
 65  
 import org.w3c.dom.Document;
 66  
 import org.w3c.dom.Node;
 67  
 import org.w3c.dom.NodeList;
 68  
 import org.xml.sax.InputSource;
 69  
 import org.xml.sax.SAXException;
 70  
 
 71  
 
 72  
 /**
 73  
  * The maintenance xml structure will be: <maintainableDocumentContents maintainableImplClass="className">
 74  
  * <oldMaintainableObject>... </oldMaintainableObject> <newMaintainableObject>... </newMaintainableObject>
 75  
  * </maintainableDocumentContents> Maintenance Document
 76  
  */
 77  
 @Entity
 78  
 @Table(name="KRNS_MAINT_DOC_T")
 79  
 public class MaintenanceDocumentBase extends DocumentBase implements MaintenanceDocument , SessionDocument{
 80  
     private static final long serialVersionUID = -505085142412593305L;
 81  0
     private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(MaintenanceDocumentBase.class);
 82  
     public static final String MAINTAINABLE_IMPL_CLASS = "maintainableImplClass";
 83  
     public static final String OLD_MAINTAINABLE_TAG_NAME = "oldMaintainableObject";
 84  
     public static final String NEW_MAINTAINABLE_TAG_NAME = "newMaintainableObject";
 85  
     public static final String MAINTENANCE_ACTION_TAG_NAME = "maintenanceAction";
 86  
     @Transient
 87  
     transient private static MaintenanceDocumentDictionaryService maintenanceDocumentDictionaryService;
 88  
     transient private static MaintenanceDocumentService maintenanceDocumentService;
 89  
     transient private static DocumentHeaderService documentHeaderService;
 90  
     
 91  
     @Transient
 92  
     protected Maintainable oldMaintainableObject;
 93  
     @Transient
 94  
     protected Maintainable newMaintainableObject;
 95  
 
 96  
     @Column(name="DOC_CNTNT", length=4096)
 97  
         protected String xmlDocumentContents;
 98  
     @Transient
 99  
     protected boolean fieldsClearedOnCopy;
 100  0
     @Transient
 101  
     protected boolean displayTopicFieldInNotes = false;
 102  
 
 103  
     @Transient
 104  
     protected transient FormFile fileAttachment;
 105  
     @Transient
 106  
     protected String attachmentPropertyName;
 107  
 
 108  
     // TODO JPA Annotate the DocumentAttachment class and hook it up to this
 109  
     @Transient
 110  
     protected DocumentAttachment attachment;
 111  
     
 112  
     public FormFile getFileAttachment() {
 113  0
         return this.fileAttachment;
 114  
     }
 115  
 
 116  
     public void setFileAttachment(FormFile fileAttachment) {
 117  0
         this.fileAttachment = fileAttachment;
 118  0
     }
 119  
 
 120  
     public String getAttachmentPropertyName() {
 121  0
         return this.attachmentPropertyName;
 122  
     }
 123  
 
 124  
     public void setAttachmentPropertyName(String attachmentPropertyName) {
 125  0
         this.attachmentPropertyName = attachmentPropertyName;
 126  0
     }
 127  
 
 128  
     public MaintenanceDocumentBase() {
 129  0
         super();
 130  0
         fieldsClearedOnCopy = false;
 131  0
     }
 132  
 
 133  
     /**
 134  
      * Initializies the maintainables.
 135  
      */
 136  
     public MaintenanceDocumentBase(String documentTypeName) {
 137  0
         this();
 138  0
         Class clazz = getMaintenanceDocumentDictionaryService().getMaintainableClass(documentTypeName);
 139  
         try {
 140  0
             oldMaintainableObject = (Maintainable) clazz.newInstance();
 141  0
             newMaintainableObject = (Maintainable) clazz.newInstance();
 142  
 
 143  
             // initialize maintainable with a business object
 144  0
             Class boClazz = getMaintenanceDocumentDictionaryService().getBusinessObjectClass(documentTypeName);
 145  0
             oldMaintainableObject.setBusinessObject((PersistableBusinessObject) boClazz.newInstance());
 146  0
             oldMaintainableObject.setBoClass(boClazz);
 147  0
             newMaintainableObject.setBusinessObject((PersistableBusinessObject) boClazz.newInstance());
 148  0
             newMaintainableObject.setBoClass(boClazz);
 149  
         }
 150  0
         catch (InstantiationException e) {
 151  0
             LOG.error("Unable to initialize maintainables of type " + clazz.getName());
 152  0
             throw new RuntimeException("Unable to initialize maintainables of type " + clazz.getName());
 153  
         }
 154  0
         catch (IllegalAccessException e) {
 155  0
             LOG.error("Unable to initialize maintainables of type " + clazz.getName());
 156  0
             throw new RuntimeException("Unable to initialize maintainables of type " + clazz.getName());
 157  0
         }
 158  0
     }
 159  
 
 160  
     /**
 161  
      * Builds out the document title for maintenance documents - this will get loaded into the flex doc and passed into workflow. It
 162  
      * will be searchable.
 163  
      */
 164  
     @Override
 165  
     public String getDocumentTitle() {
 166  0
         String documentTitle = "";
 167  
         
 168  0
         documentTitle = newMaintainableObject.getDocumentTitle(this);
 169  0
         if (StringUtils.isNotBlank(documentTitle)) {
 170  
             // if doc title has been overridden by maintainable, use it
 171  0
             return documentTitle;
 172  
         }
 173  
         
 174  
         // TODO - build out with bo label once we get the data dictionary stuff in place
 175  
         // build out the right classname
 176  0
         String className = newMaintainableObject.getBusinessObject().getClass().getName();
 177  0
         String truncatedClassName = className.substring(className.lastIndexOf('.') + 1);
 178  0
         if (isOldBusinessObjectInDocument()) {
 179  0
             documentTitle = "Edit ";
 180  
         }
 181  
         else {
 182  0
             documentTitle = "New ";
 183  
         }
 184  0
         documentTitle += truncatedClassName + " - ";
 185  0
         documentTitle += this.getDocumentHeader().getDocumentDescription() + " ";
 186  
         // TODO: talk with Aaron about the getKeysName replacement
 187  
         // HashMap keyVals = (HashMap) newMaintainableObject.getKeysNameAndValuePairs();
 188  
         // Set list = keyVals.keySet();
 189  
         // Iterator i = list.iterator();
 190  
         // int idx = 0;
 191  
         // while(i.hasNext()) {
 192  
         // String key = (String) i.next();
 193  
         // String value = (String) keyVals.get(key);
 194  
         // if(idx != 0) {
 195  
         // documentTitle += ", ";
 196  
         // }
 197  
         // documentTitle += key;
 198  
         // documentTitle += " = ";
 199  
         // documentTitle += value;
 200  
         // idx++;
 201  
         // }
 202  
         // documentTitle += " - ";
 203  
         // documentTitle += this.getDocumentHeader().getDocumentDescription();
 204  0
         return documentTitle;
 205  
     }
 206  
 
 207  
     /**
 208  
      * @param xmlDocument
 209  
      * @return
 210  
      */
 211  
     protected boolean isOldMaintainableInDocument(Document xmlDocument) {
 212  0
         boolean isOldMaintainableInExistence = false;
 213  0
         if (xmlDocument.getElementsByTagName(OLD_MAINTAINABLE_TAG_NAME).getLength() > 0) {
 214  0
             isOldMaintainableInExistence = true;
 215  
         }
 216  0
         return isOldMaintainableInExistence;
 217  
     }
 218  
 
 219  
 
 220  
     /**
 221  
      * Checks old maintainable bo has key values
 222  
      */
 223  
     public boolean isOldBusinessObjectInDocument() {
 224  0
         boolean isOldBusinessObjectInExistence = false;
 225  0
         if (oldMaintainableObject == null || oldMaintainableObject.getBusinessObject() == null) {
 226  0
             isOldBusinessObjectInExistence = false;
 227  
         } else {
 228  0
             isOldBusinessObjectInExistence = oldMaintainableObject.isOldBusinessObjectInDocument();
 229  
         }
 230  0
         return isOldBusinessObjectInExistence;
 231  
     }
 232  
 
 233  
     /**
 234  
      * This method is a simplified-naming wrapper around isOldBusinessObjectInDocument(), so that the method name matches the
 235  
      * functionality.
 236  
      */
 237  
     public boolean isNew() {
 238  0
         return MaintenanceUtils.isMaintenanceDocumentCreatingNewRecord(newMaintainableObject.getMaintenanceAction());
 239  
     }
 240  
 
 241  
     /**
 242  
      * This method is a simplified-naming wrapper around isOldBusinessObjectInDocument(), so that the method name matches the
 243  
      * functionality.
 244  
      */
 245  
     public boolean isEdit() {
 246  0
         if (KNSConstants.MAINTENANCE_EDIT_ACTION.equalsIgnoreCase(newMaintainableObject.getMaintenanceAction())) {
 247  0
             return true;
 248  
         }
 249  
         else {
 250  0
             return false;
 251  
         }
 252  
         // return isOldBusinessObjectInDocument();
 253  
     }
 254  
 
 255  
     public boolean isNewWithExisting() {
 256  0
         if (KNSConstants.MAINTENANCE_NEWWITHEXISTING_ACTION.equalsIgnoreCase(newMaintainableObject.getMaintenanceAction())) {
 257  0
             return true;
 258  
         }
 259  
         else {
 260  0
             return false;
 261  
         }
 262  
     }
 263  
 
 264  
     public void populateMaintainablesFromXmlDocumentContents() {
 265  
         // get a hold of the parsed xml document, then read the classname,
 266  
         // then instantiate one to two instances depending on content
 267  
         // then populate those instances
 268  0
         if (!StringUtils.isEmpty(xmlDocumentContents)) {
 269  0
             DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
 270  
 
 271  
             try {
 272  0
                 DocumentBuilder builder = factory.newDocumentBuilder();
 273  0
                 Document xmlDocument = builder.parse(new InputSource(new StringReader(xmlDocumentContents)));
 274  0
                 String clazz = xmlDocument.getDocumentElement().getAttribute(MAINTAINABLE_IMPL_CLASS);
 275  0
                 if (isOldMaintainableInDocument(xmlDocument)) {
 276  0
                     oldMaintainableObject = (Maintainable) Class.forName(clazz).newInstance();
 277  0
                     PersistableBusinessObject bo = getBusinessObjectFromXML(OLD_MAINTAINABLE_TAG_NAME);
 278  
 
 279  0
                     String oldMaintenanceAction = getMaintenanceAction(xmlDocument, OLD_MAINTAINABLE_TAG_NAME);
 280  0
                     oldMaintainableObject.setMaintenanceAction(oldMaintenanceAction);
 281  
 
 282  0
                     oldMaintainableObject.setBusinessObject(bo);
 283  0
                     oldMaintainableObject.setBoClass(bo.getClass());
 284  
                 }
 285  0
                 newMaintainableObject = (Maintainable) Class.forName(clazz).newInstance();
 286  0
                 PersistableBusinessObject bo = getBusinessObjectFromXML(NEW_MAINTAINABLE_TAG_NAME);
 287  0
                 newMaintainableObject.setBusinessObject(bo);
 288  0
                 newMaintainableObject.setBoClass(bo.getClass());
 289  
 
 290  0
                 String newMaintenanceAction = getMaintenanceAction(xmlDocument, NEW_MAINTAINABLE_TAG_NAME);
 291  0
                 newMaintainableObject.setMaintenanceAction(newMaintenanceAction);
 292  
 
 293  
             }
 294  0
             catch (ParserConfigurationException e) {
 295  0
                 LOG.error("Error while parsing document contents", e);
 296  0
                 throw new RuntimeException("Could not load document contents from xml", e);
 297  
             }
 298  0
             catch (SAXException e) {
 299  0
                 LOG.error("Error while parsing document contents", e);
 300  0
                 throw new RuntimeException("Could not load document contents from xml", e);
 301  
             }
 302  0
             catch (IOException e) {
 303  0
                 LOG.error("Error while parsing document contents", e);
 304  0
                 throw new RuntimeException("Could not load document contents from xml", e);
 305  
             }
 306  0
             catch (InstantiationException e) {
 307  0
                 LOG.error("Error while parsing document contents", e);
 308  0
                 throw new RuntimeException("Could not load document contents from xml", e);
 309  
             }
 310  0
             catch (IllegalAccessException e) {
 311  0
                 LOG.error("Error while parsing document contents", e);
 312  0
                 throw new RuntimeException("Could not load document contents from xml", e);
 313  
             }
 314  0
             catch (ClassNotFoundException e) {
 315  0
                 LOG.error("Error while parsing document contents", e);
 316  0
                 throw new RuntimeException("Could not load document contents from xml", e);
 317  0
             }
 318  
 
 319  
         }
 320  0
     }
 321  
 
 322  
     /**
 323  
      * This method is a lame containment of ugly DOM walking code. This is ONLY necessary because of the version conflicts between
 324  
      * Xalan.jar in 2.6.x and 2.7. As soon as we can upgrade to 2.7, this will be switched to using XPath, which is faster and much
 325  
      * easier on the eyes.
 326  
      *
 327  
      * @param xmlDocument
 328  
      * @param oldOrNewElementName - String oldMaintainableObject or newMaintainableObject
 329  
      * @return the value of the element, or null if none was there
 330  
      */
 331  
     protected String getMaintenanceAction(Document xmlDocument, String oldOrNewElementName) {
 332  
 
 333  0
         if (StringUtils.isBlank(oldOrNewElementName)) {
 334  0
             throw new IllegalArgumentException("oldOrNewElementName may not be blank, null, or empty-string.");
 335  
         }
 336  
 
 337  0
         String maintenanceAction = null;
 338  0
         NodeList rootChildren = xmlDocument.getDocumentElement().getChildNodes();
 339  0
         for (int i = 0; i < rootChildren.getLength(); i++) {
 340  0
             Node rootChild = rootChildren.item(i);
 341  0
             if (oldOrNewElementName.equalsIgnoreCase(rootChild.getNodeName())) {
 342  0
                 NodeList maintChildren = rootChild.getChildNodes();
 343  0
                 for (int j = 0; j < maintChildren.getLength(); j++) {
 344  0
                     Node maintChild = maintChildren.item(j);
 345  0
                     if (MAINTENANCE_ACTION_TAG_NAME.equalsIgnoreCase(maintChild.getNodeName())) {
 346  0
                         maintenanceAction = maintChild.getChildNodes().item(0).getNodeValue();
 347  
                     }
 348  
                 }
 349  
             }
 350  
         }
 351  0
         return maintenanceAction;
 352  
     }
 353  
 
 354  
     /**
 355  
      * Retrieves substring of document contents from maintainable tag name. Then use xml service to translate xml into a business
 356  
      * object.
 357  
      */
 358  
     protected PersistableBusinessObject getBusinessObjectFromXML(String maintainableTagName) {
 359  0
         String maintXml = StringUtils.substringBetween(xmlDocumentContents, "<" + maintainableTagName + ">", "</" + maintainableTagName + ">");
 360  0
         PersistableBusinessObject businessObject = (PersistableBusinessObject) KNSServiceLocator.getXmlObjectSerializerService().fromXml(maintXml);
 361  0
         return businessObject;
 362  
     }
 363  
 
 364  
     /**
 365  
      * Populates the xml document contents from the maintainables.
 366  
      *
 367  
      * @see org.kuali.rice.kns.document.MaintenanceDocument#populateXmlDocumentContentsFromMaintainables()
 368  
      */
 369  
     public void populateXmlDocumentContentsFromMaintainables() {
 370  0
         StringBuffer docContentBuffer = new StringBuffer();
 371  0
         docContentBuffer.append("<maintainableDocumentContents maintainableImplClass=\"").append(newMaintainableObject.getClass().getName()).append("\">");
 372  0
         if (oldMaintainableObject != null && oldMaintainableObject.getBusinessObject() != null) {
 373  
             // TODO: refactor this out into a method
 374  0
             docContentBuffer.append("<" + OLD_MAINTAINABLE_TAG_NAME + ">");
 375  
 
 376  0
             PersistableBusinessObject oldBo = oldMaintainableObject.getBusinessObject();
 377  0
             ObjectUtils.materializeAllSubObjects(oldBo); // hack to resolve XStream not dealing well with Proxies
 378  0
             docContentBuffer.append(KNSServiceLocator.getBusinessObjectSerializerService().serializeBusinessObjectToXml(oldBo));
 379  
 
 380  
             // add the maintainable's maintenanceAction
 381  0
             docContentBuffer.append("<" + MAINTENANCE_ACTION_TAG_NAME + ">");
 382  0
             docContentBuffer.append(oldMaintainableObject.getMaintenanceAction());
 383  0
             docContentBuffer.append("</" + MAINTENANCE_ACTION_TAG_NAME + ">\n");
 384  
 
 385  0
             docContentBuffer.append("</" + OLD_MAINTAINABLE_TAG_NAME + ">");
 386  
         }
 387  0
         docContentBuffer.append("<" + NEW_MAINTAINABLE_TAG_NAME + ">");
 388  
 
 389  0
         PersistableBusinessObject newBo = newMaintainableObject.getBusinessObject();
 390  0
         ObjectUtils.materializeAllSubObjects(newBo); // hack to resolve XStream not dealing well with Proxies
 391  0
         docContentBuffer.append(KNSServiceLocator.getBusinessObjectSerializerService().serializeBusinessObjectToXml(newBo));
 392  
 
 393  
         // add the maintainable's maintenanceAction
 394  0
         docContentBuffer.append("<" + MAINTENANCE_ACTION_TAG_NAME + ">");
 395  0
         docContentBuffer.append(newMaintainableObject.getMaintenanceAction());
 396  0
         docContentBuffer.append("</" + MAINTENANCE_ACTION_TAG_NAME + ">\n");
 397  
 
 398  0
         docContentBuffer.append("</" + NEW_MAINTAINABLE_TAG_NAME + ">");
 399  0
         docContentBuffer.append("</maintainableDocumentContents>");
 400  0
         xmlDocumentContents = docContentBuffer.toString();
 401  0
     }
 402  
 
 403  
     /**
 404  
      * @see org.kuali.rice.kns.document.DocumentBase#doRouteStatusChange(org.kuali.rice.kew.dto.DocumentRouteStatusChangeDTO)
 405  
      */
 406  
     @Override
 407  
     public void doRouteStatusChange(DocumentRouteStatusChangeDTO statusChangeEvent) {
 408  0
         super.doRouteStatusChange(statusChangeEvent);
 409  
 
 410  0
         KualiWorkflowDocument workflowDocument = getDocumentHeader().getWorkflowDocument();
 411  0
         getNewMaintainableObject().doRouteStatusChange(getDocumentHeader());
 412  
         // commit the changes to the Maintainable BusinessObject when it goes to Processed (ie, fully approved),
 413  
         // and also unlock it
 414  0
         if (workflowDocument.stateIsProcessed()) {
 415  0
             String documentNumber = getDocumentHeader().getDocumentNumber();
 416  0
             newMaintainableObject.setDocumentNumber(documentNumber);
 417  
             
 418  
             //Populate Attachment Property
 419  0
             if(newMaintainableObject.getBusinessObject() instanceof PersistableAttachment) {
 420  0
                 populateAttachmentForBO();
 421  
             }
 422  
             
 423  0
             newMaintainableObject.saveBusinessObject();
 424  
             
 425  
             //Attachment should be deleted from Maintenance Document attachment table
 426  0
             deleteDocumentAttachment();  
 427  
             
 428  0
             getMaintenanceDocumentService().deleteLocks(documentNumber);
 429  
             
 430  
                 //for issue 3070, check if delete record
 431  0
                          if(this.checkAllowsRecordDeletion() && this.checkMaintenanceAction() &&
 432  
                                          this.checkDeletePermission(newMaintainableObject.getBusinessObject()))
 433  0
                                  newMaintainableObject.deleteBusinessObject(); 
 434  
         }
 435  
 
 436  
         // unlock the document when its canceled or disapproved
 437  0
         if (workflowDocument.stateIsCanceled() || workflowDocument.stateIsDisapproved()) {
 438  
             //Attachment should be deleted from Maintenance Document attachment table
 439  0
             deleteDocumentAttachment();  
 440  
             
 441  0
             String documentNumber = getDocumentHeader().getDocumentNumber();
 442  0
             getMaintenanceDocumentService().deleteLocks(documentNumber);
 443  
         }
 444  0
     }
 445  
     
 446  
     @Override
 447  
     /**
 448  
      * @see org.kuali.rice.kns.document.DocumentBase#getWorkflowEngineDocumentIdsToLock()
 449  
      */
 450  
         public List<Long> getWorkflowEngineDocumentIdsToLock() {
 451  0
             if ( newMaintainableObject != null ) {
 452  0
                     return newMaintainableObject.getWorkflowEngineDocumentIdsToLock();
 453  
             }
 454  0
             return Collections.emptyList();
 455  
         }
 456  
 
 457  
     /**
 458  
      * Pre-Save hook.
 459  
      *
 460  
      * @see org.kuali.rice.kns.document.Document#prepareForSave()
 461  
      */
 462  
     @Override
 463  
     public void prepareForSave() {
 464  0
         if (newMaintainableObject != null) {
 465  0
             newMaintainableObject.prepareForSave();
 466  
         }
 467  0
     }
 468  
 
 469  
     /**
 470  
      * @see org.kuali.rice.kns.document.DocumentBase#processAfterRetrieve()
 471  
      */
 472  
     @Override
 473  
     public void processAfterRetrieve() {
 474  0
         super.processAfterRetrieve();
 475  
         
 476  0
         populateMaintainablesFromXmlDocumentContents();
 477  0
         if (oldMaintainableObject != null) {
 478  0
                 oldMaintainableObject.setDocumentNumber(documentNumber);
 479  
         }
 480  0
         if (newMaintainableObject != null) {
 481  0
                 newMaintainableObject.setDocumentNumber(documentNumber);
 482  0
             newMaintainableObject.processAfterRetrieve();
 483  
             // If a maintenance lock exists, warn the user.
 484  0
             checkForLockingDocument(false);
 485  
         }
 486  0
     }
 487  
 
 488  
     /**
 489  
      * @return Returns the newMaintainableObject.
 490  
      */
 491  
     public Maintainable getNewMaintainableObject() {
 492  0
         return newMaintainableObject;
 493  
     }
 494  
 
 495  
     /**
 496  
      * @param newMaintainableObject The newMaintainableObject to set.
 497  
      */
 498  
     public void setNewMaintainableObject(Maintainable newMaintainableObject) {
 499  0
         this.newMaintainableObject = newMaintainableObject;
 500  0
     }
 501  
 
 502  
     /**
 503  
      * @return Returns the oldMaintainableObject.
 504  
      */
 505  
     public Maintainable getOldMaintainableObject() {
 506  0
         return oldMaintainableObject;
 507  
     }
 508  
 
 509  
     /**
 510  
      * @param oldMaintainableObject The oldMaintainableObject to set.
 511  
      */
 512  
     public void setOldMaintainableObject(Maintainable oldMaintainableObject) {
 513  0
         this.oldMaintainableObject = oldMaintainableObject;
 514  0
     }
 515  
 
 516  
     @Override
 517  
     public void setDocumentNumber(String documentNumber) {
 518  0
         super.setDocumentNumber(documentNumber);
 519  
 
 520  
         // set the finDocNumber on the Maintainable
 521  0
         oldMaintainableObject.setDocumentNumber(documentNumber);
 522  0
         newMaintainableObject.setDocumentNumber(documentNumber);
 523  
 
 524  0
     }
 525  
 
 526  
     /**
 527  
      * Gets the fieldsClearedOnCopy attribute.
 528  
      *
 529  
      * @return Returns the fieldsClearedOnCopy.
 530  
      */
 531  
     public final boolean isFieldsClearedOnCopy() {
 532  0
         return fieldsClearedOnCopy;
 533  
     }
 534  
 
 535  
     /**
 536  
      * Sets the fieldsClearedOnCopy attribute value.
 537  
      *
 538  
      * @param fieldsClearedOnCopy The fieldsClearedOnCopy to set.
 539  
      */
 540  
     public final void setFieldsClearedOnCopy(boolean fieldsClearedOnCopy) {
 541  0
         this.fieldsClearedOnCopy = fieldsClearedOnCopy;
 542  0
     }
 543  
 
 544  
     /**
 545  
      * @see org.kuali.rice.kns.bo.BusinessObjectBase#toStringMapper()
 546  
      */
 547  
     @SuppressWarnings("unchecked")
 548  
         @Override
 549  
     protected LinkedHashMap toStringMapper() {
 550  0
         LinkedHashMap m = new LinkedHashMap();
 551  
 
 552  0
         m.put("versionNumber", getVersionNumber());
 553  0
         m.put("comp", Boolean.valueOf(getDocumentHeader().getWorkflowDocument().isCompletionRequested()));
 554  0
         m.put("app", Boolean.valueOf(getDocumentHeader().getWorkflowDocument().isApprovalRequested()));
 555  0
         m.put("ack", Boolean.valueOf(getDocumentHeader().getWorkflowDocument().isAcknowledgeRequested()));
 556  0
         m.put("fyi", Boolean.valueOf(getDocumentHeader().getWorkflowDocument().isFYIRequested()));
 557  
 
 558  0
         return m;
 559  
     }
 560  
 
 561  
     /**
 562  
      * Gets the xmlDocumentContents attribute.
 563  
      *
 564  
      * @return Returns the xmlDocumentContents.
 565  
      */
 566  
     public String getXmlDocumentContents() {
 567  0
         return xmlDocumentContents;
 568  
     }
 569  
 
 570  
     /**
 571  
      * Sets the xmlDocumentContents attribute value.
 572  
      *
 573  
      * @param xmlDocumentContents The xmlDocumentContents to set.
 574  
      */
 575  
     public void setXmlDocumentContents(String xmlDocumentContents) {
 576  0
         this.xmlDocumentContents = xmlDocumentContents;
 577  0
     }
 578  
 
 579  
     /**
 580  
      * @see org.kuali.rice.kns.document.Document#getAllowsCopy()
 581  
      */
 582  
     public boolean getAllowsCopy() {
 583  0
         return getMaintenanceDocumentDictionaryService().getAllowsCopy(this);
 584  
     }
 585  
 
 586  
     /**
 587  
      * @see org.kuali.rice.kns.document.MaintenanceDocument#getDisplayTopicFieldInNotes()
 588  
      */
 589  
     public boolean getDisplayTopicFieldInNotes() {
 590  0
         return displayTopicFieldInNotes;
 591  
     }
 592  
 
 593  
     /**
 594  
      * @see org.kuali.rice.kns.document.MaintenanceDocument#setDisplayTopicFieldInNotes(boolean)
 595  
      */
 596  
     public void setDisplayTopicFieldInNotes(boolean displayTopicFieldInNotes) {
 597  0
         this.displayTopicFieldInNotes = displayTopicFieldInNotes;
 598  0
     }
 599  
 
 600  
     @Override
 601  
     /**
 602  
      * Overridden to avoid serializing the xml twice, because of the xmlDocumentContents property of this object
 603  
      */
 604  
     public String serializeDocumentToXml() {
 605  0
                 String tempXmlDocumentContents = xmlDocumentContents;
 606  0
                 xmlDocumentContents = null;
 607  0
                 String xmlForWorkflow = super.serializeDocumentToXml();
 608  0
                 xmlDocumentContents = tempXmlDocumentContents;
 609  0
                 return xmlForWorkflow;
 610  
     }
 611  
 
 612  
     @Override
 613  
     public void prepareForSave(KualiDocumentEvent event) {
 614  0
         super.prepareForSave(event);
 615  
         
 616  0
         populateDocumentAttachment();
 617  0
         populateXmlDocumentContentsFromMaintainables();
 618  0
     }
 619  
     
 620  
     /**
 621  
      * The attachment BO is proxied in OJB.  For some reason when an attachment does not yet
 622  
      * exist, refreshReferenceObject is not returning null and the proxy cannot be materialized.
 623  
      * So, this method exists to properly handle the proxied attachment BO.  This is a hack
 624  
      * and should be removed post JPA migration.
 625  
      */
 626  
     protected void refreshAttachment() {
 627  0
             if (ObjectUtils.isNull(attachment)) {
 628  0
             this.refreshReferenceObject("attachment");
 629  0
             final boolean isProxy = attachment != null && ProxyHelper.isProxy(attachment);
 630  0
             if (isProxy && ProxyHelper.getRealObject(attachment) == null) {
 631  0
                     attachment = null;
 632  
             }
 633  
         }
 634  0
     }
 635  
     
 636  
     protected void populateAttachmentForBO() {
 637  0
             refreshAttachment();
 638  
         
 639  0
         PersistableAttachment boAttachment = (PersistableAttachment) newMaintainableObject.getBusinessObject();
 640  
         
 641  0
         if(attachment != null) {
 642  
             byte[] fileContents;
 643  0
             fileContents = attachment.getAttachmentContent();
 644  0
             if (fileContents.length > 0) {
 645  0
                 boAttachment.setAttachmentContent(fileContents);
 646  0
                 boAttachment.setFileName(attachment.getFileName());
 647  0
                 boAttachment.setContentType(attachment.getContentType());
 648  
             }
 649  
        }  
 650  
        
 651  0
     }
 652  
     
 653  
     public void populateDocumentAttachment() {
 654  0
             refreshAttachment();
 655  
         
 656  0
         if(fileAttachment != null && StringUtils.isNotEmpty(fileAttachment.getFileName())) {
 657  
             //Populate DocumentAttachment BO
 658  0
             if(attachment == null) {
 659  0
                 attachment = new DocumentAttachment();
 660  
             }
 661  
             
 662  
             byte[] fileContents;
 663  
             try {
 664  0
                 fileContents = fileAttachment.getFileData();
 665  0
                 if (fileContents.length > 0) {
 666  0
                     attachment.setFileName(fileAttachment.getFileName());
 667  0
                     attachment.setContentType(fileAttachment.getContentType());
 668  0
                     attachment.setAttachmentContent(fileAttachment.getFileData());
 669  0
                     attachment.setDocumentNumber(getDocumentNumber());
 670  
                 }
 671  0
             }catch (FileNotFoundException e) {
 672  0
                 LOG.error("Error while populating the Document Attachment", e);
 673  0
                 throw new RuntimeException("Could not populate DocumentAttachment object", e);
 674  0
             }catch (IOException e) {
 675  0
                 LOG.error("Error while populating the Document Attachment", e);
 676  0
                 throw new RuntimeException("Could not populate DocumentAttachment object", e);
 677  0
             } 
 678  
             
 679  
         } 
 680  
 //        else if(attachment != null) {
 681  
 //            //Attachment has been deleted - Need to delete the Attachment Reference Object
 682  
 //            deleteAttachment();
 683  
 //        }
 684  0
     }
 685  
     
 686  
     public void deleteDocumentAttachment() { 
 687  0
         KNSServiceLocator.getBusinessObjectService().delete(attachment);
 688  0
         attachment = null;     
 689  0
     }
 690  
     
 691  
     /**
 692  
      * Explicitly NOT calling super here.  This is a complete override of the validation 
 693  
      * rules behavior.
 694  
      * 
 695  
      * @see org.kuali.rice.kns.document.DocumentBase#validateBusinessRules(org.kuali.rice.kns.rule.event.KualiDocumentEvent)
 696  
      */
 697  
     public void validateBusinessRules(KualiDocumentEvent event) {
 698  0
         if (GlobalVariables.getMessageMap().hasErrors()) {
 699  0
             logErrors();
 700  0
             throw new ValidationException("errors occured before business rule");
 701  
         }
 702  
 
 703  
         // check for locking documents for MaintenanceDocuments
 704  0
         if (this instanceof MaintenanceDocument) {
 705  0
             checkForLockingDocument(true);
 706  
         }
 707  
 
 708  
         // Make sure the business object's version number matches that of the database's copy.
 709  0
         if (newMaintainableObject != null) {
 710  0
                 if ( KNSServiceLocator.getPersistenceStructureService().isPersistable( newMaintainableObject.getBusinessObject().getClass() ) ) {
 711  0
                         PersistableBusinessObject pbObject = KNSServiceLocator.getBusinessObjectService().retrieve(newMaintainableObject.getBusinessObject());
 712  0
                         Long pbObjectVerNbr = ObjectUtils.isNull(pbObject) ? null : pbObject.getVersionNumber();
 713  0
                         Long newObjectVerNbr = newMaintainableObject.getBusinessObject().getVersionNumber();
 714  0
                         if (pbObjectVerNbr != null && !(pbObjectVerNbr.equals(newObjectVerNbr))) {
 715  0
                                 GlobalVariables.getMessageMap().putError(KNSConstants.GLOBAL_ERRORS, RiceKeyConstants.ERROR_VERSION_MISMATCH);
 716  0
                                 throw new ValidationException("Version mismatch between the local business object and the database business object");
 717  
                         }
 718  
                 }
 719  
         }
 720  
         
 721  
         // perform validation against rules engine
 722  0
         if ( LOG.isInfoEnabled() ) {
 723  0
             LOG.info("invoking rules engine on document " + getDocumentNumber());
 724  
         }
 725  0
         boolean isValid = true;
 726  0
         isValid = KNSServiceLocator.getKualiRuleService().applyRules(event);
 727  
 
 728  
         // check to see if the br eval passed or failed
 729  0
         if (!isValid) {
 730  0
             logErrors();
 731  
             // TODO: better error handling at the lower level and a better error message are
 732  
             // needed here
 733  0
             throw new ValidationException("business rule evaluation failed");
 734  
         }
 735  0
         else if (GlobalVariables.getMessageMap().hasErrors()) {
 736  0
             logErrors();
 737  0
             if (event instanceof SaveDocumentEvent) {
 738  
                 // for maintenance documents, we want to always actually do a save if the
 739  
                 // user requests a save, even if there are validation or business rules
 740  
                 // failures. this empty if does this, and allows the document to be saved,
 741  
                 // even if there are failures.
 742  
                 // BR or validation failures on a ROUTE even should always stop the route,
 743  
                 // that has not changed
 744  
             }
 745  
             else {
 746  0
                 throw new ValidationException("Unreported errors occured during business rule evaluation (rule developer needs to put meaningful error messages into global ErrorMap)");
 747  
             }
 748  
         }
 749  0
         LOG.debug("validation completed");
 750  
 
 751  0
     }
 752  
 
 753  
 
 754  
     protected void checkForLockingDocument(boolean throwExceptionIfLocked) {
 755  0
         MaintenanceUtils.checkForLockingDocument(this, throwExceptionIfLocked);
 756  0
     }
 757  
 
 758  
     /**
 759  
      * this needs to happen after the document itself is saved, to preserve consistency of the ver_nbr and in the case of initial
 760  
      * save, because this can't be saved until the document is saved initially
 761  
      * 
 762  
      * @see org.kuali.rice.kns.document.DocumentBase#postProcessSave(org.kuali.rice.kns.rule.event.KualiDocumentEvent)
 763  
      */
 764  
     @Override
 765  
     public void postProcessSave(KualiDocumentEvent event) {
 766  0
         PersistableBusinessObject bo = getNewMaintainableObject().getBusinessObject();
 767  0
         if (bo instanceof GlobalBusinessObject) {
 768  0
             KNSServiceLocator.getBusinessObjectService().save(bo);
 769  
         }
 770  
         //currently only global documents could change the list of what they're affecting during routing,
 771  
         //so could restrict this to only happening with them, but who knows if that will change, so safest
 772  
         //to always do the delete and re-add...seems a bit inefficient though if nothing has changed, which is
 773  
         //most of the time...could also try to only add/update/delete what's changed, but this is easier
 774  0
         if (!(event instanceof SaveDocumentEvent)) { //don't lock until they route
 775  0
             getMaintenanceDocumentService().deleteLocks(this.getDocumentNumber());
 776  0
             getMaintenanceDocumentService().storeLocks(this.getNewMaintainableObject().generateMaintenanceLocks());
 777  
         }
 778  0
     }
 779  
     
 780  
     /**
 781  
      * @see org.kuali.rice.kns.document.DocumentBase#getDocumentBusinessObject()
 782  
      */
 783  
     @Override
 784  
     public PersistableBusinessObject getDocumentBusinessObject() {
 785  0
         if(documentBusinessObject==null) {
 786  0
             documentBusinessObject=this.newMaintainableObject.getBusinessObject();
 787  
         }
 788  0
         return documentBusinessObject;
 789  
     }
 790  
 
 791  
     @Override
 792  
     public PropertySerializabilityEvaluator getDocumentPropertySerizabilityEvaluator() {
 793  0
             String docTypeName = "";
 794  0
             if ( newMaintainableObject != null ) {
 795  0
                     docTypeName = getMaintenanceDocumentDictionaryService().getDocumentTypeName(this.newMaintainableObject.getBoClass());
 796  
             } else { // I don't know why we aren't just using the header in the first place
 797  
                             // but, in the case where we can't get it in the way above, attempt to get
 798  
                             // it off the workflow document header
 799  0
                     if ( getDocumentHeader() != null && getDocumentHeader().getWorkflowDocument() != null ) {
 800  0
                             docTypeName = getDocumentHeader().getWorkflowDocument().getDocumentType();
 801  
                     }
 802  
             }
 803  0
             if ( !StringUtils.isBlank(docTypeName) ) {
 804  0
                 DocumentEntry documentEntry = getMaintenanceDocumentDictionaryService().getMaintenanceDocumentEntry(docTypeName);
 805  0
                 if ( documentEntry != null ) {
 806  0
                         WorkflowProperties workflowProperties = documentEntry.getWorkflowProperties();
 807  0
                         WorkflowAttributes workflowAttributes = documentEntry.getWorkflowAttributes();
 808  0
                         return createPropertySerializabilityEvaluator(workflowProperties, workflowAttributes);
 809  
                 } else {
 810  0
                         LOG.error( "Unable to obtain DD DocumentEntry for document type: '" + docTypeName + "'" );
 811  
                 }
 812  0
             } else {
 813  0
                 LOG.error( "Unable to obtain document type name for this document: " + this );
 814  
             }
 815  0
             LOG.error( "Returning null for the PropertySerializabilityEvaluator" );
 816  0
         return null;
 817  
     }
 818  
     
 819  
     public DocumentAttachment getAttachment() {
 820  0
         return this.attachment;
 821  
     }
 822  
 
 823  
     public void setAttachment(DocumentAttachment attachment) {
 824  0
         this.attachment = attachment;
 825  0
     }
 826  
     
 827  
     /**
 828  
      * This overridden method is used to delete the {@link DocumentHeader} object due to the system not being able to manage the {@link DocumentHeader} object via mapping files
 829  
      * 
 830  
      * @see org.kuali.rice.kns.bo.PersistableBusinessObjectBase#afterDelete(org.apache.ojb.broker.PersistenceBroker)
 831  
      */
 832  
     @Override
 833  
     public void afterDelete(PersistenceBroker persistenceBroker) throws PersistenceBrokerException {
 834  0
         getDocumentHeaderService().deleteDocumentHeader(getDocumentHeader());
 835  0
         super.afterDelete(persistenceBroker);
 836  0
     }
 837  
 
 838  
     /**
 839  
      * This overridden method is used to retrieve the {@link DocumentHeader} object due to the system not being able to manage the {@link DocumentHeader} object via mapping files
 840  
      * 
 841  
      * @see org.kuali.rice.kns.bo.PersistableBusinessObjectBase#afterLookup(org.apache.ojb.broker.PersistenceBroker)
 842  
      */
 843  
     @Override
 844  
     public void afterLookup(PersistenceBroker persistenceBroker) throws PersistenceBrokerException {
 845  0
         setDocumentHeader(getDocumentHeaderService().getDocumentHeaderById(getDocumentNumber()));
 846  0
         super.afterLookup(persistenceBroker);
 847  0
     }
 848  
 
 849  
     /**
 850  
      * This overridden method is used to insert the {@link DocumentHeader} object due to the system not being able to manage the {@link DocumentHeader} object via mapping files
 851  
      * 
 852  
      * @see org.kuali.rice.kns.bo.PersistableBusinessObjectBase#beforeInsert(org.apache.ojb.broker.PersistenceBroker)
 853  
      */
 854  
     @Override
 855  
     public void beforeInsert(PersistenceBroker persistenceBroker) throws PersistenceBrokerException {
 856  0
         getDocumentHeaderService().saveDocumentHeader(getDocumentHeader());
 857  0
         super.beforeInsert(persistenceBroker);
 858  0
     }
 859  
 
 860  
     /**
 861  
      * This overridden method is used to save the {@link DocumentHeader} object due to the system not being able to manage the {@link DocumentHeader} object via mapping files
 862  
      * 
 863  
      * @see org.kuali.rice.kns.bo.PersistableBusinessObjectBase#beforeUpdate(org.apache.ojb.broker.PersistenceBroker)
 864  
      */
 865  
     @Override
 866  
     public void beforeUpdate(PersistenceBroker persistenceBroker) throws PersistenceBrokerException {
 867  0
         getDocumentHeaderService().saveDocumentHeader(getDocumentHeader());
 868  0
         super.beforeUpdate(persistenceBroker);
 869  0
     }
 870  
     
 871  
     /**
 872  
      * 
 873  
      * This method to check whether the document class implements SessionDocument
 874  
      * 
 875  
      * @return
 876  
      */
 877  
     public boolean isSessionDocument() {
 878  0
         return SessionDocument.class.isAssignableFrom(this.getClass());
 879  
     }
 880  
 
 881  
     /**
 882  
      * Returns whether or not the new maintainable object supports custom lock descriptors. Will always return false if the new maintainable is null.
 883  
      * 
 884  
      * @see org.kuali.rice.kns.document.Document#useCustomLockDescriptors()
 885  
      * @see org.kuali.rice.kns.maintenance.Maintainable#useCustomLockDescriptors()
 886  
      */
 887  
     @Override
 888  
     public boolean useCustomLockDescriptors() {
 889  0
             return (newMaintainableObject != null && newMaintainableObject.useCustomLockDescriptors());
 890  
     }
 891  
 
 892  
     /**
 893  
      * Returns the custom lock descriptor generated by the new maintainable object, if defined. Will throw a PessimisticLockingException if
 894  
      * the new maintainable is null.
 895  
      * 
 896  
      * @see org.kuali.rice.kns.document.Document#getCustomLockDescriptor(org.kuali.rice.kim.bo.Person)
 897  
      * @see org.kuali.rice.kns.maintenance.Maintainable#getCustomLockDescriptor(org.kuali.rice.kim.bo.Person)
 898  
      */
 899  
     @Override
 900  
     public String getCustomLockDescriptor(Person user) {
 901  0
             if (newMaintainableObject == null) {
 902  0
                     throw new PessimisticLockingException("Maintenance Document " + getDocumentNumber() +
 903  
                             " is using pessimistic locking with custom lock descriptors, but no new maintainable object has been defined");
 904  
             }
 905  0
             return newMaintainableObject.getCustomLockDescriptor(user);
 906  
     }
 907  
     
 908  
         protected MaintenanceDocumentDictionaryService getMaintenanceDocumentDictionaryService() {
 909  0
                 if ( maintenanceDocumentDictionaryService == null ) {
 910  0
                         maintenanceDocumentDictionaryService = KNSServiceLocator.getMaintenanceDocumentDictionaryService();
 911  
                 }
 912  0
                 return maintenanceDocumentDictionaryService;
 913  
         }
 914  
 
 915  
         protected MaintenanceDocumentService getMaintenanceDocumentService() {
 916  0
                 if ( maintenanceDocumentService == null ) {
 917  0
                         maintenanceDocumentService = KNSServiceLocator.getMaintenanceDocumentService();
 918  
                 }
 919  0
                 return maintenanceDocumentService;
 920  
         }
 921  
 
 922  
         protected DocumentHeaderService getDocumentHeaderService() {
 923  0
                 if ( documentHeaderService == null ) {
 924  0
                         documentHeaderService = KNSServiceLocator.getDocumentHeaderService();
 925  
                 }
 926  0
                 return documentHeaderService;
 927  
         }
 928  
 
 929  
          //for issue KULRice3070
 930  
          protected boolean checkAllowsRecordDeletion() {
 931  0
                  Boolean allowsRecordDeletion = KNSServiceLocator.getMaintenanceDocumentDictionaryService().getAllowsRecordDeletion(this.getNewMaintainableObject().getBoClass());
 932  0
                  if ( allowsRecordDeletion != null ) {
 933  0
                          return allowsRecordDeletion.booleanValue();
 934  
                  }
 935  
                  else {
 936  0
                          return false;
 937  
                  }
 938  
          }
 939  
 
 940  
          //for KULRice3070
 941  
          protected boolean checkMaintenanceAction(){         
 942  0
                  return this.getNewMaintainableObject().getMaintenanceAction().equals(KNSConstants.MAINTENANCE_DELETE_ACTION);
 943  
          }
 944  
 
 945  
          //for KULRice3070
 946  
          protected boolean checkDeletePermission(BusinessObject businessObject) {
 947  
 
 948  0
                  boolean allowsMaintain = false;
 949  
 
 950  0
                  String maintDocTypeName = KNSServiceLocator.getMaintenanceDocumentDictionaryService().getDocumentTypeName(businessObject.getClass());
 951  
 
 952  0
                  if (StringUtils.isNotBlank(maintDocTypeName)) {
 953  0
                          allowsMaintain = KNSServiceLocator.getBusinessObjectAuthorizationService().canMaintain(businessObject, GlobalVariables.getUserSession().getPerson(), maintDocTypeName);            
 954  
                  }     
 955  0
                  return allowsMaintain;
 956  
          }
 957  
 }