Coverage Report - org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue
 
Classes in this File Line Coverage Branch Coverage Complexity
DocumentRouteHeaderValue
0%
0/414
0%
0/172
1.949
 
 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.kew.routeheader;
 17  
 
 18  
 import java.sql.Timestamp;
 19  
 import java.util.ArrayList;
 20  
 import java.util.Collection;
 21  
 import java.util.HashMap;
 22  
 import java.util.Iterator;
 23  
 import java.util.List;
 24  
 import java.util.Map;
 25  
 
 26  
 import javax.persistence.CascadeType;
 27  
 import javax.persistence.Column;
 28  
 import javax.persistence.Entity;
 29  
 import javax.persistence.FetchType;
 30  
 import javax.persistence.GeneratedValue;
 31  
 import javax.persistence.Id;
 32  
 import javax.persistence.JoinColumn;
 33  
 import javax.persistence.JoinTable;
 34  
 import javax.persistence.ManyToMany;
 35  
 import javax.persistence.NamedQueries;
 36  
 import javax.persistence.NamedQuery;
 37  
 import javax.persistence.OneToMany;
 38  
 import javax.persistence.OrderBy;
 39  
 import javax.persistence.Table;
 40  
 import javax.persistence.Transient;
 41  
 
 42  
 import org.apache.commons.lang.ObjectUtils;
 43  
 import org.apache.commons.lang.StringUtils;
 44  
 import org.apache.log4j.Logger;
 45  
 import org.hibernate.annotations.Fetch;
 46  
 import org.hibernate.annotations.FetchMode;
 47  
 import org.hibernate.annotations.GenericGenerator;
 48  
 import org.hibernate.annotations.Parameter;
 49  
 import org.joda.time.DateTime;
 50  
 import org.kuali.rice.core.api.exception.RiceRuntimeException;
 51  
 import org.kuali.rice.core.util.KeyValue;
 52  
 import org.kuali.rice.kew.actionitem.ActionItem;
 53  
 import org.kuali.rice.kew.actionlist.CustomActionListAttribute;
 54  
 import org.kuali.rice.kew.actionlist.DefaultCustomActionListAttribute;
 55  
 import org.kuali.rice.kew.actionrequest.ActionRequestFactory;
 56  
 import org.kuali.rice.kew.actionrequest.ActionRequestValue;
 57  
 import org.kuali.rice.kew.actiontaken.ActionTakenValue;
 58  
 import org.kuali.rice.kew.api.KewApiConstants;
 59  
 import org.kuali.rice.kew.api.WorkflowRuntimeException;
 60  
 import org.kuali.rice.kew.api.document.Document;
 61  
 import org.kuali.rice.kew.api.document.DocumentContract;
 62  
 import org.kuali.rice.kew.api.document.DocumentStatus;
 63  
 import org.kuali.rice.kew.api.document.DocumentUpdate;
 64  
 import org.kuali.rice.kew.docsearch.SearchableAttributeValue;
 65  
 import org.kuali.rice.kew.doctype.ApplicationDocumentStatus;
 66  
 import org.kuali.rice.kew.doctype.DocumentTypePolicy;
 67  
 import org.kuali.rice.kew.doctype.bo.DocumentType;
 68  
 import org.kuali.rice.kew.dto.DTOConverter;
 69  
 import org.kuali.rice.kew.dto.RouteHeaderDTO;
 70  
 import org.kuali.rice.kew.engine.CompatUtils;
 71  
 import org.kuali.rice.kew.engine.node.Branch;
 72  
 import org.kuali.rice.kew.engine.node.BranchState;
 73  
 import org.kuali.rice.kew.engine.node.RouteNode;
 74  
 import org.kuali.rice.kew.engine.node.RouteNodeInstance;
 75  
 import org.kuali.rice.kew.exception.InvalidActionTakenException;
 76  
 import org.kuali.rice.kew.exception.ResourceUnavailableException;
 77  
 import org.kuali.rice.kew.exception.WorkflowException;
 78  
 import org.kuali.rice.kew.mail.CustomEmailAttribute;
 79  
 import org.kuali.rice.kew.mail.CustomEmailAttributeImpl;
 80  
 import org.kuali.rice.kew.notes.CustomNoteAttribute;
 81  
 import org.kuali.rice.kew.notes.CustomNoteAttributeImpl;
 82  
 import org.kuali.rice.kew.notes.Note;
 83  
 import org.kuali.rice.kew.service.KEWServiceLocator;
 84  
 import org.kuali.rice.kew.util.CodeTranslator;
 85  
 import org.kuali.rice.kew.util.KEWConstants;
 86  
 import org.kuali.rice.kim.api.identity.principal.Principal;
 87  
 import org.kuali.rice.krad.bo.PersistableBusinessObjectBase;
 88  
 
 89  
 
 90  
 
 91  
 /**
 92  
  * A document within KEW.  A document effectively represents a process that moves through
 93  
  * the workflow engine.  It is created from a particular {@link DocumentType} and follows
 94  
  * the route path defined by that DocumentType.
 95  
  *
 96  
  * <p>During a document's lifecycle it progresses through a series of statuses, starting
 97  
  * with INITIATED and moving to one of the terminal states (such as FINAL, CANCELED, etc).
 98  
  * The list of status on a document are defined in the {@link KEWConstants} class and
 99  
  * include the constants starting with "ROUTE_HEADER_" and ending with "_CD".
 100  
  *
 101  
  * <p>Associated with the document is the document content.  The document content is XML
 102  
  * which represents the content of that document.  This XML content is typically used
 103  
  * to make routing decisions for the document.
 104  
  *
 105  
  * <p>A document has associated with it a set of {@link ActionRequestValue} object and
 106  
  * {@link ActionTakenValue} objects.  Action Requests represent requests for user
 107  
  * action (such as Approve, Acknowledge, etc).  Action Takens represent action that
 108  
  * users have performed on the document, such as approvals or cancelling of the document.
 109  
  *
 110  
  * <p>The instantiated route path of a document is defined by it's graph of
 111  
  * {@link RouteNodeInstance} objects.  The path starts at the initial node of the document
 112  
  * and progresses from there following the next nodes of each node instance.  The current
 113  
  * active nodes on the document are defined by the "active" flag on the node instance
 114  
  * where are not marked as "complete".
 115  
  *
 116  
  * @see DocumentType
 117  
  * @see ActionRequestValue
 118  
  * @see ActionItem
 119  
  * @see ActionTakenValue
 120  
  * @see RouteNodeInstance
 121  
  * @see KEWConstants
 122  
  *
 123  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 124  
  */
 125  
 @Entity
 126  
 @Table(name="KREW_DOC_HDR_T")
 127  
 //@Sequence(name="KREW_DOC_HDR_S", property="documentId")
 128  
 @NamedQueries({
 129  
         @NamedQuery(name="DocumentRouteHeaderValue.FindByDocumentId", query="select d from DocumentRouteHeaderValue as d where d.documentId = :documentId"),
 130  
         @NamedQuery(name="DocumentRouteHeaderValue.QuickLinks.FindWatchedDocumentsByInitiatorWorkflowId", query="SELECT NEW org.kuali.rice.kew.quicklinks.WatchedDocument(documentId, docRouteStatus, docTitle) FROM DocumentRouteHeaderValue WHERE initiatorWorkflowId = :initiatorWorkflowId AND docRouteStatus IN ('"+ KEWConstants.ROUTE_HEADER_ENROUTE_CD +"','"+ KEWConstants.ROUTE_HEADER_EXCEPTION_CD +"') ORDER BY createDate DESC"),
 131  
         @NamedQuery(name="DocumentRouteHeaderValue.GetAppDocId", query="SELECT d.appDocId from DocumentRouteHeaderValue as d where d.documentId = :documentId")
 132  
 })
 133  
 public class DocumentRouteHeaderValue extends PersistableBusinessObjectBase implements DocumentContract {
 134  
     private static final long serialVersionUID = -4700736340527913220L;
 135  0
     private static final Logger LOG = Logger.getLogger(DocumentRouteHeaderValue.class);
 136  
 
 137  
     public static final String CURRENT_ROUTE_NODE_NAME_DELIMITER = ", ";
 138  
     
 139  
     @Column(name="DOC_TYP_ID")
 140  
         private String documentTypeId;
 141  
     @Column(name="DOC_HDR_STAT_CD")
 142  
         private java.lang.String docRouteStatus;
 143  
     @Column(name="RTE_LVL")
 144  
         private java.lang.Integer docRouteLevel;
 145  
         @Column(name="STAT_MDFN_DT")
 146  
         private java.sql.Timestamp statusModDate;
 147  
         @Column(name="CRTE_DT")
 148  
         private java.sql.Timestamp createDate;
 149  
         @Column(name="APRV_DT")
 150  
         private java.sql.Timestamp approvedDate;
 151  
         @Column(name="FNL_DT")
 152  
         private java.sql.Timestamp finalizedDate;
 153  
     @Transient
 154  
     private DocumentRouteHeaderValueContent documentContent;
 155  
     @Column(name="TTL")
 156  
         private java.lang.String docTitle;
 157  
     @Column(name="APP_DOC_ID")
 158  
         private java.lang.String appDocId;
 159  0
     @Column(name="DOC_VER_NBR")
 160  
     private java.lang.Integer docVersion = new Integer(KewApiConstants.DocumentContentVersions.NODAL);
 161  
     @Column(name="INITR_PRNCPL_ID")
 162  
         private java.lang.String initiatorWorkflowId;
 163  
     @Column(name="RTE_PRNCPL_ID")
 164  
         private java.lang.String routedByUserWorkflowId;
 165  
         @Column(name="RTE_STAT_MDFN_DT")
 166  
         private java.sql.Timestamp routeStatusDate;
 167  
         @Column(name="RTE_LVL_MDFN_DT")
 168  
         private java.sql.Timestamp routeLevelDate;
 169  
     @Column(name="APP_DOC_STAT")
 170  
         private java.lang.String appDocStatus;
 171  
         @Column(name="APP_DOC_STAT_MDFN_DT")
 172  
         private java.sql.Timestamp appDocStatusDate;
 173  
         
 174  
     @Id
 175  
     @GeneratedValue(generator="KREW_DOC_HDR_S")
 176  
         @GenericGenerator(name="KREW_DOC_HDR_S",strategy="org.hibernate.id.enhanced.SequenceStyleGenerator",parameters={
 177  
                         @Parameter(name="sequence_name",value="KREW_DOC_HDR_S"),
 178  
                         @Parameter(name="value_column",value="id")
 179  
         })
 180  
         @Column(name="DOC_HDR_ID")
 181  
         private java.lang.String documentId;
 182  
     
 183  
     //@OneToMany(fetch=FetchType.EAGER, cascade=CascadeType.REMOVE, mappedBy="routeHeader")
 184  
     //@Fetch(value = FetchMode.SELECT)
 185  
     //private List<ActionRequestValue> actionRequests = new ArrayList<ActionRequestValue>();
 186  
     
 187  
     //@OneToMany(fetch=FetchType.EAGER, cascade=CascadeType.REMOVE, mappedBy="routeHeader")
 188  
     //@OrderBy("actionDate ASC")
 189  
     //@Fetch(value = FetchMode.SELECT)
 190  
     //private List<ActionTakenValue> actionsTaken = new ArrayList<ActionTakenValue>();
 191  
     
 192  
     //@OneToMany(fetch=FetchType.EAGER, cascade=CascadeType.REMOVE, mappedBy="routeHeader")
 193  
     //@Fetch(value = FetchMode.SELECT)
 194  
     //private List<ActionItem> actionItems = new ArrayList<ActionItem>();
 195  
     
 196  
     /**
 197  
      * The appDocStatusHistory keeps a list of Application Document Status transitions
 198  
      * for the document.  It tracks the previous status, the new status, and a timestamp of the 
 199  
      * transition for each status transition.
 200  
      */
 201  0
     @OneToMany(fetch=FetchType.EAGER, cascade={CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE}, mappedBy="documentId")
 202  
     //@JoinColumn(referencedColumnName="DOC_HDR_ID")
 203  
     @OrderBy("statusTransitionId ASC")
 204  
     @Fetch(value = FetchMode.SELECT)
 205  
     private List<DocumentStatusTransition> appDocStatusHistory = new ArrayList<DocumentStatusTransition>();
 206  
     
 207  0
     @OneToMany(fetch=FetchType.LAZY, cascade={CascadeType.PERSIST, CascadeType.REMOVE})
 208  
     @JoinColumn(name="DOC_HDR_ID")
 209  
     @OrderBy("noteId ASC")
 210  
     private List<Note> notes = new ArrayList<Note>();
 211  
     
 212  0
     @Transient
 213  
     private List<SearchableAttributeValue> searchableAttributeValues = new ArrayList<SearchableAttributeValue>();
 214  0
     @Transient
 215  
     private Collection queueItems = new ArrayList();
 216  0
     @Transient
 217  
     private boolean routingReport = false;
 218  
     @Transient
 219  
     private List<ActionRequestValue> simulatedActionRequests;
 220  
     
 221  
     private static final boolean FINAL_STATE = true;
 222  
     protected static final HashMap<String,String> legalActions;
 223  
     protected static final HashMap<String,String> stateTransitionMap;
 224  
 
 225  
     /* New Workflow 2.1 Field */
 226  0
     @ManyToMany(fetch=FetchType.EAGER, cascade=CascadeType.REMOVE)
 227  
     @JoinTable(name = "KREW_INIT_RTE_NODE_INSTN_T", joinColumns = @JoinColumn(name = "DOC_HDR_ID"), inverseJoinColumns = @JoinColumn(name = "RTE_NODE_INSTN_ID")) 
 228  
     @Fetch(value = FetchMode.SELECT)
 229  
     private List<RouteNodeInstance> initialRouteNodeInstances = new ArrayList<RouteNodeInstance>();
 230  
 
 231  
     static {
 232  0
         stateTransitionMap = new HashMap<String,String>();
 233  0
         stateTransitionMap.put(KEWConstants.ROUTE_HEADER_INITIATED_CD, KEWConstants.ROUTE_HEADER_SAVED_CD + KEWConstants.ROUTE_HEADER_ENROUTE_CD + KEWConstants.ROUTE_HEADER_CANCEL_CD);
 234  
 
 235  0
         stateTransitionMap.put(KEWConstants.ROUTE_HEADER_SAVED_CD, KEWConstants.ROUTE_HEADER_SAVED_CD + KEWConstants.ROUTE_HEADER_ENROUTE_CD + KEWConstants.ROUTE_HEADER_CANCEL_CD + KEWConstants.ROUTE_HEADER_PROCESSED_CD);
 236  
 
 237  0
         stateTransitionMap.put(KEWConstants.ROUTE_HEADER_ENROUTE_CD, KEWConstants.ROUTE_HEADER_DISAPPROVED_CD +
 238  
                 KEWConstants.ROUTE_HEADER_CANCEL_CD + KEWConstants.ROUTE_HEADER_PROCESSED_CD + KEWConstants.ROUTE_HEADER_EXCEPTION_CD + KEWConstants.ROUTE_HEADER_SAVED_CD);
 239  0
         stateTransitionMap.put(KEWConstants.ROUTE_HEADER_DISAPPROVED_CD, "");
 240  0
         stateTransitionMap.put(KEWConstants.ROUTE_HEADER_CANCEL_CD, "");
 241  0
         stateTransitionMap.put(KEWConstants.ROUTE_HEADER_FINAL_CD, "");
 242  0
         stateTransitionMap.put(KEWConstants.ROUTE_HEADER_EXCEPTION_CD, KEWConstants.ROUTE_HEADER_EXCEPTION_CD + KEWConstants.ROUTE_HEADER_ENROUTE_CD + KEWConstants.ROUTE_HEADER_CANCEL_CD + KEWConstants.ROUTE_HEADER_PROCESSED_CD + KEWConstants.ROUTE_HEADER_DISAPPROVED_CD + KEWConstants.ROUTE_HEADER_SAVED_CD);
 243  0
         stateTransitionMap.put(KEWConstants.ROUTE_HEADER_PROCESSED_CD, KEWConstants.ROUTE_HEADER_FINAL_CD + KEWConstants.ROUTE_HEADER_PROCESSED_CD);
 244  
 
 245  0
         legalActions = new HashMap<String,String>();
 246  0
         legalActions.put(KEWConstants.ROUTE_HEADER_INITIATED_CD, KEWConstants.ACTION_TAKEN_FYI_CD + KEWConstants.ACTION_TAKEN_ACKNOWLEDGED_CD + KEWConstants.ACTION_TAKEN_SAVED_CD + KEWConstants.ACTION_TAKEN_COMPLETED_CD + KEWConstants.ACTION_TAKEN_ROUTED_CD + KEWConstants.ACTION_TAKEN_CANCELED_CD + KEWConstants.ACTION_TAKEN_ADHOC_CD + KEWConstants.ACTION_TAKEN_ADHOC_REVOKED_CD + KEWConstants.ACTION_TAKEN_BLANKET_APPROVE_CD + KEWConstants.ACTION_TAKEN_MOVE_CD);
 247  0
         legalActions.put(KEWConstants.ROUTE_HEADER_SAVED_CD, KEWConstants.ACTION_TAKEN_FYI_CD + KEWConstants.ACTION_TAKEN_ACKNOWLEDGED_CD + KEWConstants.ACTION_TAKEN_SAVED_CD + KEWConstants.ACTION_TAKEN_COMPLETED_CD + KEWConstants.ACTION_TAKEN_ROUTED_CD + KEWConstants.ACTION_TAKEN_APPROVED_CD + KEWConstants.ACTION_TAKEN_CANCELED_CD + KEWConstants.ACTION_TAKEN_ADHOC_CD + KEWConstants.ACTION_TAKEN_ADHOC_REVOKED_CD + KEWConstants.ACTION_TAKEN_BLANKET_APPROVE_CD + KEWConstants.ACTION_TAKEN_MOVE_CD);
 248  
         /* ACTION_TAKEN_ROUTED_CD not included in enroute state
 249  
          * ACTION_TAKEN_SAVED_CD removed as of version 2.4
 250  
          */
 251  0
         legalActions.put(KEWConstants.ROUTE_HEADER_ENROUTE_CD, /*KEWConstants.ACTION_TAKEN_SAVED_CD + KEWConstants.ACTION_TAKEN_ROUTED_CD + */KEWConstants.ACTION_TAKEN_APPROVED_CD + KEWConstants.ACTION_TAKEN_ACKNOWLEDGED_CD + KEWConstants.ACTION_TAKEN_FYI_CD + KEWConstants.ACTION_TAKEN_ADHOC_CD + KEWConstants.ACTION_TAKEN_ADHOC_REVOKED_CD + KEWConstants.ACTION_TAKEN_BLANKET_APPROVE_CD + KEWConstants.ACTION_TAKEN_CANCELED_CD + KEWConstants.ACTION_TAKEN_COMPLETED_CD + KEWConstants.ACTION_TAKEN_DENIED_CD + KEWConstants.ACTION_TAKEN_SU_APPROVED_CD + KEWConstants.ACTION_TAKEN_SU_CANCELED_CD + KEWConstants.ACTION_TAKEN_SU_DISAPPROVED_CD + KEWConstants.ACTION_TAKEN_SU_ROUTE_LEVEL_APPROVED_CD + KEWConstants.ACTION_TAKEN_RETURNED_TO_PREVIOUS_CD + KEWConstants.ACTION_TAKEN_SU_RETURNED_TO_PREVIOUS_CD + KEWConstants.ACTION_TAKEN_MOVE_CD);
 252  
         /* ACTION_TAKEN_ROUTED_CD not included in exception state
 253  
          * ACTION_TAKEN_SAVED_CD removed as of version 2.4.2
 254  
          */
 255  0
         legalActions.put(KEWConstants.ROUTE_HEADER_EXCEPTION_CD, /*KEWConstants.ACTION_TAKEN_SAVED_CD + */KEWConstants.ACTION_TAKEN_FYI_CD + KEWConstants.ACTION_TAKEN_ACKNOWLEDGED_CD + KEWConstants.ACTION_TAKEN_ADHOC_CD + KEWConstants.ACTION_TAKEN_ADHOC_REVOKED_CD + KEWConstants.ACTION_TAKEN_APPROVED_CD + KEWConstants.ACTION_TAKEN_BLANKET_APPROVE_CD + KEWConstants.ACTION_TAKEN_CANCELED_CD + KEWConstants.ACTION_TAKEN_COMPLETED_CD + KEWConstants.ACTION_TAKEN_DENIED_CD + KEWConstants.ACTION_TAKEN_SU_APPROVED_CD + KEWConstants.ACTION_TAKEN_SU_CANCELED_CD + KEWConstants.ACTION_TAKEN_SU_DISAPPROVED_CD + KEWConstants.ACTION_TAKEN_SU_ROUTE_LEVEL_APPROVED_CD + KEWConstants.ACTION_TAKEN_RETURNED_TO_PREVIOUS_CD + KEWConstants.ACTION_TAKEN_SU_RETURNED_TO_PREVIOUS_CD + KEWConstants.ACTION_TAKEN_MOVE_CD);
 256  0
         legalActions.put(KEWConstants.ROUTE_HEADER_FINAL_CD, KEWConstants.ACTION_TAKEN_FYI_CD + KEWConstants.ACTION_TAKEN_ACKNOWLEDGED_CD + KEWConstants.ACTION_TAKEN_ADHOC_REVOKED_CD);
 257  0
         legalActions.put(KEWConstants.ROUTE_HEADER_CANCEL_CD, KEWConstants.ACTION_TAKEN_FYI_CD + KEWConstants.ACTION_TAKEN_ACKNOWLEDGED_CD + KEWConstants.ACTION_TAKEN_ADHOC_REVOKED_CD);
 258  0
         legalActions.put(KEWConstants.ROUTE_HEADER_DISAPPROVED_CD, KEWConstants.ACTION_TAKEN_FYI_CD + KEWConstants.ACTION_TAKEN_ACKNOWLEDGED_CD + KEWConstants.ACTION_TAKEN_ADHOC_REVOKED_CD);
 259  0
         legalActions.put(KEWConstants.ROUTE_HEADER_PROCESSED_CD, KEWConstants.ACTION_TAKEN_FYI_CD + KEWConstants.ACTION_TAKEN_ACKNOWLEDGED_CD + KEWConstants.ACTION_TAKEN_ADHOC_REVOKED_CD);
 260  0
     }
 261  
 
 262  0
     public DocumentRouteHeaderValue() {
 263  0
     }
 264  
 
 265  
     public Principal getInitiatorPrincipal() {
 266  
             // if we are running a simulation, there will be no initiator
 267  0
             if (getInitiatorWorkflowId() == null) {
 268  0
                     return null;
 269  
             }
 270  0
             return KEWServiceLocator.getIdentityHelperService().getPrincipal(getInitiatorWorkflowId());
 271  
     }
 272  
     
 273  
     public Principal getRoutedByPrincipal()
 274  
     {
 275  0
             if (getRoutedByUserWorkflowId() == null) {
 276  0
                     return null;
 277  
             }
 278  0
             return KEWServiceLocator.getIdentityHelperService().getPrincipal(getRoutedByUserWorkflowId());
 279  
            }
 280  
         
 281  
     public String getInitiatorDisplayName() {
 282  0
         return KEWServiceLocator.getIdentityHelperService().getPerson(getInitiatorWorkflowId()).getName();
 283  
     }
 284  
 
 285  
     public String getRoutedByDisplayName() {
 286  0
             return KEWServiceLocator.getIdentityHelperService().getPerson(getRoutedByUserWorkflowId()).getName();
 287  
     }
 288  
         
 289  
     public String getCurrentRouteLevelName() {
 290  0
         String name = "Not Found";
 291  
         // TODO the isRouteLevelDocument junk can be ripped out
 292  0
             if(routingReport){
 293  0
                     name = "Routing Report";
 294  0
             } else if (CompatUtils.isRouteLevelDocument(this)) {
 295  0
             int routeLevelInt = getDocRouteLevel().intValue();
 296  0
             LOG.info("Getting current route level name for a Route level document: " + routeLevelInt+CURRENT_ROUTE_NODE_NAME_DELIMITER+documentId);
 297  0
             List routeLevelNodes = CompatUtils.getRouteLevelCompatibleNodeList(getDocumentType());
 298  0
             LOG.info("Route level compatible node list has " + routeLevelNodes.size() + " nodes");
 299  0
             if (routeLevelInt < routeLevelNodes.size()) {
 300  0
                 name = ((RouteNode)routeLevelNodes.get(routeLevelInt)).getRouteNodeName();
 301  
             }
 302  0
         } else {
 303  0
                 name = "";
 304  0
                 for (Iterator<String> iterator = getCurrentNodeNames().iterator(); iterator.hasNext();) {
 305  0
                         String nodeName = iterator.next();
 306  0
                         name += nodeName + (iterator.hasNext() ? CURRENT_ROUTE_NODE_NAME_DELIMITER : "");
 307  0
                 }
 308  
         }
 309  0
         return name;
 310  
     }
 311  
     
 312  
     public List<String> getCurrentNodeNames() {
 313  0
             List<String> currentNodeNames = new ArrayList<String>();
 314  0
             Collection<RouteNodeInstance> nodeInstances = KEWServiceLocator.getRouteNodeService().getActiveNodeInstances(getDocumentId());
 315  0
         if (nodeInstances.isEmpty()) {
 316  0
             nodeInstances = KEWServiceLocator.getRouteNodeService().getTerminalNodeInstances(getDocumentId());
 317  
         }
 318  0
         for (RouteNodeInstance nodeInstance : nodeInstances) {
 319  0
             currentNodeNames.add(nodeInstance.getRouteNode().getRouteNodeName());
 320  
         }
 321  0
         return currentNodeNames;
 322  
     }
 323  
 
 324  
     public String getRouteStatusLabel() {
 325  0
         return CodeTranslator.getRouteStatusLabel(getDocRouteStatus());
 326  
     }
 327  
 
 328  
     public String getDocRouteStatusLabel() {
 329  0
         return CodeTranslator.getRouteStatusLabel(getDocRouteStatus());
 330  
     }
 331  
     /**
 332  
      * 
 333  
      * This method returns the Document Status Policy for the document type associated with this Route Header.
 334  
      * The Document Status Policy denotes whether the KEW Route Status, or the Application Document Status,
 335  
      * or both are to be displayed.
 336  
      * 
 337  
      * @return
 338  
      */
 339  
     public String getDocStatusPolicy() {
 340  0
             return getDocumentType().getDocumentStatusPolicy().getPolicyStringValue();
 341  
     }
 342  
     
 343  
     public Collection getQueueItems() {
 344  0
         return queueItems;
 345  
     }
 346  
 
 347  
     public void setQueueItems(Collection queueItems) {
 348  0
         this.queueItems = queueItems;
 349  0
     }
 350  
 
 351  
     public List<ActionItem> getActionItems() {
 352  0
         return (List<ActionItem>) KEWServiceLocator.getActionListService().findByDocumentId(documentId);
 353  
     }
 354  
 
 355  
     public List<ActionTakenValue> getActionsTaken() {
 356  0
         return (List<ActionTakenValue>) KEWServiceLocator.getActionTakenService().findByDocumentId(documentId);
 357  
     }
 358  
 
 359  
     public List<ActionRequestValue> getActionRequests() {
 360  0
             if (this.simulatedActionRequests == null || this.simulatedActionRequests.isEmpty()) {
 361  0
                     return KEWServiceLocator.getActionRequestService().findAllActionRequestsByDocumentId(documentId);
 362  
             } else {
 363  0
                     return this.simulatedActionRequests;
 364  
             }
 365  
     }
 366  
 
 367  
     public List<ActionRequestValue> getSimulatedActionRequests() {
 368  0
             if (this.simulatedActionRequests == null) {
 369  0
                     this.simulatedActionRequests = new ArrayList<ActionRequestValue>();
 370  
             }
 371  0
                 return this.simulatedActionRequests;
 372  
         }
 373  
 
 374  
         public void setSimulatedActionRequests(List<ActionRequestValue> simulatedActionRequests) {
 375  0
                 this.simulatedActionRequests = simulatedActionRequests;
 376  0
         }
 377  
 
 378  
         public DocumentType getDocumentType() {
 379  0
             return KEWServiceLocator.getDocumentTypeService().findById(getDocumentTypeId());
 380  
     }
 381  
 
 382  
     public java.lang.String getAppDocId() {
 383  0
         return appDocId;
 384  
     }
 385  
 
 386  
     public void setAppDocId(java.lang.String appDocId) {
 387  0
         this.appDocId = appDocId;
 388  0
     }
 389  
 
 390  
     public java.sql.Timestamp getApprovedDate() {
 391  0
         return approvedDate;
 392  
     }
 393  
 
 394  
     public void setApprovedDate(java.sql.Timestamp approvedDate) {
 395  0
         this.approvedDate = approvedDate;
 396  0
     }
 397  
 
 398  
     public java.sql.Timestamp getCreateDate() {
 399  0
         return createDate;
 400  
     }
 401  
 
 402  
     public void setCreateDate(java.sql.Timestamp createDate) {
 403  0
         this.createDate = createDate;
 404  0
     }
 405  
 
 406  
     public java.lang.String getDocContent() {
 407  0
             return getDocumentContent().getDocumentContent();
 408  
     }
 409  
 
 410  
     public void setDocContent(java.lang.String docContent) {
 411  0
             DocumentRouteHeaderValueContent content = getDocumentContent();
 412  0
             content.setDocumentContent(docContent);
 413  0
     }
 414  
 
 415  
     public java.lang.Integer getDocRouteLevel() {
 416  0
         return docRouteLevel;
 417  
     }
 418  
 
 419  
     public void setDocRouteLevel(java.lang.Integer docRouteLevel) {
 420  0
         this.docRouteLevel = docRouteLevel;
 421  0
     }
 422  
 
 423  
     public java.lang.String getDocRouteStatus() {
 424  0
         return docRouteStatus;
 425  
     }
 426  
 
 427  
     public void setDocRouteStatus(java.lang.String docRouteStatus) {
 428  0
         this.docRouteStatus = docRouteStatus;
 429  0
     }
 430  
 
 431  
     public java.lang.String getDocTitle() {
 432  0
         return docTitle;
 433  
     }
 434  
 
 435  
     public void setDocTitle(java.lang.String docTitle) {
 436  0
         this.docTitle = docTitle;
 437  0
     }
 438  
 
 439  
     @Override
 440  
     public String getDocumentTypeId() {
 441  0
         return documentTypeId;
 442  
     }
 443  
 
 444  
     public void setDocumentTypeId(String documentTypeId) {
 445  0
         this.documentTypeId = documentTypeId;
 446  0
     }
 447  
 
 448  
     public java.lang.Integer getDocVersion() {
 449  0
         return docVersion;
 450  
     }
 451  
 
 452  
     public void setDocVersion(java.lang.Integer docVersion) {
 453  0
         this.docVersion = docVersion;
 454  0
     }
 455  
 
 456  
     public java.sql.Timestamp getFinalizedDate() {
 457  0
         return finalizedDate;
 458  
     }
 459  
 
 460  
     public void setFinalizedDate(java.sql.Timestamp finalizedDate) {
 461  0
         this.finalizedDate = finalizedDate;
 462  0
     }
 463  
 
 464  
     public java.lang.String getInitiatorWorkflowId() {
 465  0
         return initiatorWorkflowId;
 466  
     }
 467  
 
 468  
     public void setInitiatorWorkflowId(java.lang.String initiatorWorkflowId) {
 469  0
         this.initiatorWorkflowId = initiatorWorkflowId;
 470  0
     }
 471  
 
 472  
     public java.lang.String getRoutedByUserWorkflowId() {
 473  0
         if ( (isEnroute()) && (StringUtils.isBlank(routedByUserWorkflowId)) ) {
 474  0
             return initiatorWorkflowId;
 475  
         }
 476  0
         return routedByUserWorkflowId;
 477  
     }
 478  
 
 479  
     public void setRoutedByUserWorkflowId(java.lang.String routedByUserWorkflowId) {
 480  0
         this.routedByUserWorkflowId = routedByUserWorkflowId;
 481  0
     }
 482  
 
 483  
     @Override
 484  
     public String getDocumentId() {
 485  0
         return documentId;
 486  
     }
 487  
 
 488  
     public void setDocumentId(java.lang.String documentId) {
 489  0
         this.documentId = documentId;
 490  0
     }
 491  
 
 492  
     public java.sql.Timestamp getRouteLevelDate() {
 493  0
         return routeLevelDate;
 494  
     }
 495  
 
 496  
     public void setRouteLevelDate(java.sql.Timestamp routeLevelDate) {
 497  0
         this.routeLevelDate = routeLevelDate;
 498  0
     }
 499  
 
 500  
     public java.sql.Timestamp getRouteStatusDate() {
 501  0
         return routeStatusDate;
 502  
     }
 503  
 
 504  
     public void setRouteStatusDate(java.sql.Timestamp routeStatusDate) {
 505  0
         this.routeStatusDate = routeStatusDate;
 506  0
     }
 507  
 
 508  
     public java.sql.Timestamp getStatusModDate() {
 509  0
         return statusModDate;
 510  
     }
 511  
 
 512  
     public void setStatusModDate(java.sql.Timestamp statusModDate) {
 513  0
         this.statusModDate = statusModDate;
 514  0
     }
 515  
 
 516  
     /**
 517  
      * 
 518  
      * This method returns the Application Document Status.
 519  
      * This status is an alternative to the Route Status that may be used for a document.
 520  
      * It is configurable per document type.
 521  
      * 
 522  
      * @see ApplicationDocumentStatus
 523  
      * @see DocumentTypePolicy
 524  
      * 
 525  
      * @return
 526  
      */
 527  
     public java.lang.String getAppDocStatus() {
 528  0
             if (appDocStatus == null || "".equals(appDocStatus)){
 529  0
                     return KEWConstants.UNKNOWN_STATUS;
 530  
             }
 531  0
         return appDocStatus;
 532  
     }
 533  
 
 534  
     public void setAppDocStatus(java.lang.String appDocStatus){
 535  0
         this.appDocStatus = appDocStatus;
 536  0
     }
 537  
     
 538  
     /**
 539  
      * 
 540  
      * This method returns a combination of the route status label and the app doc status.
 541  
      * 
 542  
      * @return
 543  
      */
 544  
     public String getCombinedStatus(){
 545  0
             String routeStatus = getRouteStatusLabel();
 546  0
             String appStatus = getAppDocStatus();
 547  0
             if (routeStatus != null && routeStatus.length()>0){
 548  0
                     if (appStatus.length() > 0){
 549  0
                             routeStatus += ", "+appStatus;
 550  
                     }
 551  
             } else {
 552  0
                     return appStatus;
 553  
             }
 554  0
             return routeStatus;
 555  
     }
 556  
 
 557  
     /**
 558  
      * 
 559  
      * This method sets the appDocStatus.
 560  
      * It firsts validates the new value against the defined acceptable values, if defined.
 561  
      * It also updates the AppDocStatus date, and saves the status transition information
 562  
      * 
 563  
      * @param appDocStatus
 564  
      * @throws WorkflowRuntimeException
 565  
      */
 566  
     public void updateAppDocStatus(java.lang.String appDocStatus) throws WorkflowRuntimeException{
 567  
                //validate against allowable values if defined
 568  0
             if (appDocStatus != null && appDocStatus.length() > 0 && !appDocStatus.equalsIgnoreCase(this.appDocStatus)){                    
 569  0
                     DocumentType documentType = KEWServiceLocator.getDocumentTypeService().findById(this.getDocumentTypeId());            
 570  0
                     if (documentType.getValidApplicationStatuses() != null  && documentType.getValidApplicationStatuses().size() > 0){
 571  0
                             Iterator<ApplicationDocumentStatus> iter = documentType.getValidApplicationStatuses().iterator();
 572  0
                             boolean statusValidated = false;
 573  0
                             while (iter.hasNext())
 574  
                             {
 575  0
                                     ApplicationDocumentStatus myAppDocStat = iter.next();
 576  0
                                     if (appDocStatus.compareToIgnoreCase(myAppDocStat.getStatusName()) == 0)
 577  
                                     {
 578  0
                                             statusValidated = true;
 579  0
                                             break;
 580  
                                     }
 581  0
                             }
 582  0
                             if (!statusValidated){
 583  0
                                     WorkflowRuntimeException xpee = new WorkflowRuntimeException("AppDocStatus value " +  appDocStatus + " not allowable."); 
 584  0
                                     LOG.error("Error validating nextAppDocStatus name: " +  appDocStatus + " against acceptable values.", xpee);
 585  0
                                     throw xpee; 
 586  
                             }
 587  
                     }
 588  
 
 589  
                     // set the status value
 590  0
                     String oldStatus = this.appDocStatus;
 591  0
                     this.appDocStatus = appDocStatus;
 592  
 
 593  
                     // update the timestamp
 594  0
                     setAppDocStatusDate(new Timestamp(System.currentTimeMillis()));
 595  
 
 596  
                     // save the status transition
 597  0
                     this.appDocStatusHistory.add(new DocumentStatusTransition(documentId, oldStatus, appDocStatus));
 598  
             }
 599  
 
 600  0
     }
 601  
     
 602  
     
 603  
     public java.sql.Timestamp getAppDocStatusDate() {
 604  0
         return appDocStatusDate;
 605  
     }
 606  
 
 607  
     public void setAppDocStatusDate(java.sql.Timestamp appDocStatusDate) {
 608  0
         this.appDocStatusDate = appDocStatusDate;
 609  0
     }
 610  
 
 611  
     public Object copy(boolean preserveKeys) {
 612  0
         throw new UnsupportedOperationException("The copy method is deprecated and unimplemented!");
 613  
     }
 614  
 
 615  
     /**
 616  
      * @return True if the document is in the state of Initiated
 617  
      */
 618  
     public boolean isStateInitiated() {
 619  0
         return KEWConstants.ROUTE_HEADER_INITIATED_CD.equals(docRouteStatus);
 620  
     }
 621  
 
 622  
     /**
 623  
      * @return True if the document is in the state of Saved
 624  
      */
 625  
     public boolean isStateSaved() {
 626  0
         return KEWConstants.ROUTE_HEADER_SAVED_CD.equals(docRouteStatus);
 627  
     }
 628  
 
 629  
     /**
 630  
      * @return true if the document has ever been inte enroute state
 631  
      */
 632  
     public boolean isRouted() {
 633  0
         return !(isStateInitiated() || isStateSaved());
 634  
     }
 635  
 
 636  
     public boolean isInException() {
 637  0
         return KEWConstants.ROUTE_HEADER_EXCEPTION_CD.equals(docRouteStatus);
 638  
     }
 639  
 
 640  
     public boolean isDisaproved() {
 641  0
         return KEWConstants.ROUTE_HEADER_DISAPPROVED_CD.equals(docRouteStatus);
 642  
     }
 643  
 
 644  
     public boolean isCanceled() {
 645  0
         return KEWConstants.ROUTE_HEADER_CANCEL_CD.equals(docRouteStatus);
 646  
     }
 647  
 
 648  
     public boolean isFinal() {
 649  0
         return KEWConstants.ROUTE_HEADER_FINAL_CD.equals(docRouteStatus);
 650  
     }
 651  
 
 652  
     public boolean isEnroute() {
 653  0
             return KEWConstants.ROUTE_HEADER_ENROUTE_CD.equals(docRouteStatus);
 654  
     }
 655  
 
 656  
     /**
 657  
      * @return true if the document is in the processed state
 658  
      */
 659  
     public boolean isProcessed() {
 660  0
         return KEWConstants.ROUTE_HEADER_PROCESSED_CD.equals(docRouteStatus);
 661  
     }
 662  
 
 663  
     public boolean isRoutable() {
 664  0
         return KEWConstants.ROUTE_HEADER_ENROUTE_CD.equals(docRouteStatus) ||
 665  
                         //KEWConstants.ROUTE_HEADER_EXCEPTION_CD.equals(docRouteStatus) ||
 666  
                         KEWConstants.ROUTE_HEADER_SAVED_CD.equals(docRouteStatus) ||
 667  
                         KEWConstants.ROUTE_HEADER_PROCESSED_CD.equals(docRouteStatus);
 668  
     }
 669  
 
 670  
     /**
 671  
      * Return true if the given action code is valid for this document's current state.
 672  
      *
 673  
      * @param actionCd
 674  
      *            The action code to be tested.
 675  
      * @return True if the action code is valid for the document's status.
 676  
      */
 677  
     public boolean isValidActionToTake(String actionCd) {
 678  0
         String actions = (String) legalActions.get(docRouteStatus);
 679  0
         if (!actions.contains(actionCd)) {
 680  0
             return false;
 681  
         } else {
 682  0
             return true;
 683  
         }
 684  
     }
 685  
 
 686  
     public boolean isValidStatusChange(String newStatus) {
 687  0
             return ((String) stateTransitionMap.get(getDocRouteStatus())).contains(newStatus);
 688  
     }
 689  
 
 690  
     public void setRouteStatus(String newStatus, boolean finalState) throws InvalidActionTakenException {
 691  0
         if (newStatus != getDocRouteStatus()) {
 692  
             // only modify the status mod date if the status actually changed
 693  0
             setRouteStatusDate(new Timestamp(System.currentTimeMillis()));
 694  
         }
 695  0
         if (((String) stateTransitionMap.get(getDocRouteStatus())).contains(newStatus)) {
 696  0
             LOG.debug("changing status");
 697  0
             setDocRouteStatus(newStatus);
 698  
         } else {
 699  0
             LOG.debug("unable to change status");
 700  0
             throw new InvalidActionTakenException("Document status " + CodeTranslator.getRouteStatusLabel(getDocRouteStatus()) + " cannot transition to status " + CodeTranslator.getRouteStatusLabel(newStatus));
 701  
         }
 702  0
         setStatusModDate(new Timestamp(System.currentTimeMillis()));
 703  0
         if (finalState) {
 704  0
             LOG.debug("setting final timeStamp");
 705  0
             setFinalizedDate(new Timestamp(System.currentTimeMillis()));
 706  
         }
 707  0
     }
 708  
 
 709  
     /**
 710  
      * Mark the document as being processed.
 711  
      *
 712  
      * @throws ResourceUnavailableException
 713  
      * @throws InvalidActionTakenException
 714  
      */
 715  
     public void markDocumentProcessed() throws InvalidActionTakenException {
 716  0
         LOG.debug(this + " marked processed");
 717  0
         setRouteStatus(KEWConstants.ROUTE_HEADER_PROCESSED_CD, !FINAL_STATE);
 718  0
     }
 719  
 
 720  
     /**
 721  
      * Mark document cancled.
 722  
      *
 723  
      * @throws ResourceUnavailableException
 724  
      * @throws InvalidActionTakenException
 725  
      */
 726  
     public void markDocumentCanceled() throws InvalidActionTakenException {
 727  0
         LOG.debug(this + " marked canceled");
 728  0
         setRouteStatus(KEWConstants.ROUTE_HEADER_CANCEL_CD, FINAL_STATE);
 729  0
     }
 730  
 
 731  
     /**
 732  
      * Mark document disapproved
 733  
      *
 734  
      * @throws ResourceUnavailableException
 735  
      * @throws InvalidActionTakenException
 736  
      */
 737  
     public void markDocumentDisapproved() throws InvalidActionTakenException {
 738  0
         LOG.debug(this + " marked disapproved");
 739  0
         setRouteStatus(KEWConstants.ROUTE_HEADER_DISAPPROVED_CD, FINAL_STATE);
 740  0
     }
 741  
 
 742  
     /**
 743  
      * Mark document saved
 744  
      *
 745  
      * @throws ResourceUnavailableException
 746  
      * @throws InvalidActionTakenException
 747  
      */
 748  
     public void markDocumentSaved() throws InvalidActionTakenException {
 749  0
         LOG.debug(this + " marked saved");
 750  0
         setRouteStatus(KEWConstants.ROUTE_HEADER_SAVED_CD, !FINAL_STATE);
 751  0
     }
 752  
 
 753  
     /**
 754  
      * Mark the document as being in the exception state.
 755  
      *
 756  
      * @throws ResourceUnavailableException
 757  
      * @throws InvalidActionTakenException
 758  
      */
 759  
     public void markDocumentInException() throws InvalidActionTakenException {
 760  0
         LOG.debug(this + " marked in exception");
 761  0
         setRouteStatus(KEWConstants.ROUTE_HEADER_EXCEPTION_CD, !FINAL_STATE);
 762  0
     }
 763  
 
 764  
     /**
 765  
      * Mark the document as being actively routed.
 766  
      *
 767  
      * @throws ResourceUnavailableException
 768  
      * @throws InvalidActionTakenException
 769  
      */
 770  
     public void markDocumentEnroute() throws InvalidActionTakenException {
 771  0
         LOG.debug(this + " marked enroute");
 772  0
         setRouteStatus(KEWConstants.ROUTE_HEADER_ENROUTE_CD, !FINAL_STATE);
 773  0
     }
 774  
 
 775  
     /**
 776  
      * Mark document finalized.
 777  
      *
 778  
      * @throws ResourceUnavailableException
 779  
      * @throws InvalidActionTakenException
 780  
      */
 781  
     public void markDocumentFinalized() throws InvalidActionTakenException {
 782  0
         LOG.debug(this + " marked finalized");
 783  0
         setRouteStatus(KEWConstants.ROUTE_HEADER_FINAL_CD, FINAL_STATE);
 784  0
     }
 785  
 
 786  
     /**
 787  
      * This method takes data from a VO and sets it on this route header
 788  
      * @param routeHeaderVO
 789  
      * @throws WorkflowException
 790  
      */
 791  
     public void setRouteHeaderData(RouteHeaderDTO routeHeaderVO) throws WorkflowException {
 792  0
         if (!ObjectUtils.equals(getDocTitle(), routeHeaderVO.getDocTitle())) {
 793  0
                 KEWServiceLocator.getActionListService().updateActionItemsForTitleChange(getDocumentId(), routeHeaderVO.getDocTitle());
 794  
         }
 795  0
         setDocTitle(routeHeaderVO.getDocTitle());
 796  0
         setAppDocId(routeHeaderVO.getAppDocId());
 797  0
         setStatusModDate(new Timestamp(System.currentTimeMillis()));
 798  0
         updateAppDocStatus(routeHeaderVO.getAppDocStatus());
 799  
 
 800  
         /* set the variables from the routeHeaderVO */
 801  0
         List<KeyValue> variables = routeHeaderVO.getVariables();
 802  0
         for (KeyValue kvp : variables) {
 803  0
             setVariable(kvp.getKey(), kvp.getValue());
 804  
         }
 805  0
     }
 806  
     
 807  
     public void applyDocumentUpdate(DocumentUpdate documentUpdate) {
 808  0
             if (documentUpdate != null) {
 809  0
                     String thisDocTitle = getDocTitle() == null ? "" : getDocTitle();
 810  0
                     String updateDocTitle = documentUpdate.getTitle() == null ? "" : documentUpdate.getTitle();
 811  0
                     if (!StringUtils.equals(thisDocTitle, updateDocTitle)) {
 812  0
                     KEWServiceLocator.getActionListService().updateActionItemsForTitleChange(getDocumentId(), documentUpdate.getTitle());
 813  
             }
 814  0
             setDocTitle(updateDocTitle);
 815  0
             setAppDocId(documentUpdate.getApplicationDocumentId());
 816  0
             setStatusModDate(new Timestamp(System.currentTimeMillis()));
 817  0
             updateAppDocStatus(documentUpdate.getApplicationDocumentStatus());
 818  
             
 819  0
             Map<String, String> variables = documentUpdate.getVariables();
 820  0
             for (String variableName : variables.keySet()) {
 821  0
                 setVariable(variableName, variables.get(variableName));
 822  
             }
 823  
             }
 824  0
     }
 825  
 
 826  
     /**
 827  
      * Convenience method that returns the branch of the first (and presumably only?) initial node
 828  
      * @return the branch of the first (and presumably only?) initial node
 829  
      */
 830  
     public Branch getRootBranch() {
 831  0
         if (!this.initialRouteNodeInstances.isEmpty()) {
 832  0
             return ((RouteNodeInstance) getInitialRouteNodeInstance(0)).getBranch();
 833  
         } 
 834  0
         return null;
 835  
     }
 836  
 
 837  
     /**
 838  
      * Looks up a variable (embodied in a "BranchState" key/value pair) in the
 839  
      * branch state table.
 840  
      */
 841  
     private BranchState findVariable(String name) {
 842  0
         Branch rootBranch = getRootBranch();
 843  0
         if (rootBranch != null) {
 844  0
             List<BranchState> branchState = rootBranch.getBranchState();
 845  0
             Iterator<BranchState> it = branchState.iterator();
 846  0
             while (it.hasNext()) {
 847  0
                 BranchState state = it.next();
 848  0
                 if (ObjectUtils.equals(state.getKey(), BranchState.VARIABLE_PREFIX + name)) {
 849  0
                     return state;
 850  
                 }
 851  0
             }
 852  
         }
 853  0
         return null;
 854  
     }
 855  
 
 856  
     /**
 857  
      * Gets a variable
 858  
      * @param name variable name
 859  
      * @return variable value, or null if variable is not defined
 860  
      */
 861  
     public String getVariable(String name) {
 862  0
         BranchState state = findVariable(name);
 863  0
         if (state == null) {
 864  0
             LOG.debug("Variable not found: '" + name + "'");
 865  0
             return null;
 866  
         }
 867  0
         return state.getValue();
 868  
     }
 869  
 
 870  
     public void removeVariableThatContains(String name) {
 871  0
         List<BranchState> statesToRemove = new ArrayList<BranchState>();
 872  0
         for (BranchState state : this.getRootBranchState()) {
 873  0
             if (state.getKey().contains(name)) {
 874  0
                 statesToRemove.add(state);
 875  
             }
 876  
         }
 877  0
         this.getRootBranchState().removeAll(statesToRemove);
 878  0
     }
 879  
     
 880  
     /**
 881  
      * Sets a variable
 882  
      * @param name variable name
 883  
      * @param value variable value, or null if variable should be removed
 884  
      */
 885  
     public void setVariable(String name, String value) {
 886  0
         BranchState state = findVariable(name);
 887  0
         Branch rootBranch = getRootBranch();
 888  0
         if (rootBranch != null) {
 889  0
             List<BranchState> branchState = rootBranch.getBranchState();
 890  0
             if (state == null) {
 891  0
                 if (value == null) {
 892  0
                     LOG.debug("set non existent variable '" + name + "' to null value");
 893  0
                     return;
 894  
                 }
 895  0
                 LOG.debug("Adding branch state: '" + name + "'='" + value + "'");
 896  0
                 state = new BranchState();
 897  0
                 state.setBranch(rootBranch);
 898  0
                 state.setKey(BranchState.VARIABLE_PREFIX + name);
 899  0
                 state.setValue(value);
 900  0
                 rootBranch.addBranchState(state);
 901  
             } else {
 902  0
                 if (value == null) {
 903  0
                     LOG.debug("Removing value: " + state.getKey() + "=" + state.getValue());
 904  0
                     branchState.remove(state);
 905  
                 } else {
 906  0
                     LOG.debug("Setting value of variable '" + name + "' to '" + value + "'");
 907  0
                     state.setValue(value);
 908  
                 }
 909  
             }
 910  
         }
 911  0
     }
 912  
     
 913  
     public List<BranchState> getRootBranchState() {
 914  0
         if (this.getRootBranch() != null) {
 915  0
             return this.getRootBranch().getBranchState();
 916  
         }
 917  0
         return null;
 918  
     }
 919  
 
 920  
     public CustomActionListAttribute getCustomActionListAttribute() throws WorkflowException {
 921  0
         CustomActionListAttribute customActionListAttribute = null;
 922  0
         if (this.getDocumentType() != null) {
 923  0
                 customActionListAttribute = this.getDocumentType().getCustomActionListAttribute();
 924  0
                 if (customActionListAttribute != null) {
 925  0
                         return customActionListAttribute;
 926  
                 }
 927  
         }
 928  0
         customActionListAttribute = new DefaultCustomActionListAttribute();
 929  0
         return customActionListAttribute;
 930  
     }
 931  
 
 932  
     public CustomEmailAttribute getCustomEmailAttribute() throws WorkflowException {
 933  0
         CustomEmailAttribute customEmailAttribute = null;
 934  
         try {
 935  0
             if (this.getDocumentType() != null) {
 936  0
                 customEmailAttribute = this.getDocumentType().getCustomEmailAttribute();
 937  0
                 if (customEmailAttribute != null) {
 938  0
                     customEmailAttribute.setRouteHeaderVO(DTOConverter.convertRouteHeader(this, null));
 939  0
                     return customEmailAttribute;
 940  
                 }
 941  
             }
 942  0
         } catch (Exception e) {
 943  0
             LOG.debug("Error in retrieving custom email attribute", e);
 944  0
         }
 945  0
         customEmailAttribute = new CustomEmailAttributeImpl();
 946  0
         customEmailAttribute.setRouteHeaderVO(DTOConverter.convertRouteHeader(this, null));
 947  0
         return customEmailAttribute;
 948  
     }
 949  
 
 950  
     public CustomNoteAttribute getCustomNoteAttribute() throws WorkflowException
 951  
     {
 952  0
         CustomNoteAttribute customNoteAttribute = null;
 953  
         try {
 954  0
             if (this.getDocumentType() != null) {
 955  0
                 customNoteAttribute = this.getDocumentType().getCustomNoteAttribute();
 956  0
                 if (customNoteAttribute != null) {
 957  0
                     customNoteAttribute.setRouteHeaderVO(DTOConverter.convertRouteHeader(this, null));
 958  0
                     return customNoteAttribute;
 959  
                 }
 960  
             }
 961  0
         } catch (Exception e) {
 962  0
             LOG.debug("Error in retrieving custom note attribute", e);
 963  0
         }
 964  0
         customNoteAttribute = new CustomNoteAttributeImpl();
 965  0
         customNoteAttribute.setRouteHeaderVO(DTOConverter.convertRouteHeader(this, null));
 966  0
         return customNoteAttribute;
 967  
     }
 968  
 
 969  
     public ActionRequestValue getDocActionRequest(int index) {
 970  0
             List<ActionRequestValue> actionRequests = getActionRequests();
 971  0
         while (actionRequests.size() <= index) {
 972  0
                 ActionRequestValue actionRequest = new ActionRequestFactory(this).createBlankActionRequest();
 973  0
                 actionRequest.setNodeInstance(new RouteNodeInstance());
 974  0
             actionRequests.add(actionRequest);
 975  0
         }
 976  0
         return (ActionRequestValue) actionRequests.get(index);
 977  
     }
 978  
 
 979  
     public ActionTakenValue getDocActionTaken(int index) {
 980  0
             List<ActionTakenValue> actionsTaken = getActionsTaken();
 981  0
         while (actionsTaken.size() <= index) {
 982  0
             actionsTaken.add(new ActionTakenValue());
 983  
         }
 984  0
         return (ActionTakenValue) actionsTaken.get(index);
 985  
     }
 986  
 
 987  
     public ActionItem getDocActionItem(int index) {
 988  0
             List<ActionItem> actionItems = getActionItems();
 989  0
         while (actionItems.size() <= index) {
 990  0
             actionItems.add(new ActionItem());
 991  
         }
 992  0
         return (ActionItem) actionItems.get(index);
 993  
     }
 994  
 
 995  
     private RouteNodeInstance getInitialRouteNodeInstance(int index) {
 996  0
             if (initialRouteNodeInstances.size() >= index) {
 997  0
                 return (RouteNodeInstance) initialRouteNodeInstances.get(index);
 998  
             } 
 999  0
             return null;
 1000  
     }
 1001  
 
 1002  
 //        /**
 1003  
 //         * @param searchableAttributeValues The searchableAttributeValues to set.
 1004  
 //         */
 1005  
 //        public void setSearchableAttributeValues(List<SearchableAttributeValue> searchableAttributeValues) {
 1006  
 //                this.searchableAttributeValues = searchableAttributeValues;
 1007  
 //        }
 1008  
 //
 1009  
 //        /**
 1010  
 //         * @return Returns the searchableAttributeValues.
 1011  
 //         */
 1012  
 //        public List<SearchableAttributeValue> getSearchableAttributeValues() {
 1013  
 //                return searchableAttributeValues;
 1014  
 //        }
 1015  
 
 1016  
         public boolean isRoutingReport() {
 1017  0
                 return routingReport;
 1018  
         }
 1019  
 
 1020  
         public void setRoutingReport(boolean routingReport) {
 1021  0
                 this.routingReport = routingReport;
 1022  0
         }
 1023  
 
 1024  
     public List<RouteNodeInstance> getInitialRouteNodeInstances() {
 1025  0
         return initialRouteNodeInstances;
 1026  
     }
 1027  
 
 1028  
     public void setInitialRouteNodeInstances(List<RouteNodeInstance> initialRouteNodeInstances) {
 1029  0
         this.initialRouteNodeInstances = initialRouteNodeInstances;
 1030  0
     }
 1031  
 
 1032  
         public List<Note> getNotes() {
 1033  0
                 return notes;
 1034  
         }
 1035  
 
 1036  
         public void setNotes(List<Note> notes) {
 1037  0
                 this.notes = notes;
 1038  0
         }
 1039  
 
 1040  
         public DocumentRouteHeaderValueContent getDocumentContent() {
 1041  0
                 if (documentContent == null) {
 1042  0
                         documentContent = KEWServiceLocator.getRouteHeaderService().getContent(getDocumentId());
 1043  
                 }
 1044  0
                 return documentContent;
 1045  
         }
 1046  
 
 1047  
         public void setDocumentContent(DocumentRouteHeaderValueContent documentContent) {
 1048  0
                 this.documentContent = documentContent;
 1049  0
         }
 1050  
 
 1051  
         public List<DocumentStatusTransition> getAppDocStatusHistory() {
 1052  0
                 return this.appDocStatusHistory;
 1053  
         }
 1054  
 
 1055  
         public void setAppDocStatusHistory(
 1056  
                         List<DocumentStatusTransition> appDocStatusHistory) {
 1057  0
                 this.appDocStatusHistory = appDocStatusHistory;
 1058  0
         }
 1059  
         
 1060  
         @Override
 1061  
         public DocumentStatus getStatus() {
 1062  0
                 return DocumentStatus.fromCode(getDocRouteStatus());
 1063  
         }
 1064  
         
 1065  
         @Override
 1066  
         public DateTime getDateCreated() {
 1067  0
                 if (getCreateDate() == null) {
 1068  0
                         return null;
 1069  
                 }
 1070  0
                 return new DateTime(getCreateDate().getTime());
 1071  
         }
 1072  
         
 1073  
         @Override
 1074  
         public DateTime getDateLastModified() {
 1075  0
                 if (getStatusModDate() == null) {
 1076  0
                         return null;
 1077  
                 }
 1078  0
                 return new DateTime(getStatusModDate().getTime());
 1079  
         }
 1080  
 
 1081  
         @Override
 1082  
         public DateTime getDateApproved() {
 1083  0
                 if (getApprovedDate() == null) {
 1084  0
                         return null;
 1085  
                 }
 1086  0
                 return new DateTime(getApprovedDate().getTime());
 1087  
         }
 1088  
 
 1089  
         @Override
 1090  
         public DateTime getDateFinalized() {
 1091  0
                 if (getFinalizedDate() == null) {
 1092  0
                         return null;
 1093  
                 }
 1094  0
                 return new DateTime(getFinalizedDate().getTime());
 1095  
         }
 1096  
         
 1097  
         @Override
 1098  
         public String getTitle() {
 1099  0
                 return docTitle;
 1100  
         }
 1101  
 
 1102  
         @Override
 1103  
         public String getApplicationDocumentId() {
 1104  0
                 return appDocId;
 1105  
         }
 1106  
 
 1107  
         @Override
 1108  
         public String getInitiatorPrincipalId() {
 1109  0
                 return initiatorWorkflowId;
 1110  
         }
 1111  
 
 1112  
         @Override
 1113  
         public String getRoutedByPrincipalId() {
 1114  0
                 return routedByUserWorkflowId;
 1115  
         }
 1116  
         
 1117  
         @Override
 1118  
         public String getDocumentTypeName() {
 1119  0
                 return getDocumentType().getName();
 1120  
         }
 1121  
         
 1122  
         @Override
 1123  
         public String getDocumentHandlerUrl() {
 1124  0
                 return getDocumentType().getDocHandlerUrl();
 1125  
         }
 1126  
         
 1127  
         @Override
 1128  
         public String getApplicationDocumentStatus() {
 1129  0
                 return appDocStatus;
 1130  
         }
 1131  
         
 1132  
         @Override
 1133  
         public DateTime getApplicationDocumentStatusDate() {
 1134  0
                 if (appDocStatusDate == null) {
 1135  0
                         return null;
 1136  
                 }
 1137  0
                 return new DateTime(appDocStatusDate.getTime());
 1138  
         }
 1139  
         
 1140  
         @Override
 1141  
         public Map<String, String> getVariables() {
 1142  0
                 Map<String, String> documentVariables = new HashMap<String, String>();
 1143  
                 /* populate the routeHeaderVO with the document variables */
 1144  
         // FIXME: we assume there is only one for now
 1145  0
         Branch routeNodeInstanceBranch = getRootBranch();
 1146  
         // Ok, we are using the "branch state" as the arbitrary convenient repository for flow/process/edoc variables
 1147  
         // so we need to stuff them into the VO
 1148  0
         if (routeNodeInstanceBranch != null) {
 1149  0
             List<BranchState> listOfBranchStates = routeNodeInstanceBranch.getBranchState();
 1150  0
             for (BranchState bs : listOfBranchStates) {
 1151  0
                 if (bs.getKey() != null && bs.getKey().startsWith(BranchState.VARIABLE_PREFIX)) {
 1152  0
                     LOG.debug("Setting branch state variable on vo: " + bs.getKey() + "=" + bs.getValue());
 1153  0
                     documentVariables.put(bs.getKey().substring(BranchState.VARIABLE_PREFIX.length()), bs.getValue());
 1154  
                 }
 1155  
             }
 1156  
         }
 1157  0
         return documentVariables;
 1158  
         }
 1159  
 
 1160  
         public static Document to(DocumentRouteHeaderValue documentBo) {
 1161  0
                 if (documentBo == null) {
 1162  0
                         return null;
 1163  
                 }
 1164  0
                 Document.Builder builder = Document.Builder.create(documentBo);
 1165  0
                 return builder.build();
 1166  
         }
 1167  
         
 1168  
         public static DocumentRouteHeaderValue from(Document document) {
 1169  0
             DocumentRouteHeaderValue documentBo = new DocumentRouteHeaderValue();
 1170  0
         documentBo.setAppDocId(document.getApplicationDocumentId());
 1171  0
         if (document.getDateApproved() != null) {
 1172  0
             documentBo.setApprovedDate(new Timestamp(document.getDateApproved().getMillis()));
 1173  
         }
 1174  0
         documentBo.setCreateDate(new Timestamp(document.getDateApproved().getMillis()));
 1175  0
         if (StringUtils.isEmpty(documentBo.getDocContent())) {
 1176  0
             documentBo.setDocContent(KEWConstants.DEFAULT_DOCUMENT_CONTENT);
 1177  
         }
 1178  0
         documentBo.setDocRouteStatus(document.getStatus().getCode());
 1179  0
         documentBo.setDocTitle(document.getTitle());
 1180  0
         if (document.getDocumentTypeName() != null) {
 1181  0
             DocumentType documentType = KEWServiceLocator.getDocumentTypeService().findByName(document.getDocumentTypeName());
 1182  0
             if (documentType == null) {
 1183  0
                 throw new RiceRuntimeException("Could not locate the given document type name: " + document.getDocumentTypeName());
 1184  
             }
 1185  0
             documentBo.setDocumentTypeId(documentType.getDocumentTypeId());
 1186  
         }
 1187  0
         if (document.getDateFinalized() != null) {
 1188  0
             documentBo.setFinalizedDate(new Timestamp(document.getDateFinalized().getMillis()));
 1189  
         }
 1190  0
         documentBo.setInitiatorWorkflowId(document.getInitiatorPrincipalId());
 1191  0
         documentBo.setRoutedByUserWorkflowId(document.getRoutedByPrincipalId());
 1192  0
         documentBo.setDocumentId(document.getDocumentId());
 1193  0
         if (document.getDateLastModified() != null) {
 1194  0
             documentBo.setStatusModDate(new Timestamp(document.getDateLastModified().getMillis()));
 1195  
         }
 1196  0
         documentBo.setAppDocStatus(document.getApplicationDocumentStatus());
 1197  0
         if (document.getApplicationDocumentStatusDate() != null) {
 1198  0
             documentBo.setAppDocStatusDate(new Timestamp(document.getApplicationDocumentStatusDate().getMillis()));
 1199  
         }
 1200  
 
 1201  
         
 1202  
         // Convert the variables
 1203  0
         Map<String, String> variables = document.getVariables();
 1204  0
         if( variables != null && !variables.isEmpty()){
 1205  0
             for(String kvp : variables.keySet()){
 1206  0
                 documentBo.setVariable(kvp, variables.get(kvp));
 1207  
             }
 1208  
         }
 1209  
         
 1210  0
         return documentBo;
 1211  
         }
 1212  
         
 1213  
 }