View Javadoc
1   /**
2    * Copyright 2005-2014 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.impl.document;
17  
18  import java.io.Serializable;
19  import java.util.Arrays;
20  import java.util.Collections;
21  import java.util.Date;
22  import java.util.HashSet;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.Set;
26  
27  import org.apache.commons.lang.StringUtils;
28  import org.joda.time.DateTime;
29  import org.kuali.rice.core.api.uif.RemotableAttributeErrorContract;
30  import org.kuali.rice.kew.api.KewApiConstants;
31  import org.kuali.rice.kew.api.KewApiServiceLocator;
32  import org.kuali.rice.kew.api.action.ActionRequest;
33  import org.kuali.rice.kew.api.action.ActionRequestType;
34  import org.kuali.rice.kew.api.action.ActionTaken;
35  import org.kuali.rice.kew.api.action.ActionType;
36  import org.kuali.rice.kew.api.action.AdHocRevoke;
37  import org.kuali.rice.kew.api.action.AdHocToGroup;
38  import org.kuali.rice.kew.api.action.AdHocToPrincipal;
39  import org.kuali.rice.kew.api.action.DocumentActionParameters;
40  import org.kuali.rice.kew.api.action.DocumentActionResult;
41  import org.kuali.rice.kew.api.action.MovePoint;
42  import org.kuali.rice.kew.api.action.RequestedActions;
43  import org.kuali.rice.kew.api.action.ReturnPoint;
44  import org.kuali.rice.kew.api.action.ValidActions;
45  import org.kuali.rice.kew.api.action.WorkflowDocumentActionsService;
46  import org.kuali.rice.kew.api.document.Document;
47  import org.kuali.rice.kew.api.document.DocumentContent;
48  import org.kuali.rice.kew.api.document.DocumentContentUpdate;
49  import org.kuali.rice.kew.api.document.DocumentDetail;
50  import org.kuali.rice.kew.api.document.DocumentStatus;
51  import org.kuali.rice.kew.api.document.DocumentUpdate;
52  import org.kuali.rice.kew.api.document.attribute.WorkflowAttributeDefinition;
53  import org.kuali.rice.kew.api.document.node.RouteNodeInstance;
54  import org.kuali.rice.kew.api.document.WorkflowDocumentService;
55  
56  /**
57   * The implementation of {@link org.kuali.rice.kew.api.WorkflowDocument}.  Implements {@link WorkflowDocumentPrototype} to expose
58   * and initialization method used for construction.
59   * <p>NOTE: operations against document data on this are only "flushed" when an action is performed.</p>
60   * <p><b>This class is *not* thread safe.</b></p>
61   * @see org.kuali.rice.kew.api.WorkflowDocument
62   */
63  public class WorkflowDocumentImpl implements Serializable, WorkflowDocumentPrototype {
64  
65      private static final long serialVersionUID = -3672966990721719088L;
66  
67      /**
68       * The principal id under which all document actions will be performed.
69       */
70      private String principalId;
71      /**
72       * Stores local changes that need to be committed.
73       */
74      private ModifiableDocument modifiableDocument;
75      /**
76       * Stores local changes that need to be committed.
77       */
78      private ModifiableDocumentContent modifiableDocumentContent;
79      /**
80       * Local cache of valid document actions.
81       * @see #getValidActions()  
82       */
83      private ValidActions validActions;
84      /**
85       * Local cache of requested document actions.
86       * @see #getRequestedActions()
87       */
88      private RequestedActions requestedActions;
89      /**
90       * Flag that indicates whether the document has been deleted; if so the object is thereafter in an illegal state.
91       */
92      private boolean documentDeleted = false;
93  
94      private transient WorkflowDocumentActionsService workflowDocumentActionsService;
95      private transient WorkflowDocumentService workflowDocumentService;
96  
97      public void init(String principalId, Document document) {
98          if (StringUtils.isBlank("principalId")) {
99              throw new IllegalArgumentException("principalId was null or blank");
100         }
101         if (document == null) {
102             throw new IllegalArgumentException("document was null");
103         }
104         this.principalId = principalId;
105         this.modifiableDocument = new ModifiableDocument(document);
106         this.modifiableDocumentContent = null;
107         this.validActions = null;
108         this.requestedActions = null;
109     }
110 
111     public WorkflowDocumentActionsService getWorkflowDocumentActionsService() {
112         if (workflowDocumentActionsService == null) {
113             workflowDocumentActionsService = KewApiServiceLocator.getWorkflowDocumentActionsService();
114         }
115         return workflowDocumentActionsService;
116     }
117 
118     public void setWorkflowDocumentActionsService(WorkflowDocumentActionsService workflowDocumentActionsService) {
119         this.workflowDocumentActionsService = workflowDocumentActionsService;
120     }
121 
122     public WorkflowDocumentService getWorkflowDocumentService() {
123         if (workflowDocumentService == null) {
124             workflowDocumentService = KewApiServiceLocator.getWorkflowDocumentService();
125         }
126         return workflowDocumentService;
127     }
128 
129     public void setWorkflowDocumentService(WorkflowDocumentService workflowDocumentService) {
130         this.workflowDocumentService = workflowDocumentService;
131     }
132 
133     protected ModifiableDocument getModifiableDocument() {
134         return modifiableDocument;
135     }
136 
137     protected ModifiableDocumentContent getModifiableDocumentContent() {
138         if (this.modifiableDocumentContent == null) {
139             DocumentContent documentContent = getWorkflowDocumentService().getDocumentContent(getDocumentId());
140             if (documentContent == null) {
141                 throw new IllegalStateException("Failed to load document content for documentId: " + getDocumentId());
142             }
143             this.modifiableDocumentContent = new ModifiableDocumentContent(documentContent);
144         }
145         return this.modifiableDocumentContent;
146     }
147 
148     @Override
149     public String getDocumentId() {
150         if (documentDeleted) {
151             throw new IllegalStateException("Document has been deleted.");
152         }
153         return getModifiableDocument().getDocumentId();
154     }
155 
156     @Override
157     public Document getDocument() {
158         return getModifiableDocument().getDocument();
159     }
160 
161     @Override
162     public DocumentContent getDocumentContent() {
163         return getModifiableDocumentContent().getDocumentContent();
164     }
165 
166     @Override
167     public String getApplicationContent() {
168         return getDocumentContent().getApplicationContent();
169     }
170 
171     @Override
172     public void setApplicationContent(String applicationContent) {
173         getModifiableDocumentContent().setApplicationContent(applicationContent);
174     }
175     
176     @Override
177     public void setAttributeContent(String attributeContent) {
178         getModifiableDocumentContent().setAttributeContent(attributeContent);
179     }
180 
181     @Override
182     public void clearAttributeContent() {
183         getModifiableDocumentContent().setAttributeContent("");
184     }
185 
186     @Override
187     public String getAttributeContent() {
188         return getDocumentContent().getAttributeContent();
189     }
190 
191     @Override
192     public void addAttributeDefinition(WorkflowAttributeDefinition attributeDefinition) {
193         getModifiableDocumentContent().addAttributeDefinition(attributeDefinition);
194     }
195 
196     @Override
197     public void removeAttributeDefinition(WorkflowAttributeDefinition attributeDefinition) {
198         getModifiableDocumentContent().removeAttributeDefinition(attributeDefinition);
199     }
200 
201     @Override
202     public void clearAttributeDefinitions() {
203         getAttributeDefinitions().clear();
204     }
205 
206     @Override
207     public List<WorkflowAttributeDefinition> getAttributeDefinitions() {
208         return getModifiableDocumentContent().getAttributeDefinitions();
209     }
210 
211     @Override
212     public void setSearchableContent(String searchableContent) {
213         getModifiableDocumentContent().setSearchableContent(searchableContent);
214     }
215     
216     @Override
217     public void addSearchableDefinition(WorkflowAttributeDefinition searchableDefinition) {
218         getModifiableDocumentContent().addSearchableDefinition(searchableDefinition);
219     }
220 
221     @Override
222     public void removeSearchableDefinition(WorkflowAttributeDefinition searchableDefinition) {
223         getModifiableDocumentContent().removeSearchableDefinition(searchableDefinition);
224     }
225 
226     @Override
227     public void clearSearchableDefinitions() {
228         getSearchableDefinitions().clear();
229     }
230 
231     @Override
232     public void clearSearchableContent() {
233         getModifiableDocumentContent().setSearchableContent("");
234     }
235 
236     @Override
237     public List<WorkflowAttributeDefinition> getSearchableDefinitions() {
238         return getModifiableDocumentContent().getSearchableDefinitions();
239     }
240 
241     @Override
242     public List<? extends RemotableAttributeErrorContract> validateAttributeDefinition(
243             WorkflowAttributeDefinition attributeDefinition) {
244         return getWorkflowDocumentActionsService().validateWorkflowAttributeDefinition(attributeDefinition);
245     }
246 
247     @Override
248     public List<ActionRequest> getRootActionRequests() {
249         return getWorkflowDocumentService().getRootActionRequests(getDocumentId());
250     }
251 
252     @Override
253     public List<ActionTaken> getActionsTaken() {
254         return getWorkflowDocumentService().getActionsTaken(getDocumentId());
255     }
256 
257     @Override
258     public void setApplicationDocumentId(String applicationDocumentId) {
259         getModifiableDocument().setApplicationDocumentId(applicationDocumentId);
260     }
261 
262     @Override
263     public String getApplicationDocumentId() {
264         return getModifiableDocument().getApplicationDocumentId();
265     }
266 
267     @Override
268     public DateTime getDateCreated() {
269         return getModifiableDocument().getDateCreated();
270     }
271 
272     @Override
273     public String getTitle() {
274         return getModifiableDocument().getTitle();
275     }
276 
277     @Override
278     public ValidActions getValidActions() {
279         if (validActions == null) {
280             validActions = getWorkflowDocumentActionsService().determineValidActions(getDocumentId(), getPrincipalId());
281         }
282         return validActions;
283     }
284 
285     @Override
286     public RequestedActions getRequestedActions() {
287         if (requestedActions == null) {
288             requestedActions = getWorkflowDocumentActionsService().determineRequestedActions(getDocumentId(),
289                     getPrincipalId());
290         }
291         return requestedActions;
292     }
293 
294     protected DocumentUpdate getDocumentUpdateIfDirty() {
295         if (getModifiableDocument().isDirty()) {
296             return getModifiableDocument().build();
297         }
298         return null;
299     }
300 
301     protected DocumentContentUpdate getDocumentContentUpdateIfDirty() {
302         if (getModifiableDocumentContent().isDirty()) {
303             return getModifiableDocumentContent().build();
304         }
305         return null;
306     }
307 
308     protected void resetStateAfterAction(DocumentActionResult response) {
309         this.modifiableDocument = new ModifiableDocument(response.getDocument());
310         this.validActions = null;
311         if (response.getValidActions() != null) {
312             this.validActions = response.getValidActions();
313         }
314         this.requestedActions = null;
315         if (response.getRequestedActions() != null) {
316             this.requestedActions = response.getRequestedActions();
317         }
318         // regardless of whether modifiable document content is dirty, we null it out so it will be re-fetched next time it's needed
319         this.modifiableDocumentContent = null;
320     }
321 
322     @Override
323     public void saveDocument(String annotation) {
324         DocumentActionResult result = getWorkflowDocumentActionsService().save(
325                 constructDocumentActionParameters(annotation));
326         resetStateAfterAction(result);
327     }
328 
329     @Override
330     public void route(String annotation) {
331         DocumentActionResult result = getWorkflowDocumentActionsService().route(
332                 constructDocumentActionParameters(annotation));
333         resetStateAfterAction(result);
334     }
335 
336     @Override
337     public void disapprove(String annotation) {
338         DocumentActionResult result = getWorkflowDocumentActionsService().disapprove(
339                 constructDocumentActionParameters(annotation));
340         resetStateAfterAction(result);
341     }
342 
343     @Override
344     public void approve(String annotation) {
345         DocumentActionResult result = getWorkflowDocumentActionsService().approve(
346                 constructDocumentActionParameters(annotation));
347         resetStateAfterAction(result);
348     }
349 
350     @Override
351     public void cancel(String annotation) {
352         DocumentActionResult result = getWorkflowDocumentActionsService().cancel(
353                 constructDocumentActionParameters(annotation));
354         resetStateAfterAction(result);
355     }
356 
357     @Override
358     public void recall(String annotation, boolean cancel) {
359         DocumentActionResult result = getWorkflowDocumentActionsService().recall(
360                 constructDocumentActionParameters(annotation), cancel);
361         resetStateAfterAction(result);
362     }
363 
364     @Override
365     public void blanketApprove(String annotation) {
366         DocumentActionResult result = getWorkflowDocumentActionsService().blanketApprove(
367                 constructDocumentActionParameters(annotation));
368         resetStateAfterAction(result);
369     }
370 
371     @Override
372     public void blanketApprove(String annotation, String... nodeNames) {
373         if (nodeNames == null) {
374             throw new IllegalArgumentException("nodeNames was null");
375         }
376         Set<String> nodeNamesSet = new HashSet<String>(Arrays.asList(nodeNames));
377         DocumentActionResult result = getWorkflowDocumentActionsService().blanketApproveToNodes(
378                 constructDocumentActionParameters(annotation), nodeNamesSet);
379         resetStateAfterAction(result);
380     }
381 
382     @Override
383     public void saveDocumentData() {
384         DocumentActionResult result = getWorkflowDocumentActionsService().saveDocumentData(
385                 constructDocumentActionParameters(null));
386         resetStateAfterAction(result);
387     }
388 
389     @Override
390     public void setApplicationDocumentStatus(String applicationDocumentStatus) {
391         getModifiableDocument().setApplicationDocumentStatus(applicationDocumentStatus);
392     }
393 
394     @Override
395     public void acknowledge(String annotation) {
396         DocumentActionResult result = getWorkflowDocumentActionsService().acknowledge(
397                 constructDocumentActionParameters(annotation));
398         resetStateAfterAction(result);
399     }
400 
401     @Override
402     public void fyi(String annotation) {
403         DocumentActionResult result = getWorkflowDocumentActionsService().clearFyi(
404                 constructDocumentActionParameters(annotation));
405         resetStateAfterAction(result);
406     }
407 
408     @Override
409     public void fyi() {
410         fyi("");
411     }
412 
413     @Override
414     public void delete() {
415         getWorkflowDocumentActionsService().delete(getDocumentId(), principalId);
416         documentDeleted = true;
417     }
418 
419     @Override
420     public void refresh() {
421         Document document = getWorkflowDocumentService().getDocument(getDocumentId());
422         this.modifiableDocument = new ModifiableDocument(document);
423         this.validActions = null;
424         this.requestedActions = null;
425         this.modifiableDocumentContent = null;
426     }
427 
428     @Override
429     public void adHocToPrincipal(ActionRequestType actionRequested, String annotation, String targetPrincipalId,
430             String responsibilityDescription, boolean forceAction) {
431         adHocToPrincipal(actionRequested, null, annotation, targetPrincipalId, responsibilityDescription, forceAction);
432     }
433 
434     @Override
435     public void adHocToPrincipal(ActionRequestType actionRequested, String nodeName, String annotation,
436             String targetPrincipalId, String responsibilityDescription, boolean forceAction) {
437         adHocToPrincipal(actionRequested, nodeName, annotation, targetPrincipalId, responsibilityDescription,
438                 forceAction, null);
439     }
440 
441     @Override
442     public void adHocToPrincipal(ActionRequestType actionRequested, String nodeName, String annotation,
443             String targetPrincipalId, String responsibilityDescription, boolean forceAction, String requestLabel) {
444         AdHocToPrincipal.Builder builder = AdHocToPrincipal.Builder
445                 .create(actionRequested, nodeName, targetPrincipalId);
446         builder.setResponsibilityDescription(responsibilityDescription);
447         builder.setForceAction(forceAction);
448         builder.setRequestLabel(requestLabel);
449         DocumentActionResult result = getWorkflowDocumentActionsService().adHocToPrincipal(
450                 constructDocumentActionParameters(annotation), builder.build());
451         resetStateAfterAction(result);
452     }
453 
454     @Override
455     public void adHocToPrincipal(AdHocToPrincipal adHocToPrincipal, String annotation) {
456         DocumentActionResult result = getWorkflowDocumentActionsService().adHocToPrincipal(
457                 constructDocumentActionParameters(annotation), adHocToPrincipal);
458         resetStateAfterAction(result);
459     }
460 
461     @Override
462     public void adHocToGroup(ActionRequestType actionRequested, String annotation, String targetGroupId,
463             String responsibilityDescription, boolean forceAction) {
464         adHocToGroup(actionRequested, null, annotation, targetGroupId, responsibilityDescription, forceAction);
465     }
466 
467     @Override
468     public void adHocToGroup(ActionRequestType actionRequested, String nodeName, String annotation,
469             String targetGroupId, String responsibilityDescription, boolean forceAction) {
470         adHocToGroup(actionRequested, nodeName, annotation, targetGroupId, responsibilityDescription, forceAction, null);
471     }
472 
473     @Override
474     public void adHocToGroup(ActionRequestType actionRequested, String nodeName, String annotation,
475             String targetGroupId, String responsibilityDescription, boolean forceAction, String requestLabel) {
476         AdHocToGroup.Builder builder = AdHocToGroup.Builder.create(actionRequested, nodeName, targetGroupId);
477         builder.setResponsibilityDescription(responsibilityDescription);
478         builder.setForceAction(forceAction);
479         builder.setRequestLabel(requestLabel);
480         DocumentActionResult result = getWorkflowDocumentActionsService().adHocToGroup(
481                 constructDocumentActionParameters(annotation), builder.build());
482         resetStateAfterAction(result);
483     }
484 
485     @Override
486     public void adHocToGroup(AdHocToGroup adHocToGroup, String annotation) {
487         DocumentActionResult result = getWorkflowDocumentActionsService().adHocToGroup(
488                 constructDocumentActionParameters(annotation), adHocToGroup);
489         resetStateAfterAction(result);
490     }
491 
492     @Override
493     public void revokeAdHocRequestById(String actionRequestId, String annotation) {
494         if (StringUtils.isBlank(actionRequestId)) {
495             throw new IllegalArgumentException("actionRequestId was null or blank");
496         }
497         DocumentActionResult result = getWorkflowDocumentActionsService().revokeAdHocRequestById(
498                 constructDocumentActionParameters(annotation), actionRequestId);
499         resetStateAfterAction(result);
500     }
501 
502     @Override
503     public void revokeAdHocRequests(AdHocRevoke revoke, String annotation) {
504         if (revoke == null) {
505             throw new IllegalArgumentException("revokeFromPrincipal was null");
506         }
507         DocumentActionResult result = getWorkflowDocumentActionsService().revokeAdHocRequests(
508                 constructDocumentActionParameters(annotation), revoke);
509         resetStateAfterAction(result);
510     }
511 
512     @Override
513     public void revokeAllAdHocRequests(String annotation) {
514         DocumentActionResult result = getWorkflowDocumentActionsService().revokeAllAdHocRequests(
515                 constructDocumentActionParameters(annotation));
516         resetStateAfterAction(result);
517     }
518 
519     @Override
520     public void setTitle(String title) {
521         getModifiableDocument().setTitle(title);
522     }
523 
524     @Override
525     public String getDocumentTypeName() {
526         return getDocument().getDocumentTypeName();
527     }
528 
529     @Override
530     public boolean isCompletionRequested() {
531         return getRequestedActions().isCompleteRequested();
532     }
533 
534     @Override
535     public boolean isApprovalRequested() {
536         return getRequestedActions().isApproveRequested();
537     }
538 
539     @Override
540     public boolean isAcknowledgeRequested() {
541         return getRequestedActions().isAcknowledgeRequested();
542     }
543 
544     @Override
545     public boolean isFYIRequested() {
546         return getRequestedActions().isFyiRequested();
547     }
548 
549     @Override
550     public boolean isBlanketApproveCapable() {
551         return isValidAction(ActionType.BLANKET_APPROVE)
552                 && (isCompletionRequested() || isApprovalRequested() || isInitiated());
553     }
554 
555     @Override
556     public boolean isRouteCapable() {
557         return isValidAction(ActionType.ROUTE);
558     }
559 
560     @Override
561     public boolean isValidAction(ActionType actionType) {
562         if (actionType == null) {
563             throw new IllegalArgumentException("actionType was null");
564         }
565         return getValidActions().getValidActions().contains(actionType);
566     }
567 
568     @Override
569     public void superUserBlanketApprove(String annotation) {
570         DocumentActionResult result = getWorkflowDocumentActionsService().superUserBlanketApprove(
571                 constructDocumentActionParameters(annotation), true);
572         resetStateAfterAction(result);
573     }
574 
575     @Override
576     public void superUserNodeApprove(String nodeName, String annotation) {
577         DocumentActionResult result = getWorkflowDocumentActionsService().superUserNodeApprove(
578                 constructDocumentActionParameters(annotation), true, nodeName);
579         resetStateAfterAction(result);
580     }
581 
582     @Override
583     public void superUserTakeRequestedAction(String actionRequestId, String annotation) {
584         DocumentActionResult result = getWorkflowDocumentActionsService().superUserTakeRequestedAction(
585                 constructDocumentActionParameters(annotation), true, actionRequestId);
586         resetStateAfterAction(result);
587     }
588 
589     @Override
590     public void superUserDisapprove(String annotation) {
591         DocumentActionResult result = getWorkflowDocumentActionsService().superUserDisapprove(
592                 constructDocumentActionParameters(annotation), true);
593         resetStateAfterAction(result);
594     }
595 
596     @Override
597     public void superUserCancel(String annotation) {
598         DocumentActionResult result = getWorkflowDocumentActionsService().superUserCancel(
599                 constructDocumentActionParameters(annotation), true);
600         resetStateAfterAction(result);
601     }
602 
603     @Override
604     public void superUserReturnToPreviousNode(ReturnPoint returnPoint, String annotation) {
605         DocumentActionResult result = getWorkflowDocumentActionsService().superUserReturnToPreviousNode(
606                 constructDocumentActionParameters(annotation), true, returnPoint);
607         resetStateAfterAction(result);
608     }
609 
610     @Override
611     public void complete(String annotation) {
612         DocumentActionResult result = getWorkflowDocumentActionsService().complete(
613                 constructDocumentActionParameters(annotation));
614         resetStateAfterAction(result);
615     }
616 
617     @Override
618     public void logAnnotation(String annotation) {
619         getWorkflowDocumentActionsService().logAnnotation(getDocumentId(), principalId, annotation);
620     }
621 
622     @Override
623     public DocumentStatus getStatus() {
624         return getDocument().getStatus();
625     }
626 
627     @Override
628     public boolean checkStatus(DocumentStatus status) {
629         if (status == null) {
630             throw new IllegalArgumentException("status was null");
631         }
632         return status == getStatus();
633     }
634 
635     /**
636      * Indicates if the document is in the initiated state or not.
637      * 
638      * @return true if in the specified state
639      */
640     @Override
641     public boolean isInitiated() {
642         return checkStatus(DocumentStatus.INITIATED);
643     }
644 
645     /**
646      * Indicates if the document is in the saved state or not.
647      * 
648      * @return true if in the specified state
649      */
650     @Override
651     public boolean isSaved() {
652         return checkStatus(DocumentStatus.SAVED);
653     }
654 
655     /**
656      * Indicates if the document is in the enroute state or not.
657      * 
658      * @return true if in the specified state
659      */
660     @Override
661     public boolean isEnroute() {
662         return checkStatus(DocumentStatus.ENROUTE);
663     }
664 
665     /**
666      * Indicates if the document is in the exception state or not.
667      * 
668      * @return true if in the specified state
669      */
670     @Override
671     public boolean isException() {
672         return checkStatus(DocumentStatus.EXCEPTION);
673     }
674 
675     /**
676      * Indicates if the document is in the canceled state or not.
677      * 
678      * @return true if in the specified state
679      */
680     @Override
681     public boolean isCanceled() {
682         return checkStatus(DocumentStatus.CANCELED);
683     }
684 
685     /**
686      * Indicates if the document is in the recalled state or not.
687      *
688      * @return true if in the specified state
689      */
690     @Override
691     public boolean isRecalled() {
692         return checkStatus(DocumentStatus.RECALLED);
693     }
694 
695     /**
696      * Indicates if the document is in the disapproved state or not.
697      * 
698      * @return true if in the specified state
699      */
700     @Override
701     public boolean isDisapproved() {
702         return checkStatus(DocumentStatus.DISAPPROVED);
703     }
704 
705     /**
706      * Indicates if the document is in the Processed or Finalized state.
707      * 
708      * @return true if in the specified state
709      */
710     @Override
711     public boolean isApproved() {
712         return isProcessed() || isFinal();
713     }
714 
715     /**
716      * Indicates if the document is in the processed state or not.
717      * 
718      * @return true if in the specified state
719      */
720     @Override
721     public boolean isProcessed() {
722         return checkStatus(DocumentStatus.PROCESSED);
723     }
724 
725     /**
726      * Indicates if the document is in the final state or not.
727      * 
728      * @return true if in the specified state
729      */
730     @Override
731     public boolean isFinal() {
732         return checkStatus(DocumentStatus.FINAL);
733     }
734 
735     /**
736      * Returns the principalId with which this WorkflowDocument was constructed
737      * 
738      * @return the principalId with which this WorkflowDocument was constructed
739      */
740     @Override
741     public String getPrincipalId() {
742         return principalId;
743     }
744 
745     @Override
746     public void switchPrincipal(String principalId) {
747         if (StringUtils.isBlank(this.principalId)) {
748             throw new IllegalArgumentException("principalId was null or blank");
749         }
750         this.principalId = principalId;
751         this.validActions = null;
752         this.requestedActions = null;
753     }
754 
755     @Override
756     public void takeGroupAuthority(String annotation, String groupId) {
757         DocumentActionResult result = getWorkflowDocumentActionsService().takeGroupAuthority(
758                 constructDocumentActionParameters(annotation), groupId);
759         resetStateAfterAction(result);
760     }
761 
762     @Override
763     public void releaseGroupAuthority(String annotation, String groupId) {
764         DocumentActionResult result = getWorkflowDocumentActionsService().releaseGroupAuthority(
765                 constructDocumentActionParameters(annotation), groupId);
766         resetStateAfterAction(result);
767     }
768 
769     @Override
770     public Set<String> getNodeNames() {
771     	final List<String> names = getWorkflowDocumentService().getActiveRouteNodeNames(getDocumentId());
772         return Collections.unmodifiableSet(new HashSet<String>(names));
773     }
774 
775     public Set<String> getCurrentNodeNames() {
776     	final List<String> names = getWorkflowDocumentService().getCurrentRouteNodeNames(getDocumentId());
777         return Collections.unmodifiableSet(new HashSet<String>(names));
778     }
779 
780     @Override
781     public void returnToPreviousNode(String annotation, String nodeName) {
782         if (nodeName == null) {
783             throw new IllegalArgumentException("nodeName was null");
784         }
785         returnToPreviousNode(annotation, ReturnPoint.create(nodeName));
786     }
787 
788     @Override
789     public void returnToPreviousNode(String annotation, ReturnPoint returnPoint) {
790         if (returnPoint == null) {
791             throw new IllegalArgumentException("returnPoint was null");
792         }
793         DocumentActionResult result = getWorkflowDocumentActionsService().returnToPreviousNode(
794                 constructDocumentActionParameters(annotation), returnPoint);
795         resetStateAfterAction(result);
796     }
797 
798     @Override
799     public void move(MovePoint movePoint, String annotation) {
800         if (movePoint == null) {
801             throw new IllegalArgumentException("movePoint was null");
802         }
803         DocumentActionResult result = getWorkflowDocumentActionsService().move(
804                 constructDocumentActionParameters(annotation), movePoint);
805         resetStateAfterAction(result);
806     }
807 
808     @Override
809     public List<RouteNodeInstance> getActiveRouteNodeInstances() {
810         return getWorkflowDocumentService().getActiveRouteNodeInstances(getDocumentId());
811     }
812 
813     @Override
814     public List<RouteNodeInstance> getCurrentRouteNodeInstances() {
815         return getWorkflowDocumentService().getCurrentRouteNodeInstances(getDocumentId());
816     }
817 
818     @Override
819     public List<RouteNodeInstance> getRouteNodeInstances() {
820         return getWorkflowDocumentService().getRouteNodeInstances(getDocumentId());
821     }
822 
823     @Override
824     public List<String> getPreviousNodeNames() {
825         return getWorkflowDocumentService().getPreviousRouteNodeNames(getDocumentId());
826     }
827 
828     @Override
829     public DocumentDetail getDocumentDetail() {
830         return getWorkflowDocumentService().getDocumentDetail(getDocumentId());
831     }
832 
833     @Override
834     public void updateDocumentContent(DocumentContentUpdate documentContentUpdate) {
835         if (documentContentUpdate == null) {
836             throw new IllegalArgumentException("documentContentUpdate was null.");
837         }
838         getModifiableDocumentContent().setDocumentContentUpdate(documentContentUpdate);
839     }
840 
841     @Override
842     public void placeInExceptionRouting(String annotation) {
843         DocumentActionResult result = getWorkflowDocumentActionsService().placeInExceptionRouting(
844                 constructDocumentActionParameters(annotation));
845         resetStateAfterAction(result);
846     }
847 
848     @Override
849     public void setVariable(String name, String value) {
850         getModifiableDocument().setVariable(name, value);
851     }
852 
853     @Override
854     public String getVariableValue(String name) {
855         return getModifiableDocument().getVariableValue(name);
856     }
857 
858     @Override
859     public void setReceiveFutureRequests() {
860         setVariable(getFutureRequestsKey(principalId), getReceiveFutureRequestsValue());
861     }
862 
863     @Override
864     public void setDoNotReceiveFutureRequests() {
865         this.setVariable(getFutureRequestsKey(principalId), getDoNotReceiveFutureRequestsValue());
866     }
867 
868     @Override
869     public void setClearFutureRequests() {
870         this.setVariable(getFutureRequestsKey(principalId), getClearFutureRequestsValue());
871     }
872 
873     protected String getFutureRequestsKey(String principalId) {
874         return KewApiConstants.RECEIVE_FUTURE_REQUESTS_BRANCH_STATE_KEY + "," + principalId + ","
875                 + new Date().toString() + ", " + Math.random();
876     }
877 
878     @Override
879     public String getReceiveFutureRequestsValue() {
880         return KewApiConstants.RECEIVE_FUTURE_REQUESTS_BRANCH_STATE_VALUE;
881     }
882 
883     @Override
884     public String getDoNotReceiveFutureRequestsValue() {
885         return KewApiConstants.DONT_RECEIVE_FUTURE_REQUESTS_BRANCH_STATE_VALUE;
886     }
887 
888     @Override
889     public String getClearFutureRequestsValue() {
890         return KewApiConstants.CLEAR_FUTURE_REQUESTS_BRANCH_STATE_VALUE;
891     }
892 
893     protected DocumentActionParameters constructDocumentActionParameters(String annotation) {
894         DocumentActionParameters.Builder builder = DocumentActionParameters.Builder.create(getDocumentId(),
895                 getPrincipalId());
896         builder.setAnnotation(annotation);
897         builder.setDocumentUpdate(getDocumentUpdateIfDirty());
898         builder.setDocumentContentUpdate(getDocumentContentUpdateIfDirty());
899         return builder.build();
900     }
901     
902     @Override
903     public DateTime getDateLastModified() {
904         return getDocument().getDateLastModified();
905     }
906 
907     @Override
908     public DateTime getDateApproved() {
909         return getDocument().getDateApproved();
910     }
911 
912     @Override
913     public DateTime getDateFinalized() {
914         return getDocument().getDateFinalized();
915     }
916 
917     @Override
918     public String getInitiatorPrincipalId() {
919         return getDocument().getInitiatorPrincipalId();
920     }
921 
922     @Override
923     public String getRoutedByPrincipalId() {
924         return getDocument().getRoutedByPrincipalId();
925     }
926 
927     @Override
928     public String getDocumentTypeId() {
929         return getDocument().getDocumentTypeId();
930     }
931 
932     @Override
933     public String getDocumentHandlerUrl() {
934         return getDocument().getDocumentHandlerUrl();
935     }
936 
937     @Override
938     public String getApplicationDocumentStatus() {
939         return getDocument().getApplicationDocumentStatus();
940     }
941 
942     @Override
943     public DateTime getApplicationDocumentStatusDate() {
944         return getDocument().getApplicationDocumentStatusDate();
945     }
946 
947     @Override
948     public Map<String, String> getVariables() {
949         return getDocument().getVariables();
950     }
951 
952     /**
953      * A wrapper around DocumentContent which keeps track of local changes and generates
954      * a new updated DocumentContent as necessary.
955      */
956     protected static class ModifiableDocumentContent implements Serializable {
957 
958         private static final long serialVersionUID = -4458431160327214042L;
959 
960         private boolean dirty;
961         private DocumentContent originalDocumentContent;
962         private DocumentContentUpdate.Builder builder;
963 
964         protected ModifiableDocumentContent(DocumentContent documentContent) {
965             this.dirty = false;
966             this.originalDocumentContent = documentContent;
967             this.builder = DocumentContentUpdate.Builder.create(documentContent);
968         }
969 
970         protected DocumentContent getDocumentContent() {
971             if (!dirty) {
972                 return originalDocumentContent;
973             }
974             DocumentContent.Builder documentContentBuilder = DocumentContent.Builder.create(originalDocumentContent);
975             documentContentBuilder.setApplicationContent(builder.getApplicationContent());
976             documentContentBuilder.setAttributeContent(builder.getAttributeContent());
977             documentContentBuilder.setSearchableContent(builder.getSearchableContent());
978             return documentContentBuilder.build();
979         }
980 
981         protected DocumentContentUpdate build() {
982             return builder.build();
983         }
984 
985         protected void setDocumentContentUpdate(DocumentContentUpdate update) {
986             this.builder = DocumentContentUpdate.Builder.create(update);
987             this.dirty = true;
988         }
989 
990         protected void addAttributeDefinition(WorkflowAttributeDefinition definition) {
991             builder.getAttributeDefinitions().add(definition);
992             dirty = true;
993         }
994 
995         protected void removeAttributeDefinition(WorkflowAttributeDefinition definition) {
996             builder.getAttributeDefinitions().remove(definition);
997             dirty = true;
998         }
999 
1000         protected List<WorkflowAttributeDefinition> getAttributeDefinitions() {
1001             return builder.getAttributeDefinitions();
1002         }
1003 
1004         protected void addSearchableDefinition(WorkflowAttributeDefinition definition) {
1005             builder.getSearchableDefinitions().add(definition);
1006             dirty = true;
1007         }
1008 
1009         protected void removeSearchableDefinition(WorkflowAttributeDefinition definition) {
1010             builder.getSearchableDefinitions().remove(definition);
1011             dirty = true;
1012         }
1013 
1014         protected List<WorkflowAttributeDefinition> getSearchableDefinitions() {
1015             return builder.getAttributeDefinitions();
1016         }
1017 
1018         protected void setApplicationContent(String applicationContent) {
1019             builder.setApplicationContent(applicationContent);
1020             dirty = true;
1021         }
1022 
1023         protected void setAttributeContent(String attributeContent) {
1024             builder.setAttributeContent(attributeContent);
1025             dirty = true;
1026         }
1027 
1028         public void setAttributeDefinitions(List<WorkflowAttributeDefinition> attributeDefinitions) {
1029             builder.setAttributeDefinitions(attributeDefinitions);
1030             dirty = true;
1031         }
1032 
1033         public void setSearchableContent(String searchableContent) {
1034             builder.setSearchableContent(searchableContent);
1035             dirty = true;
1036         }
1037 
1038         public void setSearchableDefinitions(List<WorkflowAttributeDefinition> searchableDefinitions) {
1039             builder.setSearchableDefinitions(searchableDefinitions);
1040             dirty = true;
1041         }
1042 
1043         boolean isDirty() {
1044             return dirty;
1045         }
1046 
1047     }
1048 
1049     /**
1050      * A wrapper around Document which keeps track of local changes and generates
1051      * a new updated Document as necessary.
1052      */
1053     protected static class ModifiableDocument implements Serializable {
1054 
1055         private static final long serialVersionUID = -3234793238863410378L;
1056 
1057         private boolean dirty;
1058         private Document originalDocument;
1059         private DocumentUpdate.Builder builder;
1060 
1061         protected ModifiableDocument(Document document) {
1062             this.dirty = false;
1063             this.originalDocument = document;
1064             this.builder = DocumentUpdate.Builder.create(document);
1065         }
1066 
1067         protected Document getDocument() {
1068             if (!dirty) {
1069                 return originalDocument;
1070             }
1071             Document.Builder documentBuilder = Document.Builder.create(originalDocument);
1072             documentBuilder.setApplicationDocumentId(builder.getApplicationDocumentId());
1073             documentBuilder.setTitle(builder.getTitle());
1074             documentBuilder.setApplicationDocumentStatus(builder.getApplicationDocumentStatus());
1075             documentBuilder.setVariables(builder.getVariables());
1076             return documentBuilder.build();
1077         }
1078 
1079         protected DocumentUpdate build() {
1080             return builder.build();
1081         }
1082 
1083         /**
1084          * Immutable value which is accessed frequently, provide direct access to it.
1085          */
1086         protected String getDocumentId() {
1087             return originalDocument.getDocumentId();
1088         }
1089 
1090         /**
1091          * Immutable value which is accessed frequently, provide direct access to it.
1092          */
1093         protected DateTime getDateCreated() {
1094             return originalDocument.getDateCreated();
1095         }
1096 
1097         protected String getApplicationDocumentId() {
1098             return builder.getApplicationDocumentId();
1099         }
1100 
1101         protected void setApplicationDocumentId(String applicationDocumentId) {
1102             builder.setApplicationDocumentId(applicationDocumentId);
1103             dirty = true;
1104             addDirtyField("applicationDocumentId");
1105         }
1106 
1107         protected String getTitle() {
1108             return builder.getTitle();
1109         }
1110 
1111         protected void setTitle(String title) {
1112             builder.setTitle(title);
1113             dirty = true;
1114             addDirtyField("title");
1115 
1116         }
1117 
1118         protected String getApplicationDocumentStatus() {
1119             return builder.getApplicationDocumentStatus();
1120         }
1121 
1122         protected void setApplicationDocumentStatus(String applicationDocumentStatus) {
1123             builder.setApplicationDocumentStatus(applicationDocumentStatus);
1124             dirty = true;
1125             addDirtyField("applicationDocumentStatus");
1126         }
1127 
1128         protected void setVariable(String name, String value) {
1129             builder.setVariable(name, value);
1130             dirty = true;
1131             addDirtyField("var[" + name + "]");
1132         }
1133 
1134         protected String getVariableValue(String name) {
1135             return builder.getVariableValue(name);
1136         }
1137 
1138         boolean isDirty() {
1139             return dirty;
1140         }
1141 
1142         void addDirtyField(String field) {
1143             builder.addDirtyField(field);
1144         }
1145 
1146     }
1147 
1148 }