001/*
002 * (C) Copyright 2012-2018 Nuxeo (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Florent Guillaume
018 */
019package org.nuxeo.ecm.platform.routing.core.impl;
020
021import java.io.Serializable;
022import java.util.ArrayList;
023import java.util.Calendar;
024import java.util.Date;
025import java.util.List;
026import java.util.Map;
027
028import org.apache.commons.lang3.BooleanUtils;
029import org.apache.commons.lang3.builder.ToStringBuilder;
030import org.nuxeo.ecm.core.api.CoreSession;
031import org.nuxeo.ecm.core.api.DocumentModel;
032import org.nuxeo.ecm.core.api.model.Property;
033import org.nuxeo.ecm.core.api.model.impl.ListProperty;
034import org.nuxeo.ecm.core.api.model.impl.MapProperty;
035import org.nuxeo.ecm.platform.routing.api.DocumentRoute;
036import org.nuxeo.ecm.platform.routing.api.exception.DocumentRouteException;
037
038/**
039 * A node for a route graph. Represents operation chains, associated task and form, output transitions and their
040 * conditions, etc.
041 *
042 * @since 5.6
043 */
044public interface GraphNode {
045
046    String MERGE_ONE = "one";
047
048    String MERGE_ALL = "all";
049
050    String PROP_NODE_ID = "rnode:nodeId";
051
052    String PROP_TITLE = "dc:title";
053
054    String PROP_FORK = "rnode:fork";
055
056    String PROP_START = "rnode:start";
057
058    String PROP_STOP = "rnode:stop";
059
060    String PROP_MERGE = "rnode:merge";
061
062    String PROP_COUNT = "rnode:count";
063
064    String PROP_CANCELED = "rnode:canceled";
065
066    String PROP_INPUT_CHAIN = "rnode:inputChain";
067
068    String PROP_OUTPUT_CHAIN = "rnode:outputChain";
069
070    String PROP_HAS_TASK = "rnode:hasTask";
071
072    String PROP_VARIABLES_FACET = "rnode:variablesFacet";
073
074    String PROP_TRANSITIONS = "rnode:transitions";
075
076    String PROP_TRANS_NAME = "name";
077
078    String PROP_TRANS_TARGET = "targetId";
079
080    String PROP_TRANS_CONDITION = "condition";
081
082    String PROP_TRANS_RESULT = "result";
083
084    String PROP_TRANS_CHAIN = "chain";
085
086    String PROP_TRANS_LABEL = "label";
087
088    /**
089     * @since 7.1 a transition can hold a custom path
090     */
091    String PROP_TRANS_PATH = "path";
092
093    String PROP_TASK_ASSIGNEES = "rnode:taskAssignees";
094
095    String PROP_TASK_ASSIGNEES_VAR = "rnode:taskAssigneesExpr";
096
097    String PROP_TASK_ASSIGNEES_PERMISSION = "rnode:taskAssigneesPermission";
098
099    String PROP_TASK_DUE_DATE = "rnode:taskDueDate";
100
101    String PROP_TASK_DIRECTIVE = "rnode:taskDirective";
102
103    String PROP_TASK_LAYOUT = "rnode:taskLayout";
104
105    String PROP_TASK_BUTTONS = "rnode:taskButtons";
106
107    String PROP_BTN_NAME = "name";
108
109    String PROP_BTN_LABEL = "label";
110
111    String PROP_BTN_FILTER = "filter";
112
113    String PROP_BTN_VALIDATE = "validate";
114
115    String PROP_NODE_X_COORDINATE = "rnode:taskX";
116
117    String PROP_NODE_Y_COORDINATE = "rnode:taskY";
118
119    /**
120     * @since 5.7.3 a node can create multiple tasks, in this case, this stores the status of the last task ended
121     */
122    String PROP_NODE_BUTTON = "rnode:button";
123
124    String PROP_NODE_START_DATE = "rnode:startDate";
125
126    String PROP_NODE_END_DATE = "rnode:endDate";
127
128    String PROP_NODE_LAST_ACTOR = "rnode:lastActor";
129
130    String PROP_TASK_DOC_TYPE = "rnode:taskDocType";
131
132    String PROP_TASK_NOTIFICATION_TEMPLATE = "rnode:taskNotificationTemplate";
133
134    String PROP_TASK_DUE_DATE_EXPR = "rnode:taskDueDateExpr";
135
136    /** @since 5.7.2 */
137    String PROP_EXECUTE_ONLY_FIRST_TRANSITION = "rnode:executeOnlyFirstTransition";
138
139    /**
140     * The sub-route model id (expression) to run, if present.
141     *
142     * @since 5.7.2
143     */
144    String PROP_SUB_ROUTE_MODEL_EXPR = "rnode:subRouteModelExpr";
145
146    /**
147     * The sub-route instance id being run while this node is suspended.
148     *
149     * @since 5.7.2
150     */
151    String PROP_SUB_ROUTE_INSTANCE_ID = "rnode:subRouteInstanceId";
152
153    /**
154     * The sub-route variables to set (key/value list).
155     *
156     * @since 5.7.2
157     */
158    String PROP_SUB_ROUTE_VARS = "rnode:subRouteVariables";
159
160    /** @since 5.7.2 */
161    String PROP_KEYVALUE_KEY = "key";
162
163    /** @since 5.7.2 */
164    String PROP_KEYVALUE_VALUE = "value";
165
166    // @since 5.7.2
167    String PROP_ESCALATION_RULES = "rnode:escalationRules";
168
169    // @since 5.7.2
170    String PROP_ESCALATION_RULE_ID = "name";
171
172    // @since 5.7.2
173    String PROP_ESCALATION_RULE_LABEL = "label";
174
175    // @since 5.7.2
176    String PROP_ESCALATION_RULE_MULTIPLE_EXECUTION = "multipleExecution";
177
178    // @since 5.7.2
179    String PROP_ESCALATION_RULE_CONDITION = "condition";
180
181    // @since 5.7.2
182    String PROP_ESCALATION_RULE_CHAIN = "chain";
183
184    // @since 5.7.2
185    String PROP_ESCALATION_RULE_EXECUTED = "executed";
186
187    // @since 5.9.3
188    String PROP_LAST_EXECUTION_TIME = "executionDate";
189
190    // @since 5.7.3
191    String PROP_HAS_MULTIPLE_TASKS = "rnode:hasMultipleTasks";
192
193    // @since 5.7.3
194    String PROP_TASKS_INFO = "rnode:tasksInfo";
195
196    // @since 5.7.3
197    String PROP_TASK_INFO_ACTOR = "actor";
198
199    // @since 5.7.3
200    String PROP_TASK_INFO_COMMENT = "comment";
201
202    // @since 5.7.3
203    String PROP_TASK_INFO_STATUS = "status";
204
205    // @since 5.7.3
206    String PROP_TASK_INFO_ENDED = "ended";
207
208    // @since 5.7.3
209    String PROP_TASK_INFO_TASK_DOC_ID = "taskDocId";
210
211    // @since 5.7.3
212    String PROP_ALLOW_TASK_REASSIGNMENT = "rnode:allowTaskReassignment";
213
214    // @since 5.6
215    // if present as the node variable, acts as a built-in variable:
216    // is passed to the task comment and logged in by audit;
217    // internally rested when set on node having multiple tasks.
218    String NODE_VARIABLE_COMMENT = "comment";
219
220    /**
221     * The internal state of a node.
222     */
223    enum State {
224        /** Node is ready. */
225        READY("ready", "toReady"),
226        /** Merge node is waiting for more incoming transitions. */
227        WAITING("waiting", "toWaiting"),
228        /** While executing input phase. Not persisted. */
229        RUNNING_INPUT,
230        /** Task node is waiting for task to be done. */
231        SUSPENDED("suspended", "toSuspended"),
232        /** While executing output phase. Not persisted. */
233        RUNNING_OUTPUT;
234
235        private String lifeCycleState;
236
237        private String transition;
238
239        private State() {
240            lifeCycleState = null;
241            transition = null;
242        }
243
244        private State(String lifeCycleState, String transition) {
245            this.lifeCycleState = lifeCycleState;
246            this.transition = transition;
247        }
248
249        /**
250         * Corresponding lifecycle state.
251         */
252        public String getLifeCycleState() {
253            return lifeCycleState;
254        }
255
256        /**
257         * Transition leading to this state.
258         */
259        public String getTransition() {
260            return transition;
261        }
262
263        public static State fromString(String s) {
264            try {
265                return State.valueOf(s.toUpperCase());
266            } catch (IllegalArgumentException e) {
267                throw new IllegalArgumentException(s);
268            }
269        }
270    }
271
272    /**
273     * @since 7.1
274     */
275    class Point {
276
277        public double x;
278
279        public double y;
280
281        public Point(double x, double y) {
282            this.x = x;
283            this.y = y;
284        }
285    }
286
287    class Transition implements Comparable<Transition>, Serializable {
288
289        private static final long serialVersionUID = 1L;
290
291        public GraphNode source;
292
293        public MapProperty prop;
294
295        public String id;
296
297        public String condition;
298
299        public String chain;
300
301        public String target;
302
303        public String label;
304
305        public boolean result;
306
307        /**
308         * @since 7.1
309         */
310        public List<Point> path;
311
312        /** Computed by graph. */
313        public boolean loop;
314
315        protected Transition(GraphNode source, Property p) {
316            this.source = source;
317            prop = (MapProperty) p;
318            id = (String) prop.get(PROP_TRANS_NAME).getValue();
319            condition = (String) prop.get(PROP_TRANS_CONDITION).getValue();
320            chain = (String) prop.get(PROP_TRANS_CHAIN).getValue();
321            target = (String) prop.get(PROP_TRANS_TARGET).getValue();
322            label = (String) prop.get(PROP_TRANS_LABEL).getValue();
323            Property resultProp = prop.get(PROP_TRANS_RESULT);
324            if (resultProp != null) {
325                result = BooleanUtils.isTrue(resultProp.getValue(Boolean.class));
326            }
327        }
328
329        protected void setResult(boolean bool) {
330            result = bool;
331            prop.get(PROP_TRANS_RESULT).setValue(Boolean.valueOf(bool));
332        }
333
334        @Override
335        public int compareTo(Transition other) {
336            return id.compareTo(other.id);
337        }
338
339        @Override
340        public String toString() {
341            return new ToStringBuilder(this).append("id", id).append("condition", condition).append("result", result).toString();
342        }
343
344        public String getTarget() {
345            return target;
346        }
347
348        public String getId() {
349            return id;
350        }
351
352        public String getLabel() {
353            return label;
354        }
355
356        /**
357         * @since 7.1
358         */
359        public List<Point> getPath() {
360            if (path == null) {
361                path = computePath();
362            }
363            return path;
364        }
365
366        protected List<Point> computePath() {
367            ListProperty props = (ListProperty) prop.get(PROP_TRANS_PATH);
368            List<Point> points = new ArrayList<>(props.size());
369            for (Property p : props) {
370                points.add(new Point(
371                    (Double) p.get("x").getValue(),
372                    (Double) p.get("y").getValue()));
373            }
374            return points;
375        }
376    }
377
378    public class Button implements Comparable<Button> {
379
380        public GraphNode source;
381
382        public String name;
383
384        public String label;
385
386        public String filter;
387
388        public Boolean validate;
389
390        public MapProperty prop;
391
392        public Button(GraphNode source, Property p) {
393            this.source = source;
394            this.prop = (MapProperty) p;
395            name = (String) prop.get(PROP_BTN_NAME).getValue();
396            label = (String) prop.get(PROP_BTN_LABEL).getValue();
397            filter = (String) prop.get(PROP_BTN_FILTER).getValue();
398            validate = prop.getValue(Boolean.class, PROP_BTN_VALIDATE);
399        }
400
401        @Override
402        public int compareTo(Button other) {
403            return name.compareTo(other.name);
404        }
405
406        public String getLabel() {
407            return label;
408        }
409
410        public String getName() {
411            return name;
412        }
413
414        public String getFilter() {
415            return filter;
416        }
417
418        public Boolean getValidate() {
419            return validate;
420        }
421    }
422
423    /**
424     * @since 5.7.2
425     */
426    class EscalationRule implements Comparable<EscalationRule> {
427
428        protected String id;
429
430        protected String label;
431
432        protected boolean multipleExecution;
433
434        protected String condition;
435
436        protected boolean executed;
437
438        protected String chain;
439
440        protected MapProperty prop;
441
442        protected GraphNode node;
443
444        /**
445         * @since 5.9.3
446         */
447        protected Calendar lastExcutionTime;
448
449        public EscalationRule(GraphNode node, Property p) {
450            this.prop = (MapProperty) p;
451            this.node = node;
452            this.id = (String) p.get(PROP_ESCALATION_RULE_ID).getValue();
453            this.label = (String) p.get(PROP_ESCALATION_RULE_LABEL).getValue();
454            Property multipleEvaluationProp = prop.get(PROP_ESCALATION_RULE_MULTIPLE_EXECUTION);
455            if (multipleEvaluationProp != null) {
456                multipleExecution = BooleanUtils.isTrue(multipleEvaluationProp.getValue(Boolean.class));
457            }
458            this.condition = (String) p.get(PROP_ESCALATION_RULE_CONDITION).getValue();
459            Property evaluatedProp = prop.get(PROP_ESCALATION_RULE_EXECUTED);
460            if (evaluatedProp != null) {
461                executed = BooleanUtils.isTrue(evaluatedProp.getValue(Boolean.class));
462            }
463            this.chain = (String) p.get(PROP_ESCALATION_RULE_CHAIN).getValue();
464            this.lastExcutionTime = (Calendar) p.get(PROP_LAST_EXECUTION_TIME).getValue();
465        }
466
467        @Override
468        public int compareTo(EscalationRule o) {
469            return id.compareTo(o.id);
470        }
471
472        public String getLabel() {
473            return label;
474        }
475
476        public String getChain() {
477            return chain;
478        }
479
480        public GraphNode getNode() {
481            return node;
482        }
483
484        public void setExecuted(boolean executed) {
485            this.executed = executed;
486            prop.get(PROP_ESCALATION_RULE_EXECUTED).setValue(Boolean.valueOf(executed));
487            if (executed) {
488                setExecutionTime(Calendar.getInstance());
489            }
490        }
491
492        protected void setExecutionTime(Calendar time) {
493            prop.get(PROP_LAST_EXECUTION_TIME).setValue(time);
494            this.lastExcutionTime = time;
495        }
496
497        public boolean isExecuted() {
498            return executed;
499        }
500
501        public String getId() {
502            return id;
503        }
504
505        public boolean isMultipleExecution() {
506            return multipleExecution;
507        }
508
509        /**
510         * @since 5.9.3 Returns 'null' if the node was not executed, or the executed date was not computed ( for rules
511         *        created before 5.9.3)
512         */
513        public Calendar getLastExecutionTime() {
514            if (executed && lastExcutionTime != null) {
515                return lastExcutionTime;
516            }
517            return null;
518        }
519    }
520
521    /**
522     * @since 5.7.3
523     */
524    class TaskInfo implements Comparable<TaskInfo>, Serializable {
525
526        private static final long serialVersionUID = 1L;
527
528        protected String taskDocId;
529
530        protected String actor;
531
532        protected String comment;
533
534        protected String status;
535
536        protected boolean ended;
537
538        protected MapProperty prop;
539
540        protected GraphNode node;
541
542        public TaskInfo(GraphNode node, Property p) {
543            this.prop = (MapProperty) p;
544            this.node = node;
545            this.taskDocId = (String) p.get(PROP_TASK_INFO_TASK_DOC_ID).getValue();
546            this.status = (String) p.get(PROP_TASK_INFO_STATUS).getValue();
547            this.actor = (String) p.get(PROP_TASK_INFO_ACTOR).getValue();
548            this.comment = (String) p.get(PROP_TASK_INFO_COMMENT).getValue();
549            Property ended = prop.get(PROP_TASK_INFO_ENDED);
550            if (ended != null) {
551                this.ended = BooleanUtils.isTrue(ended.getValue(Boolean.class));
552            }
553        }
554
555        public TaskInfo(GraphNode node, String taskDocId) {
556            this.node = node;
557            this.prop = (MapProperty) ((ListProperty) node.getDocument().getProperty(PROP_TASKS_INFO)).addEmpty();
558            this.prop.get(PROP_TASK_INFO_TASK_DOC_ID).setValue(taskDocId);
559            this.taskDocId = taskDocId;
560        }
561
562        @Override
563        public int compareTo(TaskInfo o) {
564            return taskDocId.compareTo(o.taskDocId);
565        }
566
567        public String getTaskDocId() {
568            return taskDocId;
569        }
570
571        public String getActor() {
572            return actor;
573        }
574
575        public String getComment() {
576            return comment;
577        }
578
579        public String getStatus() {
580            return status;
581        }
582
583        public GraphNode getNode() {
584            return node;
585        }
586
587        public boolean isEnded() {
588            return ended;
589        }
590
591        public void setComment(String comment) {
592            this.comment = comment;
593            prop.get(PROP_TASK_INFO_COMMENT).setValue(comment);
594        }
595
596        public void setStatus(String status) {
597            this.status = status;
598            prop.get(PROP_TASK_INFO_STATUS).setValue(status);
599
600        }
601
602        public void setActor(String actor) {
603            this.actor = actor;
604            prop.get(PROP_TASK_INFO_ACTOR).setValue(actor);
605        }
606
607        public void setEnded(boolean ended) {
608            this.ended = ended;
609            prop.get(PROP_TASK_INFO_ENDED).setValue(Boolean.valueOf(ended));
610        }
611    }
612
613    /**
614     * Get the node id.
615     *
616     * @return the node id
617     */
618    String getId();
619
620    /**
621     * Get the node state.
622     *
623     * @return the node state
624     */
625    State getState();
626
627    /**
628     * Set the node state.
629     *
630     * @param state the node state
631     */
632    void setState(State state);
633
634    /**
635     * Checks if this is a fork node.
636     *
637     * @since 11.1
638     */
639    boolean isFork();
640
641    /**
642     * Checks if this is the start node.
643     */
644    boolean isStart();
645
646    /**
647     * Checks if this is a stop node.
648     */
649    boolean isStop();
650
651    /**
652     * Checks if this is a merge node.
653     */
654    boolean isMerge();
655
656    /**
657     * Checks if the merge is ready to execute (enough input transitions are present).
658     */
659    boolean canMerge();
660
661    /**
662     * Notes that this node was canceled (increments canceled counter).
663     */
664    void setCanceled();
665
666    /**
667     * Gets the canceled count for this node.
668     */
669    long getCanceledCount();
670
671    /**
672     * Cancels the tasks not ended on this node.
673     */
674    void cancelTasks();
675
676    /**
677     * Get input chain.
678     *
679     * @return the input chain
680     */
681    String getInputChain();
682
683    /**
684     * Get output chain.
685     *
686     * @return the output chain
687     */
688    String getOutputChain();
689
690    /**
691     * Checks it this node has an associated user task.
692     */
693    boolean hasTask();
694
695    /**
696     * Gets the task assignees
697     *
698     * @return the task assignees
699     */
700    List<String> getTaskAssignees();
701
702    /**
703     * Gets the due date
704     */
705    Date getTaskDueDate();
706
707    /**
708     * Gets the task directive
709     */
710    String getTaskDirective();
711
712    /**
713     * Gets the permission to the granted to the actors on this task on the document following the workflow
714     */
715    String getTaskAssigneesPermission();
716
717    /**
718     * Gets the task layout
719     */
720    String getTaskLayout();
721
722    /**
723     * @return the taskDocType. If none is specified, the default task type is returned.
724     */
725    String getTaskDocType();
726
727    String getTaskNotificationTemplate();
728
729    /**
730     * Does bookkeeping at node start.
731     */
732    void starting();
733
734    /**
735     * Does bookkeeping at node end.
736     */
737    void ending();
738
739    /**
740     * Executes an Automation chain in the context of this node.
741     *
742     * @param chainId the chain
743     */
744    void executeChain(String chainId) throws DocumentRouteException;
745
746    /** Internal during graph init. */
747    void initAddInputTransition(Transition transition);
748
749    /**
750     * Gets the input transitions.
751     */
752    List<Transition> getInputTransitions();
753
754    /**
755     * Gets the output transitions.
756     */
757    List<Transition> getOutputTransitions();
758
759    String getTaskDueDateExpr();
760
761    /**
762     * Executes an Automation chain in the context of this node for a given transition
763     *
764     * @param transition the transition
765     */
766    void executeTransitionChain(Transition transition) throws DocumentRouteException;
767
768    /**
769     * Evaluates transition conditions and returns the transitions that were true.
770     * <p>
771     * Transitions are evaluated in the order set on the node when the workflow was designed. Since @5.7.2 if the node
772     * has the property "executeOnlyFirstTransition" set to true, only the first transition evaluated to true is
773     * returned
774     *
775     * @return the true transitions
776     */
777    List<Transition> evaluateTransitions() throws DocumentRouteException;
778
779    /**
780     * Sets the graph and node variables.
781     *
782     * @param map the map of variables
783     */
784    void setAllVariables(Map<String, Object> map);
785
786    /**
787     * Sets the graph and node variables.
788     *
789     * @param map the map of variables
790     * @param allowGlobalVariablesAssignement if set to false, throw a DocumentRouteException when trying to set global
791     *            variables when not supposed to
792     * @since 7.2
793     */
794    void setAllVariables(Map<String, Object> map, final boolean allowGlobalVariablesAssignement);
795
796    /**
797     * Gets the task buttons
798     */
799    List<Button> getTaskButtons();
800
801    /**
802     * Has the node the given action.
803     *
804     * @since 7.2
805     */
806    boolean hasTaskButton(final String name);
807
808    /**
809     * Gets the document representing this node
810     */
811    DocumentModel getDocument();
812
813    /**
814     * Gets a map containing the variables currently defined on this node
815     */
816    Map<String, Serializable> getVariables();
817
818    /**
819     * Gets a map containing the Json formatted variables currently defined on this node
820     * @since 7.2
821     */
822    Map<String, Serializable> getJsonVariables();
823
824    /**
825     * Sets the property button on the node, keeping the id of the last action executed by the user on the associated
826     * task if any
827     */
828    void setButton(String status);
829
830    /**
831     * Sets the last actor on a node (user who completed the task).
832     *
833     * @param actor the user id
834     */
835    void setLastActor(String actor);
836
837    /**
838     * Evaluates the task assignees from the taskAssigneesVar
839     */
840    List<String> evaluateTaskAssignees() throws DocumentRouteException;
841
842    /**
843     * Evaluates the task due date from the taskDueDateExpr and sets it as the dueDate
844     */
845    Date computeTaskDueDate() throws DocumentRouteException;
846
847    /**
848     * Gets a map containing the workflow and node variables and workflow documents.
849     *
850     * @param detached The documents added into this map can be detached or not
851     */
852    Map<String, Serializable> getWorkflowContextualInfo(CoreSession session, boolean detached);
853
854    /**
855     * When workflow engine runs an exclusive node, it evaluates the transition one by one and stops a soon as one of
856     * the transition is evaluated to true
857     *
858     * @since 5.7.2
859     */
860    boolean executeOnlyFirstTransition();
861
862    /**
863     * Checks if this node has a sub-route model defined.
864     *
865     * @return {@code true} if there is a sub-route
866     * @since 5.7.2
867     */
868    boolean hasSubRoute() throws DocumentRouteException;
869
870    /**
871     * Gets the sub-route model id.
872     * <p>
873     * If this is present, then this node will be suspended while the sub-route is run. When the sub-route ends, this
874     * node will resume.
875     *
876     * @return the sub-route id, or {@code null} if none is defined
877     * @since 5.7.2
878     */
879    String getSubRouteModelId() throws DocumentRouteException;
880
881    /**
882     * Starts the sub-route on this node.
883     *
884     * @return the sub-route
885     * @since 5.7.2
886     */
887    DocumentRoute startSubRoute() throws DocumentRouteException;
888
889    /**
890     * Cancels the sub-route if there is one.
891     *
892     * @since 5.7.2
893     */
894    void cancelSubRoute() throws DocumentRouteException;
895
896    /**
897     * Evaluates the rules for the escalation rules and returns the ones to be executed. The rules already executed and
898     * not having the property multipleExecution = true are also ignored
899     *
900     * @since 5.7.2
901     */
902    List<EscalationRule> evaluateEscalationRules();
903
904    /**
905     * Gets the list of all escalation rules for the node
906     *
907     * @since 5.7.2
908     */
909    List<EscalationRule> getEscalationRules();
910
911    /**
912     * Checks if this node has created multiple tasks, one for each assignees.
913     *
914     * @since 5.7.3
915     */
916    boolean hasMultipleTasks();
917
918    /**
919     * Gets all the tasks info for the tasks created from this node
920     *
921     * @since 5.7.3
922     */
923    List<TaskInfo> getTasksInfo();
924
925    /**
926     * Persist the info when a new task is created from this node
927     *
928     * @since 5.7.3
929     */
930    void addTaskInfo(String taskId);
931
932    /**
933     * Persist these info from the task on the node. Status is the id of the button clicked to end the task by the
934     * actor.
935     *
936     * @since 5.7.3
937     */
938    void updateTaskInfo(String taskId, boolean ended, String status, String actor, String comment);
939
940    /**
941     * Gets all the ended tasks originating from this node. This also counts the canceled tasks.
942     *
943     * @since 5.7.3
944     */
945    List<TaskInfo> getEndedTasksInfo();
946
947    /**
948     * Gets all the ended tasks originating from this node that were processed with a status. Doesn't count the canceled
949     * tasks.
950     *
951     * @since 5.7.3
952     */
953    List<TaskInfo> getProcessedTasksInfo();
954
955    /**
956     * Returns false if all tasks created from this node were ended.
957     *
958     * @since 5.7.3
959     */
960    boolean hasOpenTasks();
961
962    /**
963     * Returns true if tasks created from this node can be reassigned.
964     *
965     * @since 5.7.3
966     */
967    boolean allowTaskReassignment();
968
969    /**
970     * Sets the variable on this node if it exists as a Node Variable.
971     *
972     * @since 5.8
973     */
974    void setVariable(String name, String value);
975
976    /**
977     * Sets the node variables.
978     *
979     * @since 5.9.3, 5.8.0-HF11
980     * @param map the map of variables
981     */
982    void setVariables(Map<String, Serializable> map);
983
984    /**
985     * Sets the variables of the workflow based on their JSON representation (especially for scalar lists). For example:
986     *
987     * <pre>
988     * Map&lt;String, String&gt; map = new HashMap&lt;&gt;();
989     * map.put("contributors", "[\"John Doe\", \"John Smith\"]");
990     * map.put("title", "Test Title");
991     * </pre>
992     *
993     * @param map the map of variables
994     * @since 5.9.3, 5.8.0-HF11
995     */
996    void setJSONVariables(Map<String, String> map);
997
998    /**
999     * @since 7.4
1000     */
1001    void removeTaskInfo(String taskId);
1002
1003}