Coverage Report - org.kuali.rice.kew.service.WorkflowDocument
 
Classes in this File Line Coverage Branch Coverage Complexity
WorkflowDocument
0%
0/393
0%
0/126
1.841
 
 1  
 /*
 2  
  * Copyright 2005-2007 The Kuali Foundation
 3  
  *
 4  
  *
 5  
  * Licensed under the Educational Community License, Version 2.0 (the "License");
 6  
  * you may not use this file except in compliance with the License.
 7  
  * You may obtain a copy of the License at
 8  
  *
 9  
  * http://www.opensource.org/licenses/ecl2.php
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 package org.kuali.rice.kew.service;
 18  
 
 19  
 import java.sql.Timestamp;
 20  
 import java.util.ArrayList;
 21  
 import java.util.Calendar;
 22  
 import java.util.List;
 23  
 
 24  
 import org.kuali.rice.core.exception.RiceRuntimeException;
 25  
 import org.kuali.rice.core.resourceloader.GlobalResourceLoader;
 26  
 import org.kuali.rice.kew.dto.ActionRequestDTO;
 27  
 import org.kuali.rice.kew.dto.ActionTakenDTO;
 28  
 import org.kuali.rice.kew.dto.AdHocRevokeDTO;
 29  
 import org.kuali.rice.kew.dto.DocumentContentDTO;
 30  
 import org.kuali.rice.kew.dto.DocumentDetailDTO;
 31  
 import org.kuali.rice.kew.dto.DocumentLinkDTO;
 32  
 import org.kuali.rice.kew.dto.EmplIdDTO;
 33  
 import org.kuali.rice.kew.dto.ModifiableDocumentContentDTO;
 34  
 import org.kuali.rice.kew.dto.MovePointDTO;
 35  
 import org.kuali.rice.kew.dto.NetworkIdDTO;
 36  
 import org.kuali.rice.kew.dto.NoteDTO;
 37  
 import org.kuali.rice.kew.dto.ReturnPointDTO;
 38  
 import org.kuali.rice.kew.dto.RouteHeaderDTO;
 39  
 import org.kuali.rice.kew.dto.RouteNodeInstanceDTO;
 40  
 import org.kuali.rice.kew.dto.UserIdDTO;
 41  
 import org.kuali.rice.kew.dto.WorkflowAttributeDefinitionDTO;
 42  
 import org.kuali.rice.kew.dto.WorkflowAttributeValidationErrorDTO;
 43  
 import org.kuali.rice.kew.dto.WorkflowIdDTO;
 44  
 import org.kuali.rice.kew.exception.WorkflowException;
 45  
 import org.kuali.rice.kew.exception.WorkflowRuntimeException;
 46  
 import org.kuali.rice.kew.util.KEWConstants;
 47  
 import org.kuali.rice.kim.bo.Person;
 48  
 import org.kuali.rice.kim.bo.entity.KimPrincipal;
 49  
 import org.kuali.rice.kim.service.IdentityManagementService;
 50  
 import org.kuali.rice.kim.service.PersonService;
 51  
 import org.kuali.rice.kim.util.KimConstants;
 52  
 
 53  
 /**
 54  
  * Represents a document in Workflow from the perspective of the client.  This class is one of two
 55  
  * (Java) client interfaces to the KEW system (the other being {@link WorkflowInfo} class).  The
 56  
  * first time an instance of this class is created, it will read the client configuration to determine
 57  
  * how to connect to KEW.
 58  
  *
 59  
  * <p>This class is used by creating new instances using the appropriate constructor.  To create a new
 60  
  * document in KEW, create an instance of this class passing a UserIdVO and a
 61  
  * document type name.  To load an existing document, create an instance of this class passing a
 62  
  * UserIdVO and a document ID number.
 63  
  *
 64  
  * <p>Internally, this wrapper interacts with the {@link WorkflowDocumentActions} service exported
 65  
  * over the KSB, maintaining state.
 66  
  *
 67  
  * <p>This class is not thread safe and must by synchronized externally for concurrent access.
 68  
  *
 69  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 70  
  */
 71  
 public class WorkflowDocument implements java.io.Serializable {
 72  
 
 73  
         private static final long serialVersionUID = -3672966990721719088L;
 74  
 
 75  
         /**
 76  
          * The principal ID of the user as whom actions will be taken on the KEW document
 77  
          */
 78  
     private String principalId;
 79  
     /**
 80  
      * RouteHeader VO of the KEW document this WorkflowDocument represents
 81  
      */
 82  
     private RouteHeaderDTO routeHeader;
 83  
     /**
 84  
      * Flag that indicates whether the document content currently loaded needs to be refreshed.
 85  
      * This is the case either if the document content has not yet been loaded, or an action
 86  
      * that might possibly affect the document content (which is potentially any action) has
 87  
      * subsequently been taken on the document through this API.
 88  
      */
 89  0
     private boolean documentContentDirty = false;
 90  
     /**
 91  
      * Value Object encapsulating the document content
 92  
      */
 93  
     private ModifiableDocumentContentDTO documentContent;
 94  
 
 95  
     /**
 96  
      * @deprecated Use the constructor which takes a principal ID instead.
 97  
      */
 98  0
     public WorkflowDocument(UserIdDTO userId, String documentType) throws WorkflowException {
 99  0
             String principalId = convertUserIdToPrincipalId(userId);
 100  0
             init(principalId, documentType, null);
 101  0
     }
 102  
 
 103  
     /**
 104  
      * @deprecated Use the constructor which takes a principal ID instead.
 105  
      */
 106  0
     public WorkflowDocument(UserIdDTO userId, Long routeHeaderId) throws WorkflowException {
 107  0
             String principalId = convertUserIdToPrincipalId(userId);
 108  0
             init(principalId, null, routeHeaderId);
 109  0
     }
 110  
 
 111  
     private String convertUserIdToPrincipalId(UserIdDTO userId) {
 112  
 
 113  0
         if (userId == null) {
 114  0
             return null;
 115  0
         } else if (userId instanceof WorkflowIdDTO) {
 116  0
             return ((WorkflowIdDTO)userId).getWorkflowId();
 117  0
         } else if (userId instanceof NetworkIdDTO) {
 118  0
             IdentityManagementService identityManagementService = (IdentityManagementService)GlobalResourceLoader.getService(KimConstants.KIM_IDENTITY_MANAGEMENT_SERVICE);
 119  0
             String principalName = ((NetworkIdDTO)userId).getNetworkId();
 120  0
             KimPrincipal principal = identityManagementService.getPrincipalByPrincipalName(principalName);
 121  0
             return principal.getPrincipalId();
 122  0
         } else if (userId instanceof EmplIdDTO) {
 123  0
             PersonService personService = (PersonService)GlobalResourceLoader.getService(KimConstants.KIM_PERSON_SERVICE);
 124  0
             String employeeId = ((EmplIdDTO)userId).getEmplId();
 125  0
             Person person = personService.getPersonByEmployeeId(employeeId);
 126  0
             if (person == null) {
 127  0
                 throw new RiceRuntimeException("Could not locate a person with the given employee id of " + employeeId);
 128  
             }
 129  0
             return person.getPrincipalId();
 130  
         }
 131  0
         throw new IllegalArgumentException("Invalid UserIdDTO type was passed: " + userId);
 132  
     }
 133  
 
 134  
     /**
 135  
      * Constructs a WorkflowDocument representing a new document in the workflow system.
 136  
      * Creation/committing of the new document is deferred until the first action is
 137  
      * taken on the document.
 138  
      * @param principalId the user as which to take actions on the document
 139  
      * @param documentType the type of the document to create
 140  
      * @throws WorkflowException if anything goes awry
 141  
      */
 142  0
     public WorkflowDocument(String principalId, String documentType) throws WorkflowException {
 143  0
         init(principalId, documentType, null);
 144  0
     }
 145  
 
 146  
     /**
 147  
      * Loads a workflow document with the given route header ID for the given User.  If no document
 148  
      * can be found with the given ID, then the {@link getRouteHeader()} method of the WorkflowDocument
 149  
      * which is created will return null.
 150  
      *
 151  
      * @throws WorkflowException if there is a problem loading the WorkflowDocument
 152  
      */
 153  0
     public WorkflowDocument(String principalId, Long routeHeaderId) throws WorkflowException {
 154  0
         init(principalId, null, routeHeaderId);
 155  0
     }
 156  
 
 157  
     /**
 158  
      * Initializes this WorkflowDocument object, by either attempting to load an existing document by routeHeaderid
 159  
      * if one is supplied (non-null), or by constructing an empty document of the specified type.
 160  
      * @param principalId the user under which actions via this API on the specified document will be taken
 161  
      * @param documentType the type of document this WorkflowDocument should represent (either this parameter or routeHeaderId must be specified, non-null)
 162  
      * @param routeHeaderId the id of an existing document to load (either this parameter or documentType must be specified, non-null)
 163  
      * @throws WorkflowException if a routeHeaderId is specified but an exception occurs trying to load the document route header
 164  
      */
 165  
     private void init(String principalId, String documentType, Long routeHeaderId) throws WorkflowException {
 166  0
             this.principalId = principalId;
 167  0
             routeHeader = new RouteHeaderDTO();
 168  0
             routeHeader.setDocTypeName(documentType);
 169  0
             if (routeHeaderId != null) {
 170  0
                     routeHeader = getWorkflowUtility().getRouteHeaderWithPrincipal(principalId, routeHeaderId);
 171  
             }
 172  0
     }
 173  
 
 174  
     /**
 175  
      * Retrieves the WorkflowUtility proxy from the locator.  The locator will cache this for us.
 176  
      */
 177  
     private WorkflowUtility getWorkflowUtility() throws WorkflowException {
 178  0
         WorkflowUtility workflowUtility = 
 179  
                 (WorkflowUtility)GlobalResourceLoader.getService(KEWConstants.WORKFLOW_UTILITY_SERVICE);
 180  0
             if (workflowUtility == null) {
 181  0
                     throw new WorkflowException("Could not locate the WorkflowUtility service.  Please ensure that KEW client is configured properly!");
 182  
             }
 183  0
             return workflowUtility;
 184  
 
 185  
     }
 186  
 
 187  
     /**
 188  
      * Retrieves the WorkflowDocumentActions proxy from the locator.  The locator will cache this for us.
 189  
      */
 190  
     private WorkflowDocumentActions getWorkflowDocumentActions() throws WorkflowException {
 191  0
             WorkflowDocumentActions workflowDocumentActions = 
 192  
                     (WorkflowDocumentActions)GlobalResourceLoader.getService(KEWConstants.WORKFLOW_DOCUMENT_ACTIONS_SERVICE);
 193  0
             if (workflowDocumentActions == null) {
 194  0
                     throw new WorkflowException("Could not locate the WorkflowDocumentActions service.  Please ensure that KEW client is configured properly!");
 195  
             }
 196  0
             return workflowDocumentActions;
 197  
     }
 198  
 
 199  
     // ########################
 200  
     // Document Content methods
 201  
     // ########################
 202  
 
 203  
     /**
 204  
      * Returns an up-to-date DocumentContent of this document.
 205  
      * @see WorkflowUtility#getDocumentContent(Long)
 206  
      */
 207  
     public DocumentContentDTO getDocumentContent() {
 208  
             try {
 209  
                     // create the document if it hasn't already been created
 210  0
                     if (getRouteHeader().getRouteHeaderId() == null) {
 211  0
                         routeHeader = getWorkflowDocumentActions().createDocument(principalId, getRouteHeader());
 212  
                 }
 213  0
                     if (documentContent == null || documentContentDirty) {
 214  0
                             documentContent = new ModifiableDocumentContentDTO(getWorkflowUtility().getDocumentContent(routeHeader.getRouteHeaderId()));
 215  0
                             documentContentDirty = false;
 216  
                     }
 217  0
             } catch (Exception e) {
 218  0
                     throw handleExceptionAsRuntime(e);
 219  0
             }
 220  0
             return documentContent;
 221  
     }
 222  
 
 223  
     /**
 224  
      * Returns the application specific section of the document content. This is
 225  
      * a convenience method that delegates to the {@link DocumentContentDTO}.
 226  
      *
 227  
      * For documents routed prior to Workflow 2.0:
 228  
      * If the application did NOT use attributes for XML generation, this method will
 229  
      * return the entire document content XML.  Otherwise it will return the empty string.
 230  
      * @see DocumentContentDTO#getApplicationContent()
 231  
      */
 232  
     public String getApplicationContent() {
 233  0
         return getDocumentContent().getApplicationContent();
 234  
     }
 235  
 
 236  
     /**
 237  
      * Sets the application specific section of the document content. This is
 238  
      * a convenience method that delegates to the {@link DocumentContentDTO}.
 239  
      */
 240  
     public void setApplicationContent(String applicationContent) {
 241  0
         getDocumentContent().setApplicationContent(applicationContent);
 242  0
     }
 243  
 
 244  
     /**
 245  
      * Clears all attribute document content from the document.
 246  
      * Typically, this will be used if it is necessary to update the attribute doc content on
 247  
      * the document.  This can be accomplished by clearing the content and then adding the
 248  
      * desired attribute definitions.
 249  
      *
 250  
      * This is a convenience method that delegates to the {@link DocumentContentDTO}.
 251  
      *
 252  
      * In order for these changes to take effect, an action must be performed on the document (such as "save").
 253  
      */
 254  
     public void clearAttributeContent() {
 255  0
         getDocumentContent().setAttributeContent("");
 256  0
     }
 257  
 
 258  
     /**
 259  
      * Returns the attribute-generated section of the document content. This is
 260  
      * a convenience method that delegates to the {@link DocumentContentDTO}.
 261  
      * @see DocumentContentDTO#getAttributeContent()
 262  
      */
 263  
     public String getAttributeContent() {
 264  0
         return getDocumentContent().getAttributeContent();
 265  
     }
 266  
 
 267  
     /**
 268  
      * Adds an attribute definition which defines creation parameters for a WorkflowAttribute
 269  
      * implementation.  The created attribute will be used to generate attribute document content.
 270  
      * When the document is sent to the server, this will be appended to the existing attribute
 271  
      * doc content.  If it is required to replace the attribute document content, then the
 272  
      * clearAttributeContent() method should be invoked prior to adding attribute definitions.
 273  
      *
 274  
      * This is a convenience method that delegates to the {@link DocumentContentDTO}.
 275  
      * @see DocumentContentDTO#addAttributeDefinition(WorkflowAttributeDefinitionDTO)
 276  
      */
 277  
     public void addAttributeDefinition(WorkflowAttributeDefinitionDTO attributeDefinition) {
 278  0
         getDocumentContent().addAttributeDefinition(attributeDefinition);
 279  0
     }
 280  
 
 281  
     /**
 282  
      * Validate the WorkflowAttributeDefinition against it's attribute on the server.  This will validate
 283  
      * the inputs that will eventually become xml.
 284  
      *
 285  
      * Only applies to attributes implementing WorkflowAttributeXmlValidator.
 286  
      *
 287  
      * This is a call through to the WorkflowInfo object and is here for convenience.
 288  
      *
 289  
      * @param attributeDefinition the workflow attribute definition VO to validate
 290  
      * @return WorkflowAttributeValidationErrorVO[] of error from the attribute
 291  
      * @throws WorkflowException when attribute doesn't implement WorkflowAttributeXmlValidator
 292  
      * @see WorkflowUtility#validateWorkflowAttributeDefinitionVO(WorkflowAttributeDefinitionDTO)
 293  
      */
 294  
     public WorkflowAttributeValidationErrorDTO[] validateAttributeDefinition(WorkflowAttributeDefinitionDTO attributeDefinition) throws WorkflowException {
 295  0
             return getWorkflowUtility().validateWorkflowAttributeDefinitionVO(attributeDefinition);
 296  
     }
 297  
 
 298  
     /**
 299  
      * Removes an attribute definition from the document content.  This is
 300  
      * a convenience method that delegates to the {@link DocumentContentDTO}.
 301  
      * @param attributeDefinition the attribute definition VO to remove
 302  
      */
 303  
     public void removeAttributeDefinition(WorkflowAttributeDefinitionDTO attributeDefinition) {
 304  0
         getDocumentContent().removeAttributeDefinition(attributeDefinition);
 305  0
     }
 306  
 
 307  
     /**
 308  
      * Removes all attribute definitions from the document content. This is
 309  
      * a convenience method that delegates to the {@link DocumentContentDTO}.
 310  
      */
 311  
     public void clearAttributeDefinitions() {
 312  0
             getDocumentContent().setAttributeDefinitions(new WorkflowAttributeDefinitionDTO[0]);
 313  0
     }
 314  
 
 315  
     /**
 316  
      * Returns the attribute definition VOs currently defined on the document content. This is
 317  
      * a convenience method that delegates to the {@link DocumentContentDTO}.
 318  
      * @return the attribute definition VOs currently defined on the document content.
 319  
      * @see DocumentContentDTO#getAttributeDefinitions()
 320  
      */
 321  
     public WorkflowAttributeDefinitionDTO[] getAttributeDefinitions() {
 322  0
         return getDocumentContent().getAttributeDefinitions();
 323  
     }
 324  
 
 325  
     /**
 326  
      * Adds a searchable attribute definition which defines creation parameters for a SearchableAttribute
 327  
      * implementation.  The created attribute will be used to generate searchable document content.
 328  
      * When the document is sent to the server, this will be appended to the existing searchable
 329  
      * doc content.  If it is required to replace the searchable document content, then the
 330  
      * clearSearchableContent() method should be invoked prior to adding definitions. This is
 331  
      * a convenience method that delegates to the {@link DocumentContentDTO}.
 332  
      */
 333  
     public void addSearchableDefinition(WorkflowAttributeDefinitionDTO searchableDefinition) {
 334  0
         getDocumentContent().addSearchableDefinition(searchableDefinition);
 335  0
     }
 336  
 
 337  
     /**
 338  
      * Removes a searchable attribute definition from the document content. This is
 339  
      * a convenience method that delegates to the {@link DocumentContentDTO}.
 340  
      * @param searchableDefinition the searchable attribute definition to remove
 341  
      */
 342  
     public void removeSearchableDefinition(WorkflowAttributeDefinitionDTO searchableDefinition) {
 343  0
         getDocumentContent().removeSearchableDefinition(searchableDefinition);
 344  0
     }
 345  
 
 346  
     /**
 347  
      * Removes all searchable attribute definitions from the document content. This is
 348  
      * a convenience method that delegates to the {@link DocumentContentDTO}.
 349  
      */
 350  
     public void clearSearchableDefinitions() {
 351  0
         getDocumentContent().setSearchableDefinitions(new WorkflowAttributeDefinitionDTO[0]);
 352  0
     }
 353  
 
 354  
     /**
 355  
      * Clears the searchable content from the document content. This is
 356  
      * a convenience method that delegates to the {@link DocumentContentDTO}.
 357  
      */
 358  
     public void clearSearchableContent() {
 359  0
             getDocumentContent().setSearchableContent("");
 360  0
     }
 361  
 
 362  
     /**
 363  
      * Returns the searchable attribute definitions on the document content. This is
 364  
      * a convenience method that delegates to the {@link DocumentContentDTO}.
 365  
      * @return the searchable attribute definitions on the document content.
 366  
      * @see DocumentContentDTO#getSearchableDefinitions()
 367  
      */
 368  
     public WorkflowAttributeDefinitionDTO[] getSearchableDefinitions() {
 369  0
         return getDocumentContent().getSearchableDefinitions();
 370  
     }
 371  
 
 372  
     // ########################
 373  
     // END Document Content methods
 374  
     // ########################
 375  
 
 376  
     /**
 377  
      * Returns the RouteHeaderVO for the workflow document this WorkflowDocument represents
 378  
      */
 379  
     public RouteHeaderDTO getRouteHeader() {
 380  0
         return routeHeader;
 381  
     }
 382  
 
 383  
     /**
 384  
      * Returns the id of the workflow document this WorkflowDocument represents.  If this is a new document
 385  
      * that has not yet been created, the document is first created (and therefore this will return a new id)
 386  
      * @return the id of the workflow document this WorkflowDocument represents
 387  
      * @throws WorkflowException if an error occurs during document creation
 388  
      */
 389  
     public Long getRouteHeaderId() throws WorkflowException {
 390  0
             createDocumentIfNeccessary();
 391  0
             return getRouteHeader().getRouteHeaderId();
 392  
     }
 393  
 
 394  
     /**
 395  
      * Returns VOs of the pending ActionRequests on this document.  If this object represents a new document
 396  
      * that has not yet been created, then an empty array will be returned.  The ordering of ActionRequests
 397  
      * returned by this method is not guaranteed.
 398  
      *
 399  
      * This method relies on the WorkflowUtility service
 400  
      *
 401  
      * @return VOs of the pending ActionRequests on this document
 402  
      * @throws WorkflowException if an error occurs obtaining the pending action requests for this document
 403  
      * @see WorkflowUtility#getActionRequests(Long)
 404  
      */
 405  
     public ActionRequestDTO[] getActionRequests() throws WorkflowException {
 406  0
         if (getRouteHeaderId() == null) {
 407  0
             return new ActionRequestDTO[0];
 408  
         }
 409  0
         return getWorkflowUtility().getAllActionRequests(getRouteHeaderId());
 410  
     }
 411  
 
 412  
     /**
 413  
      * Returns VOs of the actions taken on this document.  If this object represents a new document
 414  
      * that has not yet been created, then an empty array will be returned.  The ordering of actions taken
 415  
      * returned by this method is not guaranteed.
 416  
      *
 417  
      * This method relies on the WorkflowUtility service
 418  
      *
 419  
      * @return VOs of the actions that have been taken on this document
 420  
      * @throws WorkflowException if an error occurs obtaining the actions taken on this document
 421  
      * @see WorkflowUtility#getActionsTaken(Long)
 422  
      */
 423  
     public ActionTakenDTO[] getActionsTaken() throws WorkflowException {
 424  0
         if (getRouteHeaderId() == null) {
 425  0
             return new ActionTakenDTO[0];
 426  
         }
 427  0
         return getWorkflowUtility().getActionsTaken(getRouteHeaderId());
 428  
     }
 429  
 
 430  
     /**
 431  
      * Sets the "application doc id" on the document
 432  
      * @param appDocId the "application doc id" to set on the workflow document
 433  
      */
 434  
     public void setAppDocId(String appDocId) {
 435  0
         routeHeader.setAppDocId(appDocId);
 436  0
     }
 437  
 
 438  
     /**
 439  
      * Returns the "application doc id" set on this workflow document (if any)
 440  
      * @return the "application doc id" set on this workflow document (if any)
 441  
      */
 442  
     public String getAppDocId() {
 443  0
         return routeHeader.getAppDocId();
 444  
     }
 445  
 
 446  
     /**
 447  
      * Returns the date/time the document was created, or null if the document has not yet been created
 448  
      * @return the date/time the document was created, or null if the document has not yet been created
 449  
      */
 450  
     public Timestamp getDateCreated() {
 451  0
             if (routeHeader.getDateCreated() == null) {
 452  0
                     return null;
 453  
             }
 454  0
             return new Timestamp(routeHeader.getDateCreated().getTime().getTime());
 455  
     }
 456  
 
 457  
     /**
 458  
      * Returns the title of the document
 459  
      * @return the title of the document
 460  
      */
 461  
     public String getTitle() {
 462  0
         return getRouteHeader().getDocTitle();
 463  
     }
 464  
 
 465  
     /**
 466  
      * Performs the 'save' action on the document this WorkflowDocument represents.  If this is a new document,
 467  
      * the document is created first.
 468  
      * @param annotation the message to log for the action
 469  
      * @throws WorkflowException in case an error occurs saving the document
 470  
      * @see WorkflowDocumentActions#saveDocument(UserIdDTO, RouteHeaderDTO, String)
 471  
      */
 472  
     public void saveDocument(String annotation) throws WorkflowException {
 473  0
             createDocumentIfNeccessary();
 474  0
             routeHeader = getWorkflowDocumentActions().saveDocument(principalId, getRouteHeader(), annotation);
 475  0
             documentContentDirty = true;
 476  0
     }
 477  
 
 478  
     /**
 479  
      * Performs the 'route' action on the document this WorkflowDocument represents.  If this is a new document,
 480  
      * the document is created first.
 481  
      * @param annotation the message to log for the action
 482  
      * @throws WorkflowException in case an error occurs routing the document
 483  
      * @see WorkflowDocumentActions#routeDocument(UserIdDTO, RouteHeaderDTO, String)
 484  
      */
 485  
     public void routeDocument(String annotation) throws WorkflowException {
 486  0
             createDocumentIfNeccessary();
 487  0
             routeHeader = getWorkflowDocumentActions().routeDocument(principalId, routeHeader, annotation);
 488  0
             documentContentDirty = true;
 489  0
     }
 490  
 
 491  
     /**
 492  
      * Performs the 'disapprove' action on the document this WorkflowDocument represents.  If this is a new document,
 493  
      * the document is created first.
 494  
      * @param annotation the message to log for the action
 495  
      * @throws WorkflowException in case an error occurs disapproving the document
 496  
      * @see WorkflowDocumentActions#disapproveDocument(UserIdDTO, RouteHeaderDTO, String)
 497  
      */
 498  
     public void disapprove(String annotation) throws WorkflowException {
 499  0
             createDocumentIfNeccessary();
 500  0
             routeHeader = getWorkflowDocumentActions().disapproveDocument(principalId, getRouteHeader(), annotation);
 501  0
             documentContentDirty = true;
 502  0
     }
 503  
 
 504  
     /**
 505  
      * Performs the 'approve' action on the document this WorkflowDocument represents.  If this is a new document,
 506  
      * the document is created first.
 507  
      * @param annotation the message to log for the action
 508  
      * @throws WorkflowException in case an error occurs approving the document
 509  
      * @see WorkflowDocumentActions#approveDocument(UserIdDTO, RouteHeaderDTO, String)
 510  
      */
 511  
     public void approve(String annotation) throws WorkflowException {
 512  0
             createDocumentIfNeccessary();
 513  0
             routeHeader = getWorkflowDocumentActions().approveDocument(principalId, getRouteHeader(), annotation);
 514  0
             documentContentDirty = true;
 515  0
     }
 516  
 
 517  
     /**
 518  
      * Performs the 'cancel' action on the document this WorkflowDocument represents.  If this is a new document,
 519  
      * the document is created first.
 520  
      * @param annotation the message to log for the action
 521  
      * @throws WorkflowException in case an error occurs canceling the document
 522  
      * @see WorkflowDocumentActions#cancelDocument(UserIdDTO, RouteHeaderDTO, String)
 523  
      */
 524  
     public void cancel(String annotation) throws WorkflowException {
 525  0
             createDocumentIfNeccessary();
 526  0
             routeHeader = getWorkflowDocumentActions().cancelDocument(principalId, getRouteHeader(), annotation);
 527  0
             documentContentDirty = true;
 528  0
     }
 529  
 
 530  
     /**
 531  
      * Performs the 'blanket-approve' action on the document this WorkflowDocument represents.  If this is a new document,
 532  
      * the document is created first.
 533  
      * @param annotation the message to log for the action
 534  
      * @throws WorkflowException in case an error occurs blanket-approving the document
 535  
      * @see WorkflowDocumentActions#blanketApprovalToNodes(UserIdDTO, RouteHeaderDTO, String, String[])
 536  
      */
 537  
     public void blanketApprove(String annotation) throws WorkflowException {
 538  0
         blanketApprove(annotation, (String)null);
 539  0
     }
 540  
 
 541  
     /**
 542  
      * Commits any changes made to the local copy of this document to the workflow system.  If this is a new document,
 543  
      * the document is created first.
 544  
      * @throws WorkflowException in case an error occurs saving the document
 545  
      * @see WorkflowDocumentActions#saveRoutingData(UserIdDTO, RouteHeaderDTO)
 546  
      */
 547  
     public void saveRoutingData() throws WorkflowException {
 548  0
             createDocumentIfNeccessary();
 549  0
             routeHeader = getWorkflowDocumentActions().saveRoutingData(principalId, getRouteHeader());
 550  0
             documentContentDirty = true;
 551  0
     }
 552  
 
 553  
     /**
 554  
      * 
 555  
      * This method sets the Application Document Status and then calls saveRoutingData() to commit 
 556  
      * the changes to the workflow system.
 557  
      * 
 558  
      * @param appDocStatus
 559  
      * @throws WorkflowException
 560  
      */
 561  
     public void updateAppDocStatus(String appDocStatus) throws WorkflowException {
 562  0
                getRouteHeader().setAppDocStatus(appDocStatus);
 563  0
                saveRoutingData();
 564  0
     }
 565  
     
 566  
     /**
 567  
      * Performs the 'acknowledge' action on the document this WorkflowDocument represents.  If this is a new document,
 568  
      * the document is created first.
 569  
      * @param annotation the message to log for the action
 570  
      * @throws WorkflowException in case an error occurs acknowledging the document
 571  
      * @see WorkflowDocumentActions#acknowledgeDocument(UserIdDTO, RouteHeaderDTO, String)
 572  
      */
 573  
     public void acknowledge(String annotation) throws WorkflowException {
 574  0
             createDocumentIfNeccessary();
 575  0
             routeHeader = getWorkflowDocumentActions().acknowledgeDocument(principalId, getRouteHeader(), annotation);
 576  0
             documentContentDirty = true;
 577  0
     }
 578  
 
 579  
     /**
 580  
      * Performs the 'fyi' action on the document this WorkflowDocument represents.  If this is a new document,
 581  
      * the document is created first.
 582  
      * @param annotation the message to log for the action
 583  
      * @throws WorkflowException in case an error occurs fyi-ing the document
 584  
      */
 585  
     public void fyi() throws WorkflowException {
 586  0
             createDocumentIfNeccessary();
 587  0
             routeHeader = getWorkflowDocumentActions().clearFYIDocument(principalId, getRouteHeader());
 588  0
             documentContentDirty = true;
 589  0
     }
 590  
 
 591  
     /**
 592  
      * Performs the 'delete' action on the document this WorkflowDocument represents.  If this is a new document,
 593  
      * the document is created first.
 594  
      * @param annotation the message to log for the action
 595  
      * @throws WorkflowException in case an error occurs deleting the document
 596  
      * @see WorkflowDocumentActions#deleteDocument(UserIdDTO, RouteHeaderDTO)
 597  
      */
 598  
     public void delete() throws WorkflowException {
 599  0
             createDocumentIfNeccessary();
 600  0
             getWorkflowDocumentActions().deleteDocument(principalId, getRouteHeader());
 601  0
             documentContentDirty = true;
 602  0
     }
 603  
 
 604  
     /**
 605  
      * Reloads the document route header.  If this is a new document, the document is created first.
 606  
      * Next time document content is accessed, an up-to-date copy will be retrieved from workflow.
 607  
      * @throws WorkflowException in case an error occurs retrieving the route header
 608  
      */
 609  
     public void refreshContent() throws WorkflowException {
 610  0
             createDocumentIfNeccessary();
 611  0
             routeHeader = getWorkflowUtility().getRouteHeader(getRouteHeaderId());
 612  0
             documentContentDirty = true;
 613  0
     }
 614  
 
 615  
     /**
 616  
      * Sends an ad hoc request to the specified user at the current active node on the document.  If the document is
 617  
      * in a terminal state, the request will be attached to the terminal node.
 618  
      */
 619  
     public void adHocRouteDocumentToPrincipal(String actionRequested, String annotation, String principalId, String responsibilityDesc, boolean forceAction) throws WorkflowException {
 620  0
             adHocRouteDocumentToPrincipal(actionRequested, null, annotation, principalId, responsibilityDesc, forceAction);
 621  0
     }
 622  
 
 623  
     /**
 624  
      * Sends an ad hoc request to the specified user at the specified node on the document.  If the document is
 625  
      * in a terminal state, the request will be attached to the terminal node.
 626  
      */
 627  
     public void adHocRouteDocumentToPrincipal(String actionRequested, String nodeName, String annotation, String principalId, String responsibilityDesc, boolean forceAction) throws WorkflowException {
 628  0
             adHocRouteDocumentToPrincipal(actionRequested, nodeName, annotation, principalId, responsibilityDesc, forceAction, null);
 629  0
     }
 630  
 
 631  
     /**
 632  
      * Sends an ad hoc request to the specified user at the specified node on the document.  If the document is
 633  
      * in a terminal state, the request will be attached to the terminal node.
 634  
      */
 635  
     public void adHocRouteDocumentToPrincipal(String actionRequested, String nodeName, String annotation, String principalId, String responsibilityDesc, boolean forceAction, String requestLabel) throws WorkflowException {
 636  0
             createDocumentIfNeccessary();
 637  0
             routeHeader = getWorkflowDocumentActions().adHocRouteDocumentToPrincipal(principalId, getRouteHeader(), actionRequested, nodeName, annotation, principalId, responsibilityDesc, forceAction, requestLabel);
 638  0
             documentContentDirty = true;
 639  0
     }
 640  
 
 641  
     /**
 642  
      * Sends an ad hoc request to the specified workgroup at the current active node on the document.  If the document is
 643  
      * in a terminal state, the request will be attached to the terminal node.
 644  
      */
 645  
     public void adHocRouteDocumentToGroup(String actionRequested, String annotation, String groupId, String responsibilityDesc, boolean forceAction) throws WorkflowException {
 646  0
             adHocRouteDocumentToGroup(actionRequested, null, annotation, groupId, responsibilityDesc, forceAction);
 647  0
     }
 648  
 
 649  
     /**
 650  
      * Sends an ad hoc request to the specified workgroup at the specified node on the document.  If the document is
 651  
      * in a terminal state, the request will be attached to the terminal node.
 652  
      */
 653  
     public void adHocRouteDocumentToGroup(String actionRequested, String nodeName, String annotation, String groupId, String responsibilityDesc, boolean forceAction) throws WorkflowException {
 654  0
             adHocRouteDocumentToGroup(actionRequested, nodeName, annotation, groupId, responsibilityDesc, forceAction, null);
 655  0
     }
 656  
 
 657  
     /**
 658  
      * Sends an ad hoc request to the specified workgroup at the specified node on the document.  If the document is
 659  
      * in a terminal state, the request will be attached to the terminal node.
 660  
      */
 661  
     public void adHocRouteDocumentToGroup(String actionRequested, String nodeName, String annotation, String groupId, String responsibilityDesc, boolean forceAction, String requestLabel) throws WorkflowException {
 662  0
             createDocumentIfNeccessary();
 663  0
             routeHeader = getWorkflowDocumentActions().adHocRouteDocumentToGroup(principalId, getRouteHeader(), actionRequested, nodeName, annotation, groupId, responsibilityDesc, forceAction, requestLabel);
 664  0
             documentContentDirty = true;
 665  0
     }
 666  
 
 667  
     /**
 668  
      * Revokes AdHoc request(s) according to the given AdHocRevokeVO which is passed in.
 669  
      *
 670  
      * If a specific action request ID is specified on the revoke bean, and that ID is not a valid ID, this method should throw a
 671  
      * WorkflowException.
 672  
      * @param revoke AdHocRevokeVO
 673  
      * @param annotation message to note for this action
 674  
      * @throws WorkflowException if an error occurs revoking adhoc requests
 675  
      * @see WorkflowDocumentActions#revokeAdHocRequests(UserIdDTO, RouteHeaderDTO, AdHocRevokeDTO, String)
 676  
      */
 677  
     public void revokeAdHocRequests(AdHocRevokeDTO revoke, String annotation) throws WorkflowException {
 678  0
             if (getRouteHeader().getRouteHeaderId() == null) {
 679  0
                     throw new WorkflowException("Can't revoke request, the workflow document has not yet been created!");
 680  
             }
 681  0
             createDocumentIfNeccessary();
 682  0
             routeHeader = getWorkflowDocumentActions().revokeAdHocRequests(principalId, getRouteHeader(), revoke, annotation);
 683  0
             documentContentDirty = true;
 684  0
     }
 685  
 
 686  
     /**
 687  
      * Sets the title of the document, empty string if null is specified.
 688  
      * @param title title of the document to set, or null
 689  
      */
 690  
     // WorkflowException is declared but not thrown...
 691  
     public void setTitle(String title) throws WorkflowException {
 692  0
         if (title == null) {
 693  0
             title = "";
 694  
         }
 695  0
         if (title.length() > KEWConstants.TITLE_MAX_LENGTH) {
 696  0
             title = title.substring(0, KEWConstants.TITLE_MAX_LENGTH);
 697  
         }
 698  0
         getRouteHeader().setDocTitle(title);
 699  0
     }
 700  
 
 701  
     /**
 702  
      * Returns the document type of the workflow document
 703  
      * @return the document type of the workflow document
 704  
      * @throws RuntimeException if document does not exist (is not yet created)
 705  
      * @see RouteHeaderDTO#getDocTypeName()
 706  
      */
 707  
     public String getDocumentType() {
 708  0
         if (getRouteHeader() == null) {
 709  
             // HACK: FIXME: we should probably proscribe, or at least handle consistently, these corner cases
 710  
             // NPEs are not nice
 711  0
             throw new RuntimeException("No such document!");
 712  
         }
 713  0
         return getRouteHeader().getDocTypeName();
 714  
     }
 715  
 
 716  
     /**
 717  
      * Returns whether an acknowledge is requested of the user for this document.  This is
 718  
      * a convenience method that delegates to {@link RouteHeaderDTO#isAckRequested()}.
 719  
      * @return whether an acknowledge is requested of the user for this document
 720  
      * @see RouteHeaderDTO#isAckRequested()
 721  
      */
 722  
     public boolean isAcknowledgeRequested() {
 723  0
         return getRouteHeader().isAckRequested();
 724  
     }
 725  
 
 726  
     /**
 727  
      * Returns whether an approval is requested of the user for this document.  This is
 728  
      * a convenience method that delegates to {@link RouteHeaderDTO#isApproveRequested()}.
 729  
      * @return whether an approval is requested of the user for this document
 730  
      * @see RouteHeaderDTO#isApproveRequested()
 731  
      */
 732  
     public boolean isApprovalRequested() {
 733  0
         return getRouteHeader().isApproveRequested();
 734  
     }
 735  
 
 736  
     /**
 737  
      * Returns whether a completion is requested of the user for this document.  This is
 738  
      * a convenience method that delegates to {@link RouteHeaderDTO#isCompleteRequested()}.
 739  
      * @return whether an approval is requested of the user for this document
 740  
      * @see RouteHeaderDTO#isCompleteRequested()
 741  
      */
 742  
     public boolean isCompletionRequested() {
 743  0
         return getRouteHeader().isCompleteRequested();
 744  
     }
 745  
 
 746  
     /**
 747  
      * Returns whether an FYI is requested of the user for this document.  This is
 748  
      * a convenience method that delegates to {@link RouteHeaderDTO#isFyiRequested()}.
 749  
      * @return whether an FYI is requested of the user for this document
 750  
      * @see RouteHeaderDTO#isFyiRequested()
 751  
      */
 752  
     public boolean isFYIRequested() {
 753  0
         return getRouteHeader().isFyiRequested();
 754  
     }
 755  
 
 756  
     /**
 757  
      * Returns whether the user can blanket approve the document
 758  
      * @return whether the user can blanket approve the document
 759  
      * @see RouteHeaderDTO#getValidActions()
 760  
      */
 761  
     public boolean isBlanketApproveCapable() {
 762  
         // TODO delyea - refactor this to take into account non-initiator owned documents
 763  0
             return getRouteHeader().getValidActions().contains(KEWConstants.ACTION_TAKEN_BLANKET_APPROVE_CD) && (isCompletionRequested() || isApprovalRequested() || stateIsInitiated());
 764  
     }
 765  
 
 766  
     /**
 767  
      * Returns whether the specified action code is valid for the current user and document
 768  
      * @return whether the user can blanket approve the document
 769  
      * @see RouteHeaderDTO#getValidActions()
 770  
      */
 771  
     public boolean isActionCodeValidForDocument(String actionTakenCode) {
 772  0
             return getRouteHeader().getValidActions().contains(actionTakenCode);
 773  
     }
 774  
 
 775  
     /**
 776  
      * Performs the 'super-user-approve' action on the document this WorkflowDocument represents.  If this is a new document,
 777  
      * the document is created first.
 778  
      * @param annotation the message to log for the action
 779  
      * @throws WorkflowException in case an error occurs super-user-approve-ing the document
 780  
      * @see WorkflowDocumentActions#superUserApprove(UserIdDTO, RouteHeaderDTO, String)
 781  
      */
 782  
     public void superUserApprove(String annotation) throws WorkflowException {
 783  0
             createDocumentIfNeccessary();
 784  0
             routeHeader = getWorkflowDocumentActions().superUserApprove(principalId, getRouteHeader(), annotation);
 785  0
             documentContentDirty = true;
 786  0
     }
 787  
 
 788  
     /**
 789  
      * Performs the 'super-user-action-request-approve' action on the document this WorkflowDocument represents and the action
 790  
      * request the id represents.
 791  
      * @param actionRequestId the action request id for the action request the super user is approved
 792  
      * @param annotation the message to log for the action
 793  
      * @throws WorkflowException in case an error occurs super-user-action-request-approve-ing the document
 794  
      * @see WorkflowDocumentActions#superUserApprove(UserIdVO, RouteHeaderVO, String)(UserIdVO, RouteHeaderVO, String)
 795  
      */
 796  
     public void superUserActionRequestApprove(Long actionRequestId, String annotation) throws WorkflowException {
 797  0
             createDocumentIfNeccessary();
 798  0
             routeHeader = getWorkflowDocumentActions().superUserActionRequestApprove(principalId, getRouteHeader(), actionRequestId, annotation);
 799  0
             documentContentDirty = true;
 800  0
     }
 801  
 
 802  
     /**
 803  
      * Performs the 'super-user-disapprove' action on the document this WorkflowDocument represents.  If this is a new document,
 804  
      * the document is created first.
 805  
      * @param annotation the message to log for the action
 806  
      * @throws WorkflowException in case an error occurs super-user-disapprove-ing the document
 807  
      * @see WorkflowDocumentActions#superUserDisapprove(UserIdDTO, RouteHeaderDTO, String)
 808  
      */
 809  
     public void superUserDisapprove(String annotation) throws WorkflowException {
 810  0
             createDocumentIfNeccessary();
 811  0
             routeHeader = getWorkflowDocumentActions().superUserDisapprove(principalId, getRouteHeader(), annotation);
 812  0
             documentContentDirty = true;
 813  0
     }
 814  
 
 815  
     /**
 816  
      * Performs the 'super-user-cancel' action on the document this WorkflowDocument represents.  If this is a new document,
 817  
      * the document is created first.
 818  
      * @param annotation the message to log for the action
 819  
      * @throws WorkflowException in case an error occurs super-user-cancel-ing the document
 820  
      * @see WorkflowDocumentActions#superUserCancel(UserIdDTO, RouteHeaderDTO, String)
 821  
      */
 822  
     public void superUserCancel(String annotation) throws WorkflowException {
 823  0
             createDocumentIfNeccessary();
 824  0
             routeHeader = getWorkflowDocumentActions().superUserCancel(principalId, getRouteHeader(), annotation);
 825  0
             documentContentDirty = true;
 826  0
     }
 827  
 
 828  
     /**
 829  
      * Returns whether the user is a super user on this document
 830  
      * @return whether the user is a super user on this document
 831  
      * @throws WorkflowException if an error occurs determining whether the user is a super user on this document
 832  
      * @see WorkflowUtility#isSuperUserForDocumentType(UserIdDTO, Long)
 833  
      */
 834  
     public boolean isSuperUser() throws WorkflowException {
 835  0
             createDocumentIfNeccessary();
 836  0
             return getWorkflowUtility().isSuperUserForDocumentType(principalId, getRouteHeader().getDocTypeId());
 837  
         }
 838  
 
 839  
     /**
 840  
      * Returns whether the user passed into WorkflowDocument at instantiation can route
 841  
      * the document.
 842  
          * @return if user passed into WorkflowDocument at instantiation can route
 843  
          *         the document.
 844  
          */
 845  
     public boolean isRouteCapable() {
 846  0
         return isActionCodeValidForDocument(KEWConstants.ACTION_TAKEN_ROUTED_CD);
 847  
     }
 848  
 
 849  
     /**
 850  
      * Performs the 'clearFYI' action on the document this WorkflowDocument represents.  If this is a new document,
 851  
      * the document is created first.
 852  
      * @param annotation the message to log for the action
 853  
      * @throws WorkflowException in case an error occurs clearing FYI on the document
 854  
      * @see WorkflowDocumentActions#clearFYIDocument(UserIdDTO, RouteHeaderDTO)
 855  
      */
 856  
     public void clearFYI() throws WorkflowException {
 857  0
             createDocumentIfNeccessary();
 858  0
             getWorkflowDocumentActions().clearFYIDocument(principalId, getRouteHeader());
 859  0
             documentContentDirty = true;
 860  0
     }
 861  
 
 862  
     /**
 863  
      * Performs the 'complete' action on the document this WorkflowDocument represents.  If this is a new document,
 864  
      * the document is created first.
 865  
      * @param annotation the message to log for the action
 866  
      * @throws WorkflowException in case an error occurs clearing completing the document
 867  
      * @see WorkflowDocumentActions#completeDocument(UserIdDTO, RouteHeaderDTO, String)
 868  
      */
 869  
     public void complete(String annotation) throws WorkflowException {
 870  0
             createDocumentIfNeccessary();
 871  0
             routeHeader = getWorkflowDocumentActions().completeDocument(principalId, getRouteHeader(), annotation);
 872  0
             documentContentDirty = true;
 873  0
     }
 874  
 
 875  
     /**
 876  
      * Performs the 'logDocumentAction' action on the document this WorkflowDocument represents.  If this is a new document,
 877  
      * the document is created first.  The 'logDocumentAction' simply logs a message on the document.
 878  
      * @param annotation the message to log for the action
 879  
      * @throws WorkflowException in case an error occurs logging a document action on the document
 880  
      * @see WorkflowDocumentActions#logDocumentAction(UserIdDTO, RouteHeaderDTO, String)
 881  
      */
 882  
     public void logDocumentAction(String annotation) throws WorkflowException {
 883  0
             createDocumentIfNeccessary();
 884  0
             getWorkflowDocumentActions().logDocumentAction(principalId, getRouteHeader(), annotation);
 885  0
             documentContentDirty = true;
 886  0
     }
 887  
 
 888  
     /**
 889  
      * Indicates if the document is in the initiated state or not.
 890  
      *
 891  
      * @return true if in the specified state
 892  
      */
 893  
     public boolean stateIsInitiated() {
 894  0
         return KEWConstants.ROUTE_HEADER_INITIATED_CD.equals(getRouteHeader().getDocRouteStatus());
 895  
     }
 896  
 
 897  
     /**
 898  
      * Indicates if the document is in the saved state or not.
 899  
      *
 900  
      * @return true if in the specified state
 901  
      */
 902  
     public boolean stateIsSaved() {
 903  0
         return KEWConstants.ROUTE_HEADER_SAVED_CD.equals(getRouteHeader().getDocRouteStatus());
 904  
     }
 905  
 
 906  
     /**
 907  
      * Indicates if the document is in the enroute state or not.
 908  
      *
 909  
      * @return true if in the specified state
 910  
      */
 911  
     public boolean stateIsEnroute() {
 912  0
         return KEWConstants.ROUTE_HEADER_ENROUTE_CD.equals(getRouteHeader().getDocRouteStatus());
 913  
     }
 914  
 
 915  
     /**
 916  
      * Indicates if the document is in the exception state or not.
 917  
      *
 918  
      * @return true if in the specified state
 919  
      */
 920  
     public boolean stateIsException() {
 921  0
         return KEWConstants.ROUTE_HEADER_EXCEPTION_CD.equals(getRouteHeader().getDocRouteStatus());
 922  
     }
 923  
 
 924  
     /**
 925  
      * Indicates if the document is in the canceled state or not.
 926  
      *
 927  
      * @return true if in the specified state
 928  
      */
 929  
     public boolean stateIsCanceled() {
 930  0
         return KEWConstants.ROUTE_HEADER_CANCEL_CD.equals(getRouteHeader().getDocRouteStatus());
 931  
     }
 932  
 
 933  
     /**
 934  
      * Indicates if the document is in the disapproved state or not.
 935  
      *
 936  
      * @return true if in the specified state
 937  
      */
 938  
     public boolean stateIsDisapproved() {
 939  0
         return KEWConstants.ROUTE_HEADER_DISAPPROVED_CD.equals(getRouteHeader().getDocRouteStatus());
 940  
     }
 941  
 
 942  
     /**
 943  
      * Indicates if the document is in the approved state or not. Will answer true is document is in Processed or Finalized state.
 944  
      *
 945  
      * @return true if in the specified state
 946  
      */
 947  
     public boolean stateIsApproved() {
 948  0
         return KEWConstants.ROUTE_HEADER_APPROVED_CD.equals(getRouteHeader().getDocRouteStatus()) || stateIsProcessed() || stateIsFinal();
 949  
     }
 950  
 
 951  
     /**
 952  
      * Indicates if the document is in the processed state or not.
 953  
      *
 954  
      * @return true if in the specified state
 955  
      */
 956  
     public boolean stateIsProcessed() {
 957  0
         return KEWConstants.ROUTE_HEADER_PROCESSED_CD.equals(getRouteHeader().getDocRouteStatus());
 958  
     }
 959  
 
 960  
     /**
 961  
      * Indicates if the document is in the final state or not.
 962  
      *
 963  
      * @return true if in the specified state
 964  
      */
 965  
     public boolean stateIsFinal() {
 966  0
         return KEWConstants.ROUTE_HEADER_FINAL_CD.equals(getRouteHeader().getDocRouteStatus());
 967  
     }
 968  
 
 969  
     /**
 970  
      * Returns the display value of the current document status
 971  
      * @return the display value of the current document status
 972  
      */
 973  
     public String getStatusDisplayValue() {
 974  0
         return (String) KEWConstants.DOCUMENT_STATUSES.get(getRouteHeader().getDocRouteStatus());
 975  
     }
 976  
 
 977  
     /**
 978  
      * Returns the principalId with which this WorkflowDocument was constructed
 979  
      * @return the principalId with which this WorkflowDocument was constructed
 980  
      */
 981  
     public String getPrincipalId() {
 982  0
         return principalId;
 983  
     }
 984  
 
 985  
     /**
 986  
      * Sets the principalId under which actions against this document should be taken
 987  
      * @param principalId principalId under which actions against this document should be taken
 988  
      */
 989  
     public void setPrincipalId(String principalId) {
 990  0
         this.principalId = principalId;
 991  0
     }
 992  
 
 993  
     /**
 994  
      * Checks if the document has been created or not (i.e. has a route header id or not) and issues
 995  
      * a call to the server to create the document if it has not yet been created.
 996  
      *
 997  
      * Also checks if the document content has been updated and saves it if it has.
 998  
      */
 999  
     private void createDocumentIfNeccessary() throws WorkflowException {
 1000  0
             if (getRouteHeader().getRouteHeaderId() == null) {
 1001  0
                     routeHeader = getWorkflowDocumentActions().createDocument(principalId, getRouteHeader());
 1002  
             }
 1003  0
             if (documentContent != null && documentContent.isModified()) {
 1004  0
                     saveDocumentContent(documentContent);
 1005  
             }
 1006  0
     }
 1007  
 
 1008  
     /**
 1009  
      * Like handleException except it returns a RuntimeException.
 1010  
      */
 1011  
     private RuntimeException handleExceptionAsRuntime(Exception e) {
 1012  0
             if (e instanceof RuntimeException) {
 1013  0
                     return (RuntimeException)e;
 1014  
             }
 1015  0
             return new WorkflowRuntimeException(e);
 1016  
     }
 1017  
 
 1018  
     // WORKFLOW 2.1: new methods
 1019  
 
 1020  
     /**
 1021  
      * Performs the 'blanketApprove' action on the document this WorkflowDocument represents.  If this is a new document,
 1022  
      * the document is created first.
 1023  
      * @param annotation the message to log for the action
 1024  
      * @param nodeName the extent to which to blanket approve; blanket approval will stop at this node
 1025  
      * @throws WorkflowException in case an error occurs blanket-approving the document
 1026  
      * @see WorkflowDocumentActions#blanketApprovalToNodes(UserIdDTO, RouteHeaderDTO, String, String[])
 1027  
      */
 1028  
     public void blanketApprove(String annotation, String nodeName) throws WorkflowException {
 1029  0
         blanketApprove(annotation, (nodeName == null ? null : new String[] { nodeName }));
 1030  0
     }
 1031  
 
 1032  
     /**
 1033  
      * Performs the 'blanketApprove' action on the document this WorkflowDocument represents.  If this is a new document,
 1034  
      * the document is created first.
 1035  
      * @param annotation the message to log for the action
 1036  
      * @param nodeNames the nodes at which blanket approval will stop (in case the blanket approval traverses a split, in which case there may be multiple "active" nodes)
 1037  
      * @throws WorkflowException in case an error occurs blanket-approving the document
 1038  
      * @see WorkflowDocumentActions#blanketApprovalToNodes(UserIdDTO, RouteHeaderDTO, String, String[])
 1039  
      */
 1040  
     public void blanketApprove(String annotation, String[] nodeNames) throws WorkflowException {
 1041  0
             createDocumentIfNeccessary();
 1042  0
             routeHeader = getWorkflowDocumentActions().blanketApprovalToNodes(principalId, getRouteHeader(), annotation, nodeNames);
 1043  0
             documentContentDirty = true;
 1044  0
     }
 1045  
 
 1046  
     /**
 1047  
      * The user taking action removes the action items for this workgroup and document from all other
 1048  
      * group members' action lists.   If this is a new document, the document is created first.
 1049  
      *
 1050  
      * @param annotation the message to log for the action
 1051  
      * @param workgroupId the workgroup on which to take authority
 1052  
      * @throws WorkflowException user taking action is not in workgroup
 1053  
      */
 1054  
     public void takeGroupAuthority(String annotation, String groupId) throws WorkflowException {
 1055  0
             createDocumentIfNeccessary();
 1056  0
             routeHeader = getWorkflowDocumentActions().takeGroupAuthority(principalId, getRouteHeader(), groupId, annotation);
 1057  0
             documentContentDirty = true;
 1058  0
     }
 1059  
 
 1060  
     /**
 1061  
      * The user that took the group authority is putting the action items back in the other users action lists.
 1062  
      * If this is a new document, the document is created first.
 1063  
      *
 1064  
      * @param annotation the message to log for the action
 1065  
      * @param workgroupId the workgroup on which to take authority
 1066  
      * @throws WorkflowException user taking action is not in workgroup or did not take workgroup authority
 1067  
      */
 1068  
     public void releaseGroupAuthority(String annotation, String groupId) throws WorkflowException {
 1069  0
             createDocumentIfNeccessary();
 1070  0
             routeHeader = getWorkflowDocumentActions().releaseGroupAuthority(principalId, getRouteHeader(), groupId, annotation);
 1071  0
             documentContentDirty = true;
 1072  0
     }
 1073  
 
 1074  
     /**
 1075  
      * Returns names of all active nodes the document is currently at.
 1076  
      *
 1077  
      * @return names of all active nodes the document is currently at.
 1078  
      * @throws WorkflowException if there is an error obtaining the currently active nodes on the document
 1079  
      * @see WorkflowUtility#getActiveNodeInstances(Long)
 1080  
      */
 1081  
     public String[] getNodeNames() throws WorkflowException {
 1082  0
             RouteNodeInstanceDTO[] activeNodeInstances = getWorkflowUtility().getActiveNodeInstances(getRouteHeaderId());
 1083  0
             String[] nodeNames = new String[(activeNodeInstances == null ? 0 : activeNodeInstances.length)];
 1084  0
             for (int index = 0; index < activeNodeInstances.length; index++) {
 1085  0
                     nodeNames[index] = activeNodeInstances[index].getName();
 1086  
             }
 1087  0
             return nodeNames;
 1088  
     }
 1089  
 
 1090  
     /**
 1091  
      * Performs the 'returnToPrevious' action on the document this WorkflowDocument represents.  If this is a new document,
 1092  
      * the document is created first.
 1093  
      * @param annotation the message to log for the action
 1094  
      * @param nodeName the node to return to
 1095  
      * @throws WorkflowException in case an error occurs returning to previous node
 1096  
      * @see WorkflowDocumentActions#returnDocumentToPreviousNode(UserIdDTO, RouteHeaderDTO, ReturnPointDTO, String)
 1097  
      */
 1098  
     public void returnToPreviousNode(String annotation, String nodeName) throws WorkflowException {
 1099  0
         ReturnPointDTO returnPoint = new ReturnPointDTO(nodeName);
 1100  0
         returnToPreviousNode(annotation, returnPoint);
 1101  0
     }
 1102  
 
 1103  
     /**
 1104  
      * Performs the 'returnToPrevious' action on the document this WorkflowDocument represents.  If this is a new document,
 1105  
      * the document is created first.
 1106  
      * @param annotation the message to log for the action
 1107  
      * @param ReturnPointDTO the node to return to
 1108  
      * @throws WorkflowException in case an error occurs returning to previous node
 1109  
      * @see WorkflowDocumentActions#returnDocumentToPreviousNode(UserIdDTO, RouteHeaderDTO, ReturnPointDTO, String)
 1110  
      */
 1111  
     public void returnToPreviousNode(String annotation, ReturnPointDTO returnPoint) throws WorkflowException {
 1112  0
             createDocumentIfNeccessary();
 1113  0
             routeHeader = getWorkflowDocumentActions().returnDocumentToPreviousNode(principalId, getRouteHeader(), returnPoint, annotation);
 1114  0
             documentContentDirty = true;
 1115  0
     }
 1116  
 
 1117  
     /**
 1118  
      * Moves the document from a current node in it's route to another node.  If this is a new document,
 1119  
      * the document is created first.
 1120  
      * @param MovePointDTO VO representing the node at which to start, and the number of steps to move (negative steps is reverse)
 1121  
      * @param annotation the message to log for the action
 1122  
      * @throws WorkflowException in case an error occurs moving the document
 1123  
      * @see WorkflowDocumentActions#moveDocument(UserIdDTO, RouteHeaderDTO, MovePointDTO, String)
 1124  
      */
 1125  
     public void moveDocument(MovePointDTO movePoint, String annotation) throws WorkflowException {
 1126  0
             createDocumentIfNeccessary();
 1127  0
             routeHeader =  getWorkflowDocumentActions().moveDocument(principalId, getRouteHeader(), movePoint, annotation);
 1128  0
             documentContentDirty = true;
 1129  0
     }
 1130  
 
 1131  
     /**
 1132  
      * Returns the route node instances that have been created so far during the life of this document.  This includes
 1133  
      * all previous instances which have already been processed and are no longer active.
 1134  
      * @return the route node instances that have been created so far during the life of this document
 1135  
      * @throws WorkflowException if there is an error getting the route node instances for the document
 1136  
      * @see WorkflowUtility#getDocumentRouteNodeInstances(Long)
 1137  
      */
 1138  
     public RouteNodeInstanceDTO[] getRouteNodeInstances() throws WorkflowException {
 1139  0
             return getWorkflowUtility().getDocumentRouteNodeInstances(getRouteHeaderId());
 1140  
     }
 1141  
 
 1142  
     /**
 1143  
      * Returns Array of Route Nodes Names that can be safely returned to using the 'returnToPreviousXXX' methods.
 1144  
      * Names are sorted in reverse chronological order.
 1145  
      *
 1146  
      * @return array of Route Nodes Names that can be safely returned to using the 'returnToPreviousXXX' methods
 1147  
      * @throws WorkflowException if an error occurs obtaining the names of the previous route nodes for this document
 1148  
      * @see WorkflowUtility#getPreviousRouteNodeNames(Long)
 1149  
      */
 1150  
     public String[] getPreviousNodeNames() throws WorkflowException {
 1151  0
             return getWorkflowUtility().getPreviousRouteNodeNames(getRouteHeaderId());
 1152  
         }
 1153  
 
 1154  
     /**
 1155  
      * Returns a document detail VO representing the route header along with action requests, actions taken,
 1156  
      * and route node instances.
 1157  
      * @return Returns a document detail VO representing the route header along with action requests, actions taken, and route node instances.
 1158  
      * @throws WorkflowException
 1159  
      */
 1160  
     public DocumentDetailDTO getDetail() throws WorkflowException {
 1161  0
             return getWorkflowUtility().getDocumentDetail(getRouteHeaderId());
 1162  
     }
 1163  
 
 1164  
     /**
 1165  
      * Saves the given DocumentContentVO for this document.
 1166  
      * @param documentContent document content VO to store for this document
 1167  
      * @since 2.3
 1168  
      * @see WorkflowDocumentActions#saveDocumentContent(DocumentContentDTO)
 1169  
      */
 1170  
     public DocumentContentDTO saveDocumentContent(DocumentContentDTO documentContent) throws WorkflowException {
 1171  0
             if (documentContent.getRouteHeaderId() == null) {
 1172  0
                     throw new WorkflowException("Document Content does not have a valid document ID.");
 1173  
             }
 1174  
             // important to check directly against getRouteHeader().getRouteHeaderId() instead of just getRouteHeaderId() because saveDocumentContent
 1175  
             // is called from createDocumentIfNeccessary which is called from getRouteHeaderId().  If that method was used, we would have an infinite loop.
 1176  0
             if (!documentContent.getRouteHeaderId().equals(getRouteHeader().getRouteHeaderId())) {
 1177  0
                     throw new WorkflowException("Attempted to save content on this document with an invalid document id of " + documentContent.getRouteHeaderId());
 1178  
             }
 1179  0
             DocumentContentDTO newDocumentContent = getWorkflowDocumentActions().saveDocumentContent(documentContent);
 1180  0
             this.documentContent = new ModifiableDocumentContentDTO(newDocumentContent);
 1181  0
             documentContentDirty = false;
 1182  0
             return this.documentContent;
 1183  
     }
 1184  
     
 1185  
     public void placeInExceptionRouting(String annotation) throws WorkflowException {
 1186  0
             createDocumentIfNeccessary();
 1187  0
             routeHeader = getWorkflowDocumentActions().placeInExceptionRouting(principalId, getRouteHeader(), annotation);
 1188  0
             documentContentDirty = true;
 1189  0
     }
 1190  
 
 1191  
 
 1192  
 
 1193  
     // DEPRECATED: as of Workflow 2.1
 1194  
 
 1195  
     /**
 1196  
      * @deprecated use blanketApprove(String annotation, String nodeName) instead
 1197  
      */
 1198  
     public void blanketApprove(String annotation, Integer routeLevel) throws WorkflowException {
 1199  0
             createDocumentIfNeccessary();
 1200  0
             routeHeader = getWorkflowDocumentActions().blanketApproval(principalId, getRouteHeader(), annotation, routeLevel);
 1201  0
             documentContentDirty = true;
 1202  0
     }
 1203  
 
 1204  
     /**
 1205  
      * @deprecated use getNodeNames() instead
 1206  
      */
 1207  
     public Integer getDocRouteLevel() {
 1208  0
         return routeHeader.getDocRouteLevel();
 1209  
     }
 1210  
 
 1211  
     /**
 1212  
      * @deprecated use returnToPreviousNode(String annotation, String nodeName) instead
 1213  
      */
 1214  
     public void returnToPreviousRouteLevel(String annotation, Integer destRouteLevel) throws WorkflowException {
 1215  0
             createDocumentIfNeccessary();
 1216  0
             getWorkflowDocumentActions().returnDocumentToPreviousRouteLevel(principalId, getRouteHeader(), destRouteLevel, annotation);
 1217  0
             documentContentDirty = true;
 1218  0
     }
 1219  
 
 1220  
     /**
 1221  
      * Returns a list of NoteVO representing the notes on the document
 1222  
      * @return a list of NoteVO representing the notes on the document
 1223  
      * @see RouteHeaderDTO#getNotes()
 1224  
      */
 1225  
     public List<NoteDTO> getNoteList(){
 1226  0
             List<NoteDTO> notesList = new ArrayList<NoteDTO>();
 1227  0
             NoteDTO[] notes = routeHeader.getNotes();
 1228  0
             if (notes != null){
 1229  0
                     for (int i=0; i<notes.length; i++){
 1230  0
                             if (! isDeletedNote(notes[i])){
 1231  0
                                     notesList.add(notes[i]);
 1232  
                             }
 1233  
                     }
 1234  
             }
 1235  0
             return notesList;
 1236  
     }
 1237  
 
 1238  
     /**
 1239  
      * Deletes a note from the document.  The deletion is deferred until the next time the document is committed (via an action).
 1240  
      * @param noteVO the note to remove from the document
 1241  
      */
 1242  
     public void deleteNote(NoteDTO noteVO){
 1243  0
             if (noteVO != null && noteVO.getNoteId()!=null){
 1244  0
                     NoteDTO noteToDelete = new NoteDTO();
 1245  0
                     noteToDelete.setNoteId(new Long(noteVO.getNoteId().longValue()));
 1246  
                     /*noteToDelete.setRouteHeaderId(noteVO.getRouteHeaderId());
 1247  
                     noteToDelete.setNoteAuthorWorkflowId(noteVO.getNoteAuthorWorkflowId());
 1248  
                     noteToDelete.setNoteCreateDate(noteVO.getNoteCreateDate());
 1249  
                     noteToDelete.setNoteText(noteVO.getNoteText());
 1250  
                     noteToDelete.setLockVerNbr(noteVO.getLockVerNbr());*/
 1251  0
                     increaseNotesToDeleteArraySizeByOne();
 1252  0
                     routeHeader.getNotesToDelete()[routeHeader.getNotesToDelete().length - 1]=noteToDelete;
 1253  
             }
 1254  0
     }
 1255  
 
 1256  
     /**
 1257  
      * Updates the note of the same note id, on the document. The update is deferred until the next time the document is committed (via an action).
 1258  
      * @param noteVO the note to update
 1259  
      */
 1260  
     public void updateNote (NoteDTO noteVO){
 1261  0
             boolean isUpdateNote = false;
 1262  0
             if (noteVO != null){
 1263  0
                     NoteDTO[] notes = routeHeader.getNotes();
 1264  0
                     NoteDTO  copyNote = new NoteDTO();
 1265  0
                         if (noteVO.getNoteId() != null){
 1266  0
                                 copyNote.setNoteId(new Long(noteVO.getNoteId().longValue()));
 1267  
                         }
 1268  
 
 1269  0
                         if (noteVO.getRouteHeaderId() != null){
 1270  0
                                 copyNote.setRouteHeaderId(new Long(noteVO.getRouteHeaderId().longValue()));
 1271  
                         } else {
 1272  0
                                 copyNote.setRouteHeaderId(routeHeader.getRouteHeaderId());
 1273  
                         }
 1274  
 
 1275  0
                         if (noteVO.getNoteAuthorWorkflowId() != null){
 1276  0
                                 copyNote.setNoteAuthorWorkflowId(new String(noteVO.getNoteAuthorWorkflowId()));
 1277  
                         } else {
 1278  0
                             copyNote.setNoteAuthorWorkflowId(principalId.toString())        ;
 1279  
                         }
 1280  
 
 1281  0
                         if (noteVO.getNoteCreateDate() != null){
 1282  0
                                 Calendar cal = Calendar.getInstance();
 1283  0
                                 cal.setTimeInMillis(noteVO.getNoteCreateDate().getTimeInMillis());
 1284  0
                                 copyNote.setNoteCreateDate(cal);
 1285  0
                         } else {
 1286  0
                                 copyNote.setNoteCreateDate(Calendar.getInstance());
 1287  
                         }
 1288  
 
 1289  0
                         if (noteVO.getNoteText() != null){
 1290  0
                                 copyNote.setNoteText(new String(noteVO.getNoteText()));
 1291  
                         }
 1292  0
                         if (noteVO.getLockVerNbr() != null){
 1293  0
                                 copyNote.setLockVerNbr(new Integer(noteVO.getLockVerNbr().intValue()));
 1294  
                         }
 1295  0
                     if (notes != null){
 1296  0
                             for (int i=0; i<notes.length; i++){
 1297  0
                                     if (notes[i].getNoteId()!= null && notes[i].getNoteId().equals(copyNote.getNoteId())){
 1298  0
                                             notes[i] = copyNote;
 1299  0
                                             isUpdateNote = true;
 1300  0
                                             break;
 1301  
                                     }
 1302  
                             }
 1303  
                     }
 1304  
                     // add new note to the notes array
 1305  0
                     if (! isUpdateNote){
 1306  0
                             copyNote.setNoteId(null);
 1307  0
                             increaseNotesArraySizeByOne();
 1308  0
                             routeHeader.getNotes()[routeHeader.getNotes().length-1]= copyNote;
 1309  
                     }
 1310  
             }
 1311  0
     }
 1312  
 
 1313  
     /**
 1314  
      * Sets a variable on the document.  The assignment is deferred until the next time the document is committed (via an action).
 1315  
      * @param name name of the variable
 1316  
      * @param value value of the variable
 1317  
      */
 1318  
     public void setVariable(String name, String value) throws WorkflowException {
 1319  0
             createDocumentIfNeccessary();
 1320  0
         getRouteHeader().setVariable(name, value);
 1321  0
     }
 1322  
 
 1323  
     /**
 1324  
      * Gets the value of a variable on the document, creating the document first if it does not exist.
 1325  
      * @param name variable name
 1326  
      * @return variable value
 1327  
      */
 1328  
     public String getVariable(String name) throws WorkflowException {
 1329  0
             createDocumentIfNeccessary();
 1330  0
         return getRouteHeader().getVariable(name);
 1331  
     }
 1332  
 
 1333  
     /**
 1334  
      *
 1335  
      * Tells workflow that the current the document is constructed as will receive all future requests routed to them
 1336  
      * disregarding any force action flags set on the action request.  Uses the setVariable method behind the seens so
 1337  
      * an action needs taken on the document to set this state on the document.
 1338  
      *
 1339  
      * @throws WorkflowException
 1340  
      */
 1341  
     public void setReceiveFutureRequests() throws WorkflowException {
 1342  0
         WorkflowUtility workflowUtility = getWorkflowUtility();
 1343  0
         this.setVariable(workflowUtility.getFutureRequestsKey(principalId), workflowUtility.getReceiveFutureRequestsValue());
 1344  0
     }
 1345  
 
 1346  
     /**
 1347  
      * Tell workflow that the current document is constructed as will not receive any future requests routed to them
 1348  
      * disregarding any force action flags set on action requests.  Uses the setVariable method behind the scenes so
 1349  
      * an action needs taken on the document to set this state on the document.
 1350  
      *
 1351  
      * @throws WorkflowException
 1352  
      */
 1353  
     public void setDoNotReceiveFutureRequests() throws WorkflowException {
 1354  0
         WorkflowUtility workflowUtility = getWorkflowUtility();
 1355  0
         this.setVariable(workflowUtility.getFutureRequestsKey(principalId), workflowUtility.getDoNotReceiveFutureRequestsValue());
 1356  0
     }
 1357  
 
 1358  
     /**
 1359  
      * Clears any state set on the document regarding a user receiving or not receiving action requests.  Uses the setVariable method
 1360  
      * behind the seens so an action needs taken on the document to set this state on the document.
 1361  
      *
 1362  
      * @throws WorkflowException
 1363  
      */
 1364  
     public void setClearFutureRequests() throws WorkflowException {
 1365  0
         WorkflowUtility workflowUtility = getWorkflowUtility();
 1366  0
         this.setVariable(workflowUtility.getFutureRequestsKey(principalId), workflowUtility.getClearFutureRequestsValue());
 1367  0
     }
 1368  
 
 1369  
     /**
 1370  
      * Deletes the note of with the same id as that of the argument on the document.
 1371  
      * @param noteVO the note to test for deletion
 1372  
      * @return whether the note is already marked for deletion.
 1373  
      */
 1374  
     private boolean isDeletedNote(NoteDTO noteVO) {
 1375  0
             NoteDTO[] notesToDelete = routeHeader.getNotesToDelete();
 1376  0
             if (notesToDelete != null){
 1377  0
                     for (int i=0; i<notesToDelete.length; i++){
 1378  0
                             if (notesToDelete[i].getNoteId().equals(noteVO.getNoteId())){
 1379  0
                                     return true;
 1380  
                             }
 1381  
                     }
 1382  
             }
 1383  0
             return false;
 1384  
     }
 1385  
 
 1386  
     /**
 1387  
      * Increases the size of the routeHeader notes VO array
 1388  
      */
 1389  
    private void increaseNotesArraySizeByOne() {
 1390  
            NoteDTO[] tempArray;
 1391  0
            NoteDTO[] notes = routeHeader.getNotes();
 1392  0
            if (notes == null){
 1393  0
                    tempArray = new NoteDTO[1];
 1394  
            } else {
 1395  0
                    tempArray = new NoteDTO[notes.length + 1];
 1396  0
                    for (int i=0; i<notes.length; i++){
 1397  0
                            tempArray[i] = notes[i];
 1398  
                    }
 1399  
            }
 1400  0
            routeHeader.setNotes(tempArray);
 1401  0
    }
 1402  
 
 1403  
    /**
 1404  
     * Increases the size of the routeHeader notesToDelete VO array
 1405  
     */
 1406  
    private void increaseNotesToDeleteArraySizeByOne() {
 1407  
            NoteDTO[] tempArray;
 1408  0
            NoteDTO[] notesToDelete = routeHeader.getNotesToDelete();
 1409  0
            if (notesToDelete == null){
 1410  0
                    tempArray = new NoteDTO[1];
 1411  
            } else {
 1412  0
                    tempArray = new NoteDTO[notesToDelete.length + 1];
 1413  0
                    for (int i=0; i<notesToDelete.length; i++){
 1414  0
                            tempArray[i] = notesToDelete[i];
 1415  
                    }
 1416  
            }
 1417  0
            routeHeader.setNotesToDelete(tempArray);
 1418  0
    }
 1419  
    
 1420  
    //add 1 link between 2 docs by DTO, double link added
 1421  
    public void addLinkedDocument(DocumentLinkDTO docLinkVO) throws WorkflowException{
 1422  
            try{
 1423  0
                    if(DocumentLinkDTO.checkDocLink(docLinkVO))
 1424  0
                            getWorkflowUtility().addDocumentLink(docLinkVO);
 1425  
            }
 1426  0
            catch(Exception e){
 1427  0
                    throw handleExceptionAsRuntime(e); 
 1428  0
            } 
 1429  0
    }
 1430  
    
 1431  
    //get link from orgn doc to a specifc doc
 1432  
    public DocumentLinkDTO getLinkedDocument(DocumentLinkDTO docLinkVO) throws WorkflowException{
 1433  
            try{
 1434  0
                    if(DocumentLinkDTO.checkDocLink(docLinkVO))
 1435  0
                            return getWorkflowUtility().getLinkedDocument(docLinkVO);
 1436  
                    else
 1437  0
                            return null;
 1438  
            }
 1439  0
            catch(Exception e){
 1440  0
                    throw handleExceptionAsRuntime(e); 
 1441  
            }
 1442  
    }
 1443  
    
 1444  
    //get all links to orgn doc
 1445  
    public List<DocumentLinkDTO> getLinkedDocumentsByDocId(Long id) throws WorkflowException{
 1446  0
            if(id == null)
 1447  0
                    throw new WorkflowException("doc id is null");
 1448  
            try{   
 1449  0
                    return getWorkflowUtility().getLinkedDocumentsByDocId(id);
 1450  
            } 
 1451  0
            catch (Exception e) {
 1452  0
                    throw handleExceptionAsRuntime(e);
 1453  
            }
 1454  
    }
 1455  
    
 1456  
    //remove all links from orgn: double links removed
 1457  
    public void removeLinkedDocuments(Long docId) throws WorkflowException{
 1458  
            
 1459  0
            if(docId == null)
 1460  0
                    throw new WorkflowException("doc id is null");
 1461  
            
 1462  
            try{   
 1463  0
                    getWorkflowUtility().deleteDocumentLinksByDocId(docId);
 1464  
            } 
 1465  0
            catch (Exception e) {
 1466  0
                    throw handleExceptionAsRuntime(e);
 1467  0
            }
 1468  0
    }
 1469  
    
 1470  
    //remove link between 2 docs, double link removed
 1471  
    public void removeLinkedDocument(DocumentLinkDTO docLinkVO) throws WorkflowException{
 1472  
            
 1473  
            try{
 1474  0
                    if(DocumentLinkDTO.checkDocLink(docLinkVO))
 1475  0
                            getWorkflowUtility().deleteDocumentLink(docLinkVO);
 1476  
            }
 1477  0
            catch(Exception e){
 1478  0
                    throw handleExceptionAsRuntime(e); 
 1479  0
            } 
 1480  0
    }
 1481  
 
 1482  
 }