1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.scxml2.semantics;
18
19 import java.util.ArrayList;
20 import java.util.Collections;
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.LinkedHashSet;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Set;
27
28 import org.apache.commons.scxml2.ActionExecutionContext;
29 import org.apache.commons.scxml2.Context;
30 import org.apache.commons.scxml2.ErrorReporter;
31 import org.apache.commons.scxml2.SCInstance;
32 import org.apache.commons.scxml2.SCXMLExecutionContext;
33 import org.apache.commons.scxml2.SCXMLExpressionException;
34 import org.apache.commons.scxml2.SCXMLSemantics;
35 import org.apache.commons.scxml2.SCXMLSystemContext;
36 import org.apache.commons.scxml2.StateConfiguration;
37 import org.apache.commons.scxml2.TriggerEvent;
38 import org.apache.commons.scxml2.invoke.Invoker;
39 import org.apache.commons.scxml2.invoke.InvokerException;
40 import org.apache.commons.scxml2.model.Action;
41 import org.apache.commons.scxml2.model.DocumentOrder;
42 import org.apache.commons.scxml2.model.EnterableState;
43 import org.apache.commons.scxml2.model.Executable;
44 import org.apache.commons.scxml2.model.Final;
45 import org.apache.commons.scxml2.model.History;
46 import org.apache.commons.scxml2.model.Invoke;
47 import org.apache.commons.scxml2.model.OnEntry;
48 import org.apache.commons.scxml2.model.OnExit;
49 import org.apache.commons.scxml2.model.Script;
50 import org.apache.commons.scxml2.model.SimpleTransition;
51 import org.apache.commons.scxml2.model.TransitionalState;
52 import org.apache.commons.scxml2.model.ModelException;
53 import org.apache.commons.scxml2.model.Parallel;
54 import org.apache.commons.scxml2.model.SCXML;
55 import org.apache.commons.scxml2.model.State;
56 import org.apache.commons.scxml2.model.Transition;
57 import org.apache.commons.scxml2.model.TransitionTarget;
58 import org.apache.commons.scxml2.system.EventVariable;
59
60
61
62
63
64
65
66
67
68
69 public class SCXMLSemanticsImpl implements SCXMLSemantics {
70
71
72
73
74
75 public static final String ERR_ILLEGAL_ALLOC = ".error.illegalalloc";
76
77
78
79
80
81
82
83
84
85 public SCXML normalizeStateMachine(final SCXML input, final ErrorReporter errRep) {
86
87 return input;
88 }
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111 public void firstStep(final SCXMLExecutionContext exctx) throws ModelException {
112
113 exctx.initialize();
114
115 executeGlobalScript(exctx);
116
117 HashSet<TransitionalState> statesToInvoke = new HashSet<TransitionalState>();
118 Step step = new Step(null);
119 step.getTransitList().add(exctx.getStateMachine().getInitialTransition());
120 microStep(exctx, step, statesToInvoke);
121
122
123 if (exctx.isRunning()) {
124 macroStep(exctx, statesToInvoke);
125 }
126
127 if (!exctx.isRunning()) {
128 finalStep(exctx);
129 }
130 }
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158 public void nextStep(final SCXMLExecutionContext exctx, final TriggerEvent event) throws ModelException {
159 if (!exctx.isRunning()) {
160 return;
161 }
162 if (isCancelEvent(event)) {
163 exctx.stopRunning();
164 }
165 else {
166 setSystemEventVariable(exctx.getScInstance(), event, false);
167 processInvokes(exctx, event);
168 Step step = new Step(event);
169 selectTransitions(exctx, step);
170 if (!step.getTransitList().isEmpty()) {
171 HashSet<TransitionalState> statesToInvoke = new HashSet<TransitionalState>();
172 microStep(exctx, step, statesToInvoke);
173 if (exctx.isRunning()) {
174 macroStep(exctx, statesToInvoke);
175 }
176 }
177 }
178 if (!exctx.isRunning()) {
179 finalStep(exctx);
180 }
181 }
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202 public void finalStep(SCXMLExecutionContext exctx) throws ModelException {
203 if (exctx.isRunning()) {
204 return;
205 }
206 ArrayList<EnterableState> configuration = new ArrayList<EnterableState>(exctx.getScInstance().getStateConfiguration().getActiveStates());
207 Collections.sort(configuration, DocumentOrder.reverseDocumentOrderComparator);
208 for (EnterableState es : configuration) {
209 for (OnExit onexit : es.getOnExits()) {
210 executeContent(exctx, onexit);
211 }
212 if (es instanceof TransitionalState) {
213
214 for (Invoke inv : ((TransitionalState)es).getInvokes()) {
215 exctx.cancelInvoker(inv);
216 }
217 }
218 exctx.getNotificationRegistry().fireOnExit(es, es);
219 exctx.getNotificationRegistry().fireOnExit(exctx.getStateMachine(), es);
220 if (!(es instanceof Final && es.getParent() == null)) {
221 exctx.getScInstance().getStateConfiguration().exitState(es);
222 }
223
224
225 }
226 }
227
228
229
230
231
232
233
234
235
236
237
238
239 public void microStep(final SCXMLExecutionContext exctx, final Step step,
240 final Set<TransitionalState> statesToInvoke)
241 throws ModelException {
242 buildStep(exctx, step);
243 exitStates(exctx, step, statesToInvoke);
244 executeTransitionContent(exctx, step);
245 enterStates(exctx, step, statesToInvoke);
246 step.clearIntermediateState();
247 }
248
249
250
251
252
253
254
255
256 public void buildStep(final SCXMLExecutionContext exctx, final Step step) throws ModelException {
257 step.clearIntermediateState();
258
259
260 if (!exctx.getScInstance().getStateConfiguration().getActiveStates().isEmpty()) {
261 computeExitSet(step, exctx.getScInstance().getStateConfiguration());
262 }
263
264 computeEntrySet(exctx, step);
265
266
267 Set<EnterableState> states = step.getEntrySet();
268 if (!step.getExitSet().isEmpty()) {
269
270 states = new HashSet<EnterableState>(exctx.getScInstance().getStateConfiguration().getStates());
271 states.removeAll(step.getExitSet());
272 states.addAll(step.getEntrySet());
273 }
274
275 if (exctx.isCheckLegalConfiguration() && !isLegalConfiguration(states, exctx.getErrorReporter())) {
276 throw new ModelException("Illegal state machine configuration!");
277 }
278 }
279
280
281
282
283
284
285
286
287
288
289
290
291
292 public void macroStep(final SCXMLExecutionContext exctx, final Set<TransitionalState> statesToInvoke)
293 throws ModelException {
294 do {
295 boolean macroStepDone = false;
296 do {
297 Step step = new Step(null);
298 selectTransitions(exctx, step);
299 if (step.getTransitList().isEmpty()) {
300 TriggerEvent event = exctx.nextInternalEvent();
301 if (event != null) {
302 if (isCancelEvent(event)) {
303 exctx.stopRunning();
304 }
305 else {
306 setSystemEventVariable(exctx.getScInstance(), event, true);
307 step = new Step(event);
308 selectTransitions(exctx, step);
309 }
310 }
311 }
312 if (step.getTransitList().isEmpty()) {
313 macroStepDone = true;
314 }
315 else {
316 microStep(exctx, step, statesToInvoke);
317 }
318
319 } while (exctx.isRunning() && !macroStepDone);
320
321 if (exctx.isRunning() && !statesToInvoke.isEmpty()) {
322 initiateInvokes(exctx, statesToInvoke);
323 statesToInvoke.clear();
324 }
325 } while (exctx.isRunning() && exctx.hasPendingInternalEvent());
326 }
327
328
329
330
331
332
333
334
335
336 public void computeExitSet(final Step step, final StateConfiguration stateConfiguration) {
337 if (!stateConfiguration.getActiveStates().isEmpty()) {
338 for (SimpleTransition st : step.getTransitList()) {
339 computeExitSet(st, step.getExitSet(), stateConfiguration.getActiveStates());
340 }
341 recordHistory(step, stateConfiguration.getStates(), stateConfiguration.getActiveStates());
342 }
343 }
344
345
346
347
348
349
350
351
352
353
354 public void computeExitSet(SimpleTransition transition, Set<EnterableState> exitSet, Set<EnterableState> activeStates) {
355 if (!transition.getTargets().isEmpty()) {
356 TransitionalState transitionDomain = transition.getTransitionDomain();
357 if (transitionDomain == null) {
358
359 exitSet.addAll(activeStates);
360 }
361 else {
362 for (EnterableState state : activeStates) {
363 if (state.isDescendantOf(transitionDomain)) {
364 exitSet.add(state);
365 }
366 }
367 }
368 }
369 }
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386 public void recordHistory(final Step step, final Set<EnterableState> atomicStates, final Set<EnterableState> activeStates) {
387 for (EnterableState es : step.getExitSet()) {
388 if (es instanceof TransitionalState && ((TransitionalState)es).hasHistory()) {
389 TransitionalState ts = (TransitionalState)es;
390 Set<EnterableState> shallow = null;
391 Set<EnterableState> deep = null;
392 for (History h : ts.getHistory()) {
393 if (h.isDeep()) {
394 if (deep == null) {
395
396 deep = new HashSet<EnterableState>();
397 for (EnterableState ott : atomicStates) {
398 if (ott.isDescendantOf(es)) {
399 deep.add(ott);
400 }
401 }
402 }
403 step.getNewHistoryConfigurations().put(h, deep);
404 } else {
405 if (shallow == null) {
406
407 shallow = new HashSet<EnterableState>(ts.getChildren());
408 shallow.retainAll(activeStates);
409 }
410 step.getNewHistoryConfigurations().put(h, shallow);
411 }
412 }
413 }
414 }
415 }
416
417
418
419
420
421
422
423
424
425 public void computeEntrySet(final SCXMLExecutionContext exctx, final Step step) {
426 Set<History> historyTargets = new HashSet<History>();
427 Set<EnterableState> entrySet = new HashSet<EnterableState>();
428 for (SimpleTransition st : step.getTransitList()) {
429 for (TransitionTarget tt : st.getTargets()) {
430 if (tt instanceof EnterableState) {
431 entrySet.add((EnterableState) tt);
432 }
433 else {
434
435 historyTargets.add((History)tt);
436 }
437 }
438 }
439 for (EnterableState es : entrySet) {
440 addDescendantStatesToEnter(exctx, step, es);
441 }
442 for (History h : historyTargets) {
443 addDescendantStatesToEnter(exctx, step, h);
444 }
445 for (SimpleTransition st : step.getTransitList()) {
446 TransitionalState ancestor = st.getTransitionDomain();
447 for (TransitionTarget tt : st.getTargets()) {
448 addAncestorStatesToEnter(exctx, step, tt, ancestor);
449 }
450 }
451 }
452
453
454
455
456
457
458
459
460 public void addDescendantStatesToEnter(final SCXMLExecutionContext exctx, final Step step,
461 final TransitionTarget tt) {
462 if (tt instanceof History) {
463 History h = (History) tt;
464 Set<EnterableState> lastConfiguration = step.getNewHistoryConfigurations().get(h);
465 if (lastConfiguration == null) {
466 lastConfiguration = exctx.getScInstance().getLastConfiguration(h);
467 }
468 if (lastConfiguration.isEmpty()) {
469 step.getDefaultHistoryTransitions().put(h.getParent(), h.getTransition());
470 for (TransitionTarget dtt : h.getTransition().getTargets()) {
471 addDescendantStatesToEnter(exctx, step, dtt);
472 }
473 for (TransitionTarget dtt : h.getTransition().getTargets()) {
474 addAncestorStatesToEnter(exctx, step, dtt, tt.getParent());
475 }
476 } else {
477 for (TransitionTarget dtt : lastConfiguration) {
478 addDescendantStatesToEnter(exctx, step, dtt);
479 }
480 for (TransitionTarget dtt : lastConfiguration) {
481 addAncestorStatesToEnter(exctx, step, dtt, tt.getParent());
482 }
483 }
484 }
485 else {
486 EnterableState es = (EnterableState)tt;
487 step.getEntrySet().add(es);
488 if (es instanceof Parallel) {
489 for (EnterableState child : ((Parallel)es).getChildren()) {
490 if (!containsDescendant(step.getEntrySet(), child)) {
491 addDescendantStatesToEnter(exctx, step, child);
492 }
493 }
494 }
495 else if (es instanceof State && ((State) es).isComposite()) {
496 step.getDefaultEntrySet().add(es);
497 for (TransitionTarget dtt : ((State)es).getInitial().getTransition().getTargets()) {
498 addDescendantStatesToEnter(exctx, step, dtt);
499 }
500 for (TransitionTarget dtt : ((State)es).getInitial().getTransition().getTargets()) {
501 addAncestorStatesToEnter(exctx, step, dtt, tt);
502 }
503 }
504 }
505 }
506
507
508
509
510
511
512
513
514
515 public void addAncestorStatesToEnter(final SCXMLExecutionContext exctx, final Step step,
516 final TransitionTarget tt, TransitionTarget ancestor) {
517
518 for (int i = tt.getNumberOfAncestors()-1; i > -1; i--) {
519 EnterableState anc = tt.getAncestor(i);
520 if (anc == ancestor) {
521 break;
522 }
523 step.getEntrySet().add(anc);
524 if (anc instanceof Parallel) {
525 for (EnterableState child : ((Parallel)anc).getChildren()) {
526 if (!containsDescendant(step.getEntrySet(), child)) {
527 addDescendantStatesToEnter(exctx, step, child);
528 }
529 }
530
531 }
532 }
533 }
534
535
536
537
538
539
540 public boolean containsDescendant(Set<EnterableState> states, EnterableState state) {
541 for (EnterableState es : states) {
542 if (es.isDescendantOf(state)) {
543 return true;
544 }
545 }
546 return false;
547 }
548
549
550
551
552
553
554
555
556 public void selectTransitions(final SCXMLExecutionContext exctx, final Step step) throws ModelException {
557 step.getTransitList().clear();
558 ArrayList<Transition> enabledTransitions = new ArrayList<Transition>();
559
560 ArrayList<EnterableState> configuration = new ArrayList<EnterableState>(exctx.getScInstance().getStateConfiguration().getActiveStates());
561 Collections.sort(configuration,DocumentOrder.documentOrderComparator);
562
563 HashSet<EnterableState> visited = new HashSet<EnterableState>();
564
565 String eventName = step.getEvent() != null ? step.getEvent().getName() : null;
566 for (EnterableState es : configuration) {
567 if (es.isAtomicState()) {
568 if (es instanceof Final) {
569
570 if (es.getParent() == null) {
571
572 throw new ModelException("Illegal state machine configuration: encountered top level <final> "
573 + "state while processing an event");
574 }
575 else {
576 es = es.getParent();
577 }
578 }
579 TransitionalState state = (TransitionalState)es;
580 TransitionalState current = state;
581 int ancestorIndex = state.getNumberOfAncestors()-1;
582 boolean transitionMatched = false;
583 do {
584 for (Transition transition : current.getTransitionsList()) {
585 if (transitionMatched = matchTransition(exctx, transition, eventName)) {
586 enabledTransitions.add(transition);
587 break;
588 }
589 }
590 current = (!transitionMatched && ancestorIndex > -1) ? state.getAncestor(ancestorIndex--) : null;
591 } while (!transitionMatched && current != null && visited.add(current));
592 }
593 }
594 removeConflictingTransitions(exctx, step, enabledTransitions);
595 }
596
597
598
599
600
601
602
603
604 public void removeConflictingTransitions(final SCXMLExecutionContext exctx, final Step step,
605 final List<Transition> enabledTransitions) {
606 LinkedHashSet<Transition> filteredTransitions = new LinkedHashSet<Transition>();
607 LinkedHashSet<Transition> preemptedTransitions = new LinkedHashSet<Transition>();
608 Map<Transition, Set<EnterableState>> exitSets = new HashMap<Transition, Set<EnterableState>>();
609
610 Set<EnterableState> configuration = exctx.getScInstance().getStateConfiguration().getActiveStates();
611 Collections.sort(enabledTransitions, DocumentOrder.documentOrderComparator);
612
613 for (Transition t1 : enabledTransitions) {
614 boolean t1Preempted = false;
615 Set<EnterableState> t1ExitSet = exitSets.get(t1);
616 for (Transition t2 : filteredTransitions) {
617 if (t1ExitSet == null) {
618 t1ExitSet = new HashSet<EnterableState>();
619 computeExitSet(t1, t1ExitSet, configuration);
620 exitSets.put(t1, t1ExitSet);
621 }
622 Set<EnterableState> t2ExitSet = exitSets.get(t2);
623 if (t2ExitSet == null) {
624 t2ExitSet = new HashSet<EnterableState>();
625 computeExitSet(t2, t2ExitSet, configuration);
626 exitSets.put(t2, t2ExitSet);
627 }
628 Set<EnterableState> smaller = t1ExitSet.size() < t2ExitSet.size() ? t1ExitSet : t2ExitSet;
629 Set<EnterableState> larger = smaller == t1ExitSet ? t2ExitSet : t1ExitSet;
630 boolean hasIntersection = false;
631 for (EnterableState s1 : smaller) {
632 hasIntersection = larger.contains(s1);
633 if (hasIntersection) {
634 break;
635 }
636 }
637 if (hasIntersection) {
638 if (t1.getParent().isDescendantOf(t2.getParent())) {
639 preemptedTransitions.add(t2);
640 }
641 else {
642 t1Preempted = true;
643 break;
644 }
645 }
646 }
647 if (t1Preempted) {
648 exitSets.remove(t1);
649 }
650 else {
651 for (Transition preempted : preemptedTransitions) {
652 filteredTransitions.remove(preempted);
653 exitSets.remove(preempted);
654 }
655 filteredTransitions.add(t1);
656 }
657 }
658 step.getTransitList().addAll(filteredTransitions);
659 }
660
661
662
663
664
665
666
667
668 public boolean matchTransition(final SCXMLExecutionContext exctx, final Transition transition, final String eventName) {
669 if (eventName != null) {
670 if (!(transition.isNoEventsTransition() || transition.isAllEventsTransition())) {
671 boolean eventMatch = false;
672 for (String event : transition.getEvents()) {
673 if (eventName.startsWith(event)) {
674 if (eventName.length() == event.length() || eventName.charAt(event.length())=='.')
675 eventMatch = true;
676 break;
677 }
678 }
679 if (!eventMatch) {
680 return false;
681 }
682 }
683 else if (!transition.isAllEventsTransition()) {
684 return false;
685 }
686 }
687 else if (!transition.isNoEventsTransition()) {
688 return false;
689 }
690 if (transition.getCond() != null) {
691 Boolean result = Boolean.FALSE;
692 Context context = exctx.getScInstance().getContext(transition.getParent());
693 context.setLocal(Context.NAMESPACES_KEY, transition.getNamespaces());
694 try {
695 if ((result = exctx.getEvaluator().evalCond(context, transition.getCond())) == null) {
696 result = Boolean.FALSE;
697 if (exctx.getAppLog().isDebugEnabled()) {
698 exctx.getAppLog().debug("Treating as false because the cond expression was evaluated as null: '"
699 + transition.getCond() + "'");
700 }
701 }
702 }
703 catch (SCXMLExpressionException e) {
704 exctx.getInternalIOProcessor().addEvent(new TriggerEvent(TriggerEvent.ERROR_EXECUTION, TriggerEvent.ERROR_EVENT));
705 exctx.getErrorReporter().onError(ErrorConstants.EXPRESSION_ERROR, "Treating as false due to error: "
706 + e.getMessage(), transition);
707 }
708 finally {
709 context.setLocal(Context.NAMESPACES_KEY, null);
710 }
711 return result;
712 }
713 return true;
714 }
715
716
717
718
719
720
721
722
723
724 public boolean isInFinalState(final EnterableState es, final Set<EnterableState> configuration) {
725 if (es instanceof State) {
726 for (EnterableState child : ((State)es).getChildren()) {
727 if (child instanceof Final && configuration.contains(child)) {
728 return true;
729 }
730 }
731 }
732 else if (es instanceof Parallel) {
733 for (EnterableState child : ((Parallel)es).getChildren()) {
734 if (!isInFinalState(child, configuration)) {
735 return false;
736 }
737 }
738 return true;
739 }
740 return false;
741 }
742
743
744
745
746
747
748
749
750
751 public boolean isInvokerEvent(final String invokerId, final TriggerEvent event) {
752 return event.getName().equals("done.invoke."+invokerId) ||
753 event.getName().startsWith("done.invoke."+invokerId+".");
754 }
755
756
757
758
759
760
761
762 public boolean isCancelEvent(TriggerEvent event) {
763 return (event.getType() == TriggerEvent.CANCEL_EVENT);
764 }
765
766
767
768
769
770
771
772
773
774
775 public boolean isLegalConfiguration(final Set<EnterableState> states, final ErrorReporter errRep) {
776
777
778
779
780
781
782
783
784 boolean legalConfig = true;
785 Map<EnterableState, Set<EnterableState>> counts = new HashMap<EnterableState, Set<EnterableState>>();
786 Set<EnterableState> scxmlCount = new HashSet<EnterableState>();
787 for (EnterableState es : states) {
788 EnterableState parent;
789 while ((parent = es.getParent()) != null) {
790 Set<EnterableState> cnt = counts.get(parent);
791 if (cnt == null) {
792 cnt = new HashSet<EnterableState>();
793 counts.put(parent, cnt);
794 }
795 cnt.add(es);
796 es = parent;
797 }
798
799 scxmlCount.add(es);
800 }
801 if (scxmlCount.size() > 1) {
802 errRep.onError(ErrorConstants.ILLEGAL_CONFIG, "Multiple top-level OR states active!", scxmlCount);
803 legalConfig = false;
804 }
805 else {
806
807 for (Map.Entry<EnterableState, Set<EnterableState>> entry : counts.entrySet()) {
808 EnterableState es = entry.getKey();
809 Set<EnterableState> count = entry.getValue();
810 if (es instanceof Parallel) {
811 Parallel p = (Parallel) es;
812 if (count.size() < p.getChildren().size()) {
813 errRep.onError(ErrorConstants.ILLEGAL_CONFIG, "Not all AND states active for parallel " + p.getId(), entry);
814 legalConfig = false;
815 }
816 } else {
817 if (count.size() > 1) {
818 errRep.onError(ErrorConstants.ILLEGAL_CONFIG, "Multiple OR states active for state " + es.getId(), entry);
819 legalConfig = false;
820 }
821 }
822 count.clear();
823 }
824 }
825
826 scxmlCount.clear();
827 counts.clear();
828 return legalConfig;
829 }
830
831
832
833
834
835
836
837
838
839
840
841 public void setSystemEventVariable(final SCInstance scInstance, final TriggerEvent event, boolean internal) {
842 Context systemContext = scInstance.getSystemContext();
843 EventVariable eventVar = null;
844 if (event != null) {
845 String eventType = internal ? EventVariable.TYPE_INTERNAL : EventVariable.TYPE_EXTERNAL;
846
847 final int triggerEventType = event.getType();
848 if (triggerEventType == TriggerEvent.ERROR_EVENT || triggerEventType == TriggerEvent.CHANGE_EVENT) {
849 eventType = EventVariable.TYPE_PLATFORM;
850 }
851
852
853 eventVar = new EventVariable(event.getName(), eventType, null, null, null, null, event.getPayload());
854 }
855 systemContext.setLocal(SCXMLSystemContext.EVENT_KEY, eventVar);
856 }
857
858
859
860
861
862
863
864 public void executeGlobalScript(final SCXMLExecutionContext exctx) throws ModelException {
865 Script globalScript = exctx.getStateMachine().getGlobalScript();
866 if ( globalScript != null) {
867 try {
868 globalScript.execute(exctx.getActionExecutionContext());
869 } catch (SCXMLExpressionException e) {
870 exctx.getInternalIOProcessor().addEvent(new TriggerEvent(TriggerEvent.ERROR_EXECUTION, TriggerEvent.ERROR_EVENT));
871 exctx.getErrorReporter().onError(ErrorConstants.EXPRESSION_ERROR, e.getMessage(), exctx.getStateMachine());
872 }
873 }
874 }
875
876
877
878
879
880
881
882
883
884
885
886 public void exitStates(final SCXMLExecutionContext exctx, final Step step,
887 final Set<TransitionalState> statesToInvoke)
888 throws ModelException {
889 if (step.getExitSet().isEmpty()) {
890 return;
891 }
892 ArrayList<EnterableState> exitList = new ArrayList<EnterableState>(step.getExitSet());
893 Collections.sort(exitList, DocumentOrder.reverseDocumentOrderComparator);
894
895 for (EnterableState es : exitList) {
896
897 if (es instanceof TransitionalState && ((TransitionalState)es).hasHistory()) {
898
899 for (History h : ((TransitionalState)es).getHistory()) {
900 exctx.getScInstance().setLastConfiguration(h, step.getNewHistoryConfigurations().get(h));
901 }
902 }
903
904 boolean onexitEventRaised = false;
905 for (OnExit onexit : es.getOnExits()) {
906 executeContent(exctx, onexit);
907 if (!onexitEventRaised && onexit.isRaiseEvent()) {
908 onexitEventRaised = true;
909 exctx.getInternalIOProcessor().addEvent(new TriggerEvent("exit.state."+es.getId(), TriggerEvent.CHANGE_EVENT));
910 }
911 }
912 exctx.getNotificationRegistry().fireOnExit(es, es);
913 exctx.getNotificationRegistry().fireOnExit(exctx.getStateMachine(), es);
914
915 if (es instanceof TransitionalState && !statesToInvoke.remove(es)) {
916
917 for (Invoke inv : ((TransitionalState)es).getInvokes()) {
918 exctx.cancelInvoker(inv);
919 }
920 }
921 exctx.getScInstance().getStateConfiguration().exitState(es);
922 }
923 }
924
925
926
927
928
929
930
931
932 public void executeTransitionContent(final SCXMLExecutionContext exctx, final Step step) throws ModelException {
933 for (SimpleTransition transition : step.getTransitList()) {
934 executeContent(exctx, transition);
935 }
936 }
937
938
939
940
941
942
943
944
945 public void executeContent(SCXMLExecutionContext exctx, Executable exec) throws ModelException {
946 try {
947 for (Action action : exec.getActions()) {
948 action.execute(exctx.getActionExecutionContext());
949 }
950 } catch (SCXMLExpressionException e) {
951 exctx.getInternalIOProcessor().addEvent(new TriggerEvent(TriggerEvent.ERROR_EXECUTION, TriggerEvent.ERROR_EVENT));
952 exctx.getErrorReporter().onError(ErrorConstants.EXPRESSION_ERROR, e.getMessage(), exec);
953 }
954 if (exec instanceof Transition) {
955 Transition t = (Transition)exec;
956 if (t.getTargets().isEmpty()) {
957 notifyOnTransition(exctx, t, t.getParent());
958 }
959 else {
960 for (TransitionTarget tt : t.getTargets()) {
961 notifyOnTransition(exctx, t, tt);
962 }
963 }
964 }
965 }
966
967
968
969
970
971
972
973
974 public void notifyOnTransition(final SCXMLExecutionContext exctx, final Transition t,
975 final TransitionTarget target) {
976 EventVariable event = (EventVariable)exctx.getScInstance().getSystemContext().getVars().get(SCXMLSystemContext.EVENT_KEY);
977 String eventName = event != null ? event.getName() : null;
978 exctx.getNotificationRegistry().fireOnTransition(t, t.getParent(), target, t, eventName);
979 exctx.getNotificationRegistry().fireOnTransition(exctx.getStateMachine(), t.getParent(), target, t, eventName);
980 }
981
982
983
984
985
986
987
988
989
990
991
992 public void enterStates(final SCXMLExecutionContext exctx, final Step step,
993 final Set<TransitionalState> statesToInvoke)
994 throws ModelException {
995 if (step.getEntrySet().isEmpty()) {
996 return;
997 }
998 ArrayList<EnterableState> entryList = new ArrayList<EnterableState>(step.getEntrySet());
999 Collections.sort(entryList, DocumentOrder.documentOrderComparator);
1000 for (EnterableState es : entryList) {
1001 exctx.getScInstance().getStateConfiguration().enterState(es);
1002 if (es instanceof TransitionalState && !((TransitionalState)es).getInvokes().isEmpty()) {
1003 statesToInvoke.add((TransitionalState) es);
1004 }
1005
1006 boolean onentryEventRaised = false;
1007 for (OnEntry onentry : es.getOnEntries()) {
1008 executeContent(exctx, onentry);
1009 if (!onentryEventRaised && onentry.isRaiseEvent()) {
1010 onentryEventRaised = true;
1011 exctx.getInternalIOProcessor().addEvent(new TriggerEvent("entry.state."+es.getId(), TriggerEvent.CHANGE_EVENT));
1012 }
1013 }
1014 exctx.getNotificationRegistry().fireOnEntry(es, es);
1015 exctx.getNotificationRegistry().fireOnEntry(exctx.getStateMachine(), es);
1016
1017 if (es instanceof State && step.getDefaultEntrySet().contains(es) && ((State)es).getInitial() != null) {
1018 executeContent(exctx, ((State)es).getInitial().getTransition());
1019 }
1020 if (es instanceof TransitionalState) {
1021 SimpleTransition hTransition = step.getDefaultHistoryTransitions().get(es);
1022 if (hTransition != null) {
1023 executeContent(exctx, hTransition);
1024 }
1025 }
1026
1027 if (es instanceof Final) {
1028 State parent = (State)es.getParent();
1029 if (parent == null) {
1030 exctx.stopRunning();
1031 }
1032 else {
1033 exctx.getInternalIOProcessor().addEvent(new TriggerEvent("done.state."+parent.getId(),TriggerEvent.CHANGE_EVENT));
1034 if (parent.isRegion()) {
1035 if (isInFinalState(parent.getParent(), exctx.getScInstance().getStateConfiguration().getActiveStates())) {
1036 exctx.getInternalIOProcessor().addEvent(new TriggerEvent("done.state."+parent.getParent().getId()
1037 , TriggerEvent.CHANGE_EVENT));
1038 }
1039 }
1040 }
1041 }
1042 }
1043 }
1044
1045
1046
1047
1048
1049
1050
1051 public void initiateInvokes(final SCXMLExecutionContext exctx,
1052 final Set<TransitionalState> statesToInvoke) throws ModelException {
1053 ActionExecutionContext aexctx = exctx.getActionExecutionContext();
1054 for (TransitionalState ts : statesToInvoke) {
1055 for (Invoke invoke : ts.getInvokes()) {
1056 Context ctx = aexctx.getContext(invoke.getParentEnterableState());
1057 String exctxKey = invoke.getCurrentSCXMLExecutionContextKey();
1058 ctx.setLocal(exctxKey, exctx);
1059 invoke.execute(aexctx);
1060 ctx.setLocal(exctxKey, null);
1061 }
1062 }
1063 }
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073 public void processInvokes(final SCXMLExecutionContext exctx, final TriggerEvent event) throws ModelException {
1074 for (Map.Entry<Invoke, String> entry : exctx.getInvokeIds().entrySet()) {
1075 if (!isInvokerEvent(entry.getValue(), event)) {
1076 if (entry.getKey().isAutoForward()) {
1077 Invoker inv = exctx.getInvoker(entry.getKey());
1078 try {
1079 inv.parentEvent(event);
1080 } catch (InvokerException ie) {
1081 exctx.getAppLog().error(ie.getMessage(), ie);
1082 throw new ModelException(ie.getMessage(), ie.getCause());
1083 }
1084 }
1085 }
1086
1087
1088
1089
1090
1091 }
1092 }
1093 }
1094