1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.scxml.semantics;
18
19 import java.io.Serializable;
20 import java.util.Arrays;
21 import java.util.Collection;
22 import java.util.Collections;
23 import java.util.Comparator;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.Iterator;
27 import java.util.LinkedList;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Set;
31
32 import org.apache.commons.logging.Log;
33 import org.apache.commons.logging.LogFactory;
34 import org.apache.commons.scxml.Context;
35 import org.apache.commons.scxml.ErrorReporter;
36 import org.apache.commons.scxml.Evaluator;
37 import org.apache.commons.scxml.EventDispatcher;
38 import org.apache.commons.scxml.NotificationRegistry;
39 import org.apache.commons.scxml.PathResolver;
40 import org.apache.commons.scxml.SCInstance;
41 import org.apache.commons.scxml.SCXMLExpressionException;
42 import org.apache.commons.scxml.SCXMLHelper;
43 import org.apache.commons.scxml.SCXMLSemantics;
44 import org.apache.commons.scxml.Step;
45 import org.apache.commons.scxml.TriggerEvent;
46 import org.apache.commons.scxml.invoke.Invoker;
47 import org.apache.commons.scxml.invoke.InvokerException;
48 import org.apache.commons.scxml.model.Action;
49 import org.apache.commons.scxml.model.Finalize;
50 import org.apache.commons.scxml.model.History;
51 import org.apache.commons.scxml.model.Initial;
52 import org.apache.commons.scxml.model.Invoke;
53 import org.apache.commons.scxml.model.ModelException;
54 import org.apache.commons.scxml.model.OnEntry;
55 import org.apache.commons.scxml.model.OnExit;
56 import org.apache.commons.scxml.model.Parallel;
57 import org.apache.commons.scxml.model.Param;
58 import org.apache.commons.scxml.model.Path;
59 import org.apache.commons.scxml.model.SCXML;
60 import org.apache.commons.scxml.model.State;
61 import org.apache.commons.scxml.model.Transition;
62 import org.apache.commons.scxml.model.TransitionTarget;
63
64 /***
65 * <p>This class encapsulates a particular SCXML semantics, that is, a
66 * particular semantic interpretation of Harel Statecharts, which aligns
67 * mostly with W3C SCXML July 5 public draft (that is, UML 1.5). However,
68 * certain aspects are taken from STATEMATE.</p>
69 *
70 * <p>Specific semantics can be created by subclassing this class.</p>
71 */
72 public class SCXMLSemanticsImpl implements SCXMLSemantics, Serializable {
73
74 /***
75 * Serial version UID.
76 */
77 private static final long serialVersionUID = 1L;
78
79 /***
80 * SCXML Logger for the application.
81 */
82 private Log appLog = LogFactory.getLog("scxml.app.log");
83
84 /***
85 * The TransitionTarget comparator.
86 */
87 private TransitionTargetComparator targetComparator =
88 new TransitionTargetComparator();
89
90 /***
91 * Current document namespaces are saved under this key in the parent
92 * state's context.
93 */
94 private static final String NAMESPACES_KEY = "_ALL_NAMESPACES";
95
96 /***
97 * @param input
98 * SCXML state machine
99 * @return normalized SCXML state machine, pseudo states are removed, etc.
100 * @param errRep
101 * ErrorReporter callback
102 */
103 public SCXML normalizeStateMachine(final SCXML input,
104 final ErrorReporter errRep) {
105
106 return input;
107 }
108
109 /***
110 * @param input
111 * SCXML state machine [in]
112 * @param states
113 * a set of States to populate [out]
114 * @param entryList
115 * a list of States and Parallels to enter [out]
116 * @param errRep
117 * ErrorReporter callback [inout]
118 * @param scInstance
119 * The state chart instance [in]
120 * @throws ModelException
121 * in case there is a fatal SCXML object model problem.
122 */
123 public void determineInitialStates(final SCXML input, final Set states,
124 final List entryList, final ErrorReporter errRep,
125 final SCInstance scInstance)
126 throws ModelException {
127 State tmp = input.getInitialState();
128 if (tmp == null) {
129 errRep.onError(ErrorConstants.NO_INITIAL,
130 "SCXML initialstate is missing!", input);
131 } else {
132 states.add(tmp);
133 determineTargetStates(states, errRep, scInstance);
134
135 Set onEntry = SCXMLHelper.getAncestorClosure(states, null);
136
137 Object[] oen = onEntry.toArray();
138 onEntry.clear();
139 Arrays.sort(oen, getTTComparator());
140
141 List entering = Arrays.asList(oen);
142 Collections.reverse(entering);
143 entryList.addAll(entering);
144
145 }
146 }
147
148 /***
149 * Executes all OnExit/Transition/OnEntry transitional actions.
150 *
151 * @param step
152 * provides EntryList, TransitList, ExitList gets
153 * updated its AfterStatus/Events
154 * @param stateMachine
155 * state machine - SCXML instance
156 * @param evtDispatcher
157 * the event dispatcher - EventDispatcher instance
158 * @param errRep
159 * error reporter
160 * @param scInstance
161 * The state chart instance
162 * @throws ModelException
163 * in case there is a fatal SCXML object model problem.
164 */
165 public void executeActions(final Step step, final SCXML stateMachine,
166 final EventDispatcher evtDispatcher,
167 final ErrorReporter errRep, final SCInstance scInstance)
168 throws ModelException {
169 NotificationRegistry nr = scInstance.getNotificationRegistry();
170 Collection internalEvents = step.getAfterStatus().getEvents();
171 Map invokers = scInstance.getInvokers();
172
173 for (Iterator i = step.getExitList().iterator(); i.hasNext();) {
174 TransitionTarget tt = (TransitionTarget) i.next();
175 OnExit oe = tt.getOnExit();
176 try {
177 for (Iterator onExitIter = oe.getActions().iterator();
178 onExitIter.hasNext();) {
179 ((Action) onExitIter.next()).execute(evtDispatcher,
180 errRep, scInstance, appLog, internalEvents);
181 }
182 } catch (SCXMLExpressionException e) {
183 errRep.onError(ErrorConstants.EXPRESSION_ERROR, e.getMessage(),
184 oe);
185 }
186
187 if (invokers.containsKey(tt)) {
188 Invoker toCancel = (Invoker) invokers.get(tt);
189 try {
190 toCancel.cancel();
191 } catch (InvokerException ie) {
192 TriggerEvent te = new TriggerEvent(tt.getId()
193 + ".invoke.cancel.failed", TriggerEvent.ERROR_EVENT);
194 internalEvents.add(te);
195 }
196
197 invokers.remove(tt);
198 }
199 nr.fireOnExit(tt, tt);
200 nr.fireOnExit(stateMachine, tt);
201 TriggerEvent te = new TriggerEvent(tt.getId() + ".exit",
202 TriggerEvent.CHANGE_EVENT);
203 internalEvents.add(te);
204 }
205
206 for (Iterator i = step.getTransitList().iterator(); i.hasNext();) {
207 Transition t = (Transition) i.next();
208 try {
209 for (Iterator transitIter = t.getActions().iterator();
210 transitIter.hasNext();) {
211 ((Action) transitIter.next()).execute(evtDispatcher,
212 errRep, scInstance, appLog, internalEvents);
213 }
214 } catch (SCXMLExpressionException e) {
215 errRep.onError(ErrorConstants.EXPRESSION_ERROR,
216 e.getMessage(), t);
217 }
218 nr.fireOnTransition(t, t.getParent(), t.getRuntimeTarget(), t);
219 nr.fireOnTransition(stateMachine, t.getParent(),
220 t.getRuntimeTarget(), t);
221 }
222
223 for (Iterator i = step.getEntryList().iterator(); i.hasNext();) {
224 TransitionTarget tt = (TransitionTarget) i.next();
225 OnEntry oe = tt.getOnEntry();
226 try {
227 for (Iterator onEntryIter = oe.getActions().iterator();
228 onEntryIter.hasNext();) {
229 ((Action) onEntryIter.next()).execute(evtDispatcher,
230 errRep, scInstance, appLog, internalEvents);
231 }
232 } catch (SCXMLExpressionException e) {
233 errRep.onError(ErrorConstants.EXPRESSION_ERROR, e.getMessage(),
234 oe);
235 }
236 nr.fireOnEntry(tt, tt);
237 nr.fireOnEntry(stateMachine, tt);
238 TriggerEvent te = new TriggerEvent(tt.getId() + ".entry",
239 TriggerEvent.CHANGE_EVENT);
240 internalEvents.add(te);
241
242 if (tt instanceof State) {
243 State ts = (State) tt;
244 if (ts.getIsFinal()) {
245 State parent = (State) ts.getParent();
246 String prefix = "";
247 if (parent != null) {
248 prefix = parent.getId();
249 }
250 te = new TriggerEvent(prefix + ".done",
251 TriggerEvent.CHANGE_EVENT);
252 internalEvents.add(te);
253 if (parent != null) {
254 parent.setDone(true);
255 }
256 if (parent != null && parent.isRegion()) {
257
258
259 Parallel p = (Parallel) parent.getParent();
260 int finCount = 0;
261 int pCount = p.getStates().size();
262 for (Iterator regions = p.getStates().iterator();
263 regions.hasNext();) {
264 State reg = (State) regions.next();
265 if (reg.isDone()) {
266 finCount++;
267 }
268 }
269 if (finCount == pCount) {
270 te = new TriggerEvent(p.getId() + ".done",
271 TriggerEvent.CHANGE_EVENT);
272 internalEvents.add(te);
273 te = new TriggerEvent(p.getParent().getId()
274 + ".done", TriggerEvent.CHANGE_EVENT);
275 internalEvents.add(te);
276
277 p.getParentState().setDone(true);
278 }
279 }
280 }
281 }
282 }
283 }
284
285 /***
286 * @param stateMachine
287 * a SM to traverse [in]
288 * @param step
289 * with current status and list of transitions to populate
290 * [inout]
291 * @param errRep
292 * ErrorReporter callback [inout]
293 */
294 public void enumerateReachableTransitions(final SCXML stateMachine,
295 final Step step, final ErrorReporter errRep) {
296
297 Set transSet = new HashSet();
298
299 Set stateSet = new HashSet(step.getBeforeStatus().getStates());
300
301 LinkedList todoList = new LinkedList(stateSet);
302 while (!todoList.isEmpty()) {
303 State st = (State) todoList.removeFirst();
304 for (Iterator i = st.getTransitionsList().iterator();
305 i.hasNext();) {
306 Transition t = (Transition) i.next();
307 if (!transSet.contains(t)) {
308 transSet.add(t);
309 step.getTransitList().add(t);
310 }
311 }
312 State parent = st.getParentState();
313 if (parent != null && !stateSet.contains(parent)) {
314 stateSet.add(parent);
315 todoList.addLast(parent);
316 }
317 }
318 transSet.clear();
319 stateSet.clear();
320 todoList.clear();
321 }
322
323 /***
324 * @param step
325 * [inout]
326 * @param evtDispatcher
327 * The {@link EventDispatcher} [in]
328 * @param errRep
329 * ErrorReporter callback [inout]
330 * @param scInstance
331 * The state chart instance [in]
332 * @throws ModelException
333 * in case there is a fatal SCXML object model problem.
334 */
335 public void filterTransitionsSet(final Step step,
336 final EventDispatcher evtDispatcher,
337 final ErrorReporter errRep, final SCInstance scInstance)
338 throws ModelException {
339
340
341
342
343
344
345
346 Set allEvents = new HashSet(step.getBeforeStatus().getEvents().size()
347 + step.getExternalEvents().size());
348
349 for (Iterator ei = step.getBeforeStatus().getEvents().iterator();
350 ei.hasNext();) {
351 TriggerEvent te = (TriggerEvent) ei.next();
352 allEvents.add(te.getName());
353 }
354 for (Iterator ei = step.getExternalEvents().iterator();
355 ei.hasNext();) {
356 TriggerEvent te = (TriggerEvent) ei.next();
357 allEvents.add(te.getName());
358 }
359
360 for (Iterator iter = scInstance.getInvokers().keySet().iterator();
361 iter.hasNext();) {
362 State s = (State) iter.next();
363 if (finalizeMatch(s.getId(), allEvents)) {
364 Finalize fn = s.getInvoke().getFinalize();
365 if (fn != null) {
366 try {
367 for (Iterator fnIter = fn.getActions().iterator();
368 fnIter.hasNext();) {
369 ((Action) fnIter.next()).execute(evtDispatcher,
370 errRep, scInstance, appLog,
371 step.getAfterStatus().getEvents());
372 }
373 } catch (SCXMLExpressionException e) {
374 errRep.onError(ErrorConstants.EXPRESSION_ERROR,
375 e.getMessage(), fn);
376 }
377 }
378 }
379 }
380
381 List removeList = new LinkedList();
382
383 for (Iterator iter = step.getTransitList().iterator();
384 iter.hasNext();) {
385 Transition t = (Transition) iter.next();
386
387 String event = t.getEvent();
388 if (!eventMatch(event, allEvents)) {
389
390 removeList.add(t);
391 continue;
392 }
393
394 Boolean rslt;
395 String expr = t.getCond();
396 if (SCXMLHelper.isStringEmpty(expr)) {
397 rslt = Boolean.TRUE;
398 } else {
399 try {
400 Context ctx = scInstance.getContext(t.getParent());
401 ctx.setLocal(NAMESPACES_KEY, t.getNamespaces());
402 rslt = scInstance.getEvaluator().evalCond(ctx,
403 t.getCond());
404 ctx.setLocal(NAMESPACES_KEY, null);
405 } catch (SCXMLExpressionException e) {
406 rslt = Boolean.FALSE;
407 errRep.onError(ErrorConstants.EXPRESSION_ERROR, e
408 .getMessage(), t);
409 }
410 }
411 if (!rslt.booleanValue()) {
412
413 removeList.add(t);
414 }
415 }
416
417 step.getTransitList().removeAll(removeList);
418
419 allEvents.clear();
420 removeList.clear();
421
422
423 if (step.getTransitList().size() > 1) {
424
425 Object[] trans = step.getTransitList().toArray();
426 Set currentStates = step.getBeforeStatus().getStates();
427
428 Set nonDeterm = new HashSet();
429 for (int i = 0; i < trans.length; i++) {
430 Transition t = (Transition) trans[i];
431 TransitionTarget tsrc = t.getParent();
432 for (int j = i + 1; j < trans.length; j++) {
433 Transition t2 = (Transition) trans[j];
434 boolean conflict = SCXMLHelper.inConflict(t, t2,
435 currentStates);
436 if (conflict) {
437
438 TransitionTarget t2src = t2.getParent();
439 if (SCXMLHelper.isDescendant(t2src, tsrc)) {
440
441 removeList.add(t);
442 break;
443 } else if (SCXMLHelper.isDescendant(tsrc, t2src)) {
444
445 removeList.add(t2);
446 } else {
447
448 nonDeterm.add(t);
449 nonDeterm.add(t2);
450 }
451 }
452 }
453 }
454
455 nonDeterm.removeAll(removeList);
456 if (nonDeterm.size() > 0) {
457 errRep.onError(ErrorConstants.NON_DETERMINISTIC,
458 "Multiple conflicting transitions enabled.", nonDeterm);
459 }
460
461 step.getTransitList().removeAll(removeList);
462 removeList.clear();
463 nonDeterm.clear();
464 }
465 }
466
467 /***
468 * Populate the target set.
469 * <ul>
470 * <li>take targets of selected transitions</li>
471 * <li>take exited regions into account and make sure every active
472 * parallel region has all siblings active
473 * [that is, explicitly visit or sibling regions in case of newly visited
474 * (revisited) orthogonal states]</li>
475 * </ul>
476 * @param residual [in]
477 * @param transitList [in]
478 * @param errRep
479 * ErrorReporter callback [inout]
480 * @return Set The target set
481 */
482 public Set seedTargetSet(final Set residual, final List transitList,
483 final ErrorReporter errRep) {
484 Set seedSet = new HashSet();
485 Set regions = new HashSet();
486 for (Iterator i = transitList.iterator(); i.hasNext();) {
487 Transition t = (Transition) i.next();
488
489 if (t.getTarget() != null) {
490 seedSet.add(t.getTarget());
491 }
492
493 Path p = t.getPath();
494 if (p.isCrossRegion()) {
495 List regs = p.getRegionsEntered();
496 for (Iterator j = regs.iterator(); j.hasNext();) {
497 State region = (State) j.next();
498 regions.addAll(((Parallel) region.getParent()).
499 getStates());
500 }
501 }
502 }
503
504 Set allStates = new HashSet(residual);
505 allStates.addAll(seedSet);
506 allStates = SCXMLHelper.getAncestorClosure(allStates, null);
507 regions.removeAll(allStates);
508
509 for (Iterator i = regions.iterator(); i.hasNext();) {
510 State reg = (State) i.next();
511 seedSet.add(reg);
512 }
513 return seedSet;
514 }
515
516 /***
517 * @param states
518 * a set seeded in previous step [inout]
519 * @param errRep
520 * ErrorReporter callback [inout]
521 * @param scInstance
522 * The state chart instance [in]
523 * @throws ModelException On illegal configuration
524 * @see #seedTargetSet(Set, List, ErrorReporter)
525 */
526 public void determineTargetStates(final Set states,
527 final ErrorReporter errRep, final SCInstance scInstance)
528 throws ModelException {
529 LinkedList wrkSet = new LinkedList(states);
530
531 states.clear();
532 while (!wrkSet.isEmpty()) {
533 TransitionTarget tt = (TransitionTarget) wrkSet.removeFirst();
534 if (tt instanceof State) {
535 State st = (State) tt;
536
537
538
539 if (st.isSimple()) {
540 states.add(st);
541 } else if (st.isOrthogonal()) {
542 wrkSet.addLast(st.getParallel());
543 } else {
544
545 Initial ini = st.getInitial();
546 if (ini == null) {
547 errRep.onError(ErrorConstants.NO_INITIAL,
548 "Initial pseudostate is missing!", st);
549 } else {
550
551
552 Transition initialTransition = ini.getTransition();
553 if (initialTransition == null) {
554 errRep.onError(ErrorConstants.ILLEGAL_INITIAL,
555 "Initial transition is null!", st);
556 } else {
557 TransitionTarget init = initialTransition.
558 getTarget();
559 if (init == null
560 ||
561 !(init instanceof State
562 || init instanceof History)) {
563 errRep.onError(ErrorConstants.ILLEGAL_INITIAL,
564 "Initial not pointing to a State or History!",
565 st);
566 } else {
567 wrkSet.addLast(init);
568 }
569 }
570 }
571 }
572 } else if (tt instanceof Parallel) {
573 Parallel prl = (Parallel) tt;
574 for (Iterator i = prl.getStates().iterator(); i.hasNext();) {
575
576 wrkSet.addLast(i.next());
577 }
578 } else if (tt instanceof History) {
579 History h = (History) tt;
580 if (scInstance.isEmpty(h)) {
581 wrkSet.addLast(h.getTransition().getRuntimeTarget());
582 } else {
583 wrkSet.addAll(scInstance.getLastConfiguration(h));
584 }
585 } else {
586 throw new ModelException("Unknown TransitionTarget subclass:"
587 + tt.getClass().getName());
588 }
589 }
590 }
591
592 /***
593 * Go over the exit list and update history information for
594 * relevant states.
595 *
596 * @param step
597 * [inout]
598 * @param errRep
599 * ErrorReporter callback [inout]
600 * @param scInstance
601 * The state chart instance [inout]
602 */
603 public void updateHistoryStates(final Step step,
604 final ErrorReporter errRep, final SCInstance scInstance) {
605 Set oldState = step.getBeforeStatus().getStates();
606 for (Iterator i = step.getExitList().iterator(); i.hasNext();) {
607 Object o = i.next();
608 if (o instanceof State) {
609 State s = (State) o;
610 if (s.hasHistory()) {
611 Set shallow = null;
612 Set deep = null;
613 for (Iterator j = s.getHistory().iterator();
614 j.hasNext();) {
615 History h = (History) j.next();
616 if (h.isDeep()) {
617 if (deep == null) {
618
619 deep = new HashSet();
620 Iterator k = oldState.iterator();
621 while (k.hasNext()) {
622 State os = (State) k.next();
623 if (SCXMLHelper.isDescendant(os, s)) {
624 deep.add(os);
625 }
626 }
627 }
628 scInstance.setLastConfiguration(h, deep);
629 } else {
630 if (shallow == null) {
631
632
633 shallow = new HashSet();
634 shallow.addAll(s.getChildren().values());
635 shallow.retainAll(SCXMLHelper
636 .getAncestorClosure(oldState, null));
637 }
638 scInstance.setLastConfiguration(h, shallow);
639 }
640 }
641 shallow = null;
642 deep = null;
643 }
644 }
645 }
646 }
647
648 /***
649 * Follow the candidate transitions for this execution Step, and update the
650 * lists of entered and exited states accordingly.
651 *
652 * @param step The current Step
653 * @param errorReporter The ErrorReporter for the current environment
654 * @param scInstance The state chart instance
655 *
656 * @throws ModelException
657 * in case there is a fatal SCXML object model problem.
658 */
659 public void followTransitions(final Step step,
660 final ErrorReporter errorReporter, final SCInstance scInstance)
661 throws ModelException {
662 Set currentStates = step.getBeforeStatus().getStates();
663 List transitions = step.getTransitList();
664
665 Set exitedStates = new HashSet();
666 for (Iterator i = transitions.iterator(); i.hasNext();) {
667 Transition t = (Transition) i.next();
668 Set ext = SCXMLHelper.getStatesExited(t, currentStates);
669 exitedStates.addAll(ext);
670 }
671
672 Set residual = new HashSet(currentStates);
673 residual.removeAll(exitedStates);
674
675 Set seedSet = seedTargetSet(residual, transitions, errorReporter);
676
677 Set targetSet = step.getAfterStatus().getStates();
678 targetSet.addAll(seedSet);
679 determineTargetStates(targetSet, errorReporter, scInstance);
680
681 Set entered = SCXMLHelper.getAncestorClosure(targetSet, seedSet);
682 seedSet.clear();
683 for (Iterator i = transitions.iterator(); i.hasNext();) {
684 Transition t = (Transition) i.next();
685 entered.addAll(t.getPath().getDownwardSegment());
686
687 if (t.getRuntimeTarget() instanceof History) {
688 entered.remove(t.getRuntimeTarget());
689 }
690 }
691
692 targetSet.addAll(residual);
693 residual.clear();
694 if (!SCXMLHelper.isLegalConfig(targetSet, errorReporter)) {
695 throw new ModelException("Illegal state machine configuration!");
696 }
697
698 Object[] oex = exitedStates.toArray();
699 exitedStates.clear();
700 Object[] oen = entered.toArray();
701 entered.clear();
702 Arrays.sort(oex, getTTComparator());
703 Arrays.sort(oen, getTTComparator());
704 step.getExitList().addAll(Arrays.asList(oex));
705
706 List entering = Arrays.asList(oen);
707 Collections.reverse(entering);
708 step.getEntryList().addAll(entering);
709
710 for (Iterator reset = entering.iterator(); reset.hasNext();) {
711 Object o = reset.next();
712 if (o instanceof State) {
713 ((State) o).setDone(false);
714 }
715 }
716 }
717 /***
718 * Process any existing invokes, includes forwarding external events,
719 * and executing any finalize handlers.
720 *
721 * @param events
722 * The events to be forwarded
723 * @param errRep
724 * ErrorReporter callback
725 * @param scInstance
726 * The state chart instance
727 * @throws ModelException
728 * in case there is a fatal SCXML object model problem.
729 */
730 public void processInvokes(final TriggerEvent[] events,
731 final ErrorReporter errRep, final SCInstance scInstance)
732 throws ModelException {
733 Set eventNames = new HashSet();
734
735 for (int i = 0; i < events.length; i++) {
736 eventNames.add(events[i].getName());
737 }
738 for (Iterator invokeIter = scInstance.getInvokers().entrySet().
739 iterator(); invokeIter.hasNext();) {
740 Map.Entry iEntry = (Map.Entry) invokeIter.next();
741 String parentId = ((TransitionTarget) iEntry.getKey()).getId();
742 if (!finalizeMatch(parentId, eventNames)) {
743 Invoker inv = (Invoker) iEntry.getValue();
744 try {
745 inv.parentEvents(events);
746 } catch (InvokerException ie) {
747 appLog.error(ie.getMessage(), ie);
748 throw new ModelException(ie.getMessage(), ie.getCause());
749 }
750 }
751 }
752 }
753
754 /***
755 * Initiate any new invokes.
756 *
757 * @param step
758 * The current Step
759 * @param errRep
760 * ErrorReporter callback
761 * @param scInstance
762 * The state chart instance
763 */
764 public void initiateInvokes(final Step step, final ErrorReporter errRep,
765 final SCInstance scInstance) {
766 Evaluator eval = scInstance.getEvaluator();
767 Collection internalEvents = step.getAfterStatus().getEvents();
768 for (Iterator iter = step.getAfterStatus().getStates().iterator();
769 iter.hasNext();) {
770 State s = (State) iter.next();
771 Context ctx = scInstance.getContext(s);
772 Invoke i = s.getInvoke();
773 if (i != null && scInstance.getInvoker(s) == null) {
774 String src = i.getSrc();
775 if (src == null) {
776 String srcexpr = i.getSrcexpr();
777 Object srcObj = null;
778 try {
779 ctx.setLocal(NAMESPACES_KEY, i.getNamespaces());
780 srcObj = eval.eval(ctx, srcexpr);
781 ctx.setLocal(NAMESPACES_KEY, null);
782 src = String.valueOf(srcObj);
783 } catch (SCXMLExpressionException see) {
784 errRep.onError(ErrorConstants.EXPRESSION_ERROR,
785 see.getMessage(), i);
786 }
787 }
788 String source = src;
789 PathResolver pr = i.getPathResolver();
790 if (pr != null) {
791 source = i.getPathResolver().resolvePath(src);
792 }
793 String ttype = i.getTargettype();
794 Invoker inv = null;
795 try {
796 inv = scInstance.newInvoker(ttype);
797 } catch (InvokerException ie) {
798 TriggerEvent te = new TriggerEvent(s.getId()
799 + ".invoke.failed", TriggerEvent.ERROR_EVENT);
800 internalEvents.add(te);
801 continue;
802 }
803 inv.setParentStateId(s.getId());
804 inv.setSCInstance(scInstance);
805 List params = i.params();
806 Map args = new HashMap();
807 for (Iterator pIter = params.iterator(); pIter.hasNext();) {
808 Param p = (Param) pIter.next();
809 String argExpr = p.getExpr();
810 Object argValue = null;
811 if (argExpr != null && argExpr.trim().length() > 0) {
812 try {
813 ctx.setLocal(NAMESPACES_KEY, p.getNamespaces());
814 argValue = eval.eval(ctx, argExpr);
815 ctx.setLocal(NAMESPACES_KEY, null);
816 } catch (SCXMLExpressionException see) {
817 errRep.onError(ErrorConstants.EXPRESSION_ERROR,
818 see.getMessage(), i);
819 }
820 }
821 args.put(p.getName(), argValue);
822 }
823 try {
824 inv.invoke(source, args);
825 } catch (InvokerException ie) {
826 TriggerEvent te = new TriggerEvent(s.getId()
827 + ".invoke.failed", TriggerEvent.ERROR_EVENT);
828 internalEvents.add(te);
829 continue;
830 }
831 scInstance.setInvoker(s, inv);
832 }
833 }
834 }
835
836 /***
837 * Implements prefix match, that is, if, for example,
838 * "mouse.click" is a member of eventOccurrences and a
839 * transition is triggered by "mouse", the method returns true.
840 *
841 * @param transEvent
842 * a trigger event of a transition
843 * @param eventOccurrences
844 * current events
845 * @return true/false
846 */
847 protected boolean eventMatch(final String transEvent,
848 final Set eventOccurrences) {
849 if (SCXMLHelper.isStringEmpty(transEvent)) {
850 return true;
851 } else {
852 String transEventDot = transEvent + ".";
853 Iterator i = eventOccurrences.iterator();
854 while (i.hasNext()) {
855 String evt = (String) i.next();
856 if (evt == null) {
857 continue;
858 } else if (evt.equals("*")) {
859 return true;
860 } else if (evt.equals(transEvent)
861 || evt.startsWith(transEventDot)) {
862 return true;
863 }
864 }
865 return false;
866 }
867 }
868
869 /***
870 * Implements event prefix match to ascertain <finalize> execution.
871 *
872 * @param parentStateId
873 * the ID of the parent state of the <invoke> holding
874 * the <finalize>
875 * @param eventOccurrences
876 * current events
877 * @return true/false
878 */
879 protected boolean finalizeMatch(final String parentStateId,
880 final Set eventOccurrences) {
881 String prefix = parentStateId + ".invoke.";
882 Iterator i = eventOccurrences.iterator();
883 while (i.hasNext()) {
884 String evt = (String) i.next();
885 if (evt == null) {
886 continue;
887 } else if (evt.startsWith(prefix)) {
888 return true;
889 }
890 }
891 return false;
892 }
893
894 /***
895 * TransitionTargetComparator factory method.
896 * @return Comparator The TransitionTarget comparator
897 */
898 protected Comparator getTTComparator() {
899 return targetComparator;
900 }
901
902 /***
903 * Set the log used by this <code>SCXMLSemantics</code> instance.
904 *
905 * @param log The new log.
906 */
907 protected void setLog(final Log log) {
908 this.appLog = log;
909 }
910
911 /***
912 * Get the log used by this <code>SCXMLSemantics</code> instance.
913 *
914 * @return Log The log being used.
915 */
916 protected Log getLog() {
917 return appLog;
918 }
919
920 }
921