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