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.LinkedHashSet;
28 import java.util.LinkedList;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Set;
32
33 import org.apache.commons.logging.Log;
34 import org.apache.commons.logging.LogFactory;
35 import org.apache.commons.scxml.Context;
36 import org.apache.commons.scxml.ErrorReporter;
37 import org.apache.commons.scxml.Evaluator;
38 import org.apache.commons.scxml.EventDispatcher;
39 import org.apache.commons.scxml.NotificationRegistry;
40 import org.apache.commons.scxml.PathResolver;
41 import org.apache.commons.scxml.SCInstance;
42 import org.apache.commons.scxml.SCXMLExpressionException;
43 import org.apache.commons.scxml.SCXMLHelper;
44 import org.apache.commons.scxml.SCXMLSemantics;
45 import org.apache.commons.scxml.Step;
46 import org.apache.commons.scxml.TriggerEvent;
47 import org.apache.commons.scxml.invoke.Invoker;
48 import org.apache.commons.scxml.invoke.InvokerException;
49 import org.apache.commons.scxml.model.Action;
50 import org.apache.commons.scxml.model.Finalize;
51 import org.apache.commons.scxml.model.History;
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 targets
113 * a set of initial targets 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 targets,
124 final List entryList, final ErrorReporter errRep,
125 final SCInstance scInstance)
126 throws ModelException {
127 TransitionTarget tmp = input.getInitialTarget();
128 if (tmp == null) {
129 errRep.onError(ErrorConstants.NO_INITIAL,
130 "SCXML initialstate is missing!", input);
131 } else {
132 targets.add(tmp);
133 determineTargetStates(targets, errRep, scInstance);
134
135 Set onEntry = SCXMLHelper.getAncestorClosure(targets, 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 List rtargets = t.getRuntimeTargets();
219 for (int j = 0; j < rtargets.size(); j++) {
220 TransitionTarget tt = (TransitionTarget) rtargets.get(j);
221 nr.fireOnTransition(t, t.getParent(), tt, t);
222 nr.fireOnTransition(stateMachine, t.getParent(), tt, t);
223 }
224 }
225
226 for (Iterator i = step.getEntryList().iterator(); i.hasNext();) {
227 TransitionTarget tt = (TransitionTarget) i.next();
228 OnEntry oe = tt.getOnEntry();
229 try {
230 for (Iterator onEntryIter = oe.getActions().iterator();
231 onEntryIter.hasNext();) {
232 ((Action) onEntryIter.next()).execute(evtDispatcher,
233 errRep, scInstance, appLog, internalEvents);
234 }
235 } catch (SCXMLExpressionException e) {
236 errRep.onError(ErrorConstants.EXPRESSION_ERROR, e.getMessage(),
237 oe);
238 }
239 nr.fireOnEntry(tt, tt);
240 nr.fireOnEntry(stateMachine, tt);
241 TriggerEvent te = new TriggerEvent(tt.getId() + ".entry",
242 TriggerEvent.CHANGE_EVENT);
243 internalEvents.add(te);
244
245 if (tt instanceof State) {
246 State ts = (State) tt;
247 if (ts.isFinal()) {
248 State parent = (State) ts.getParent();
249 String prefix = "";
250 if (parent != null) {
251 prefix = parent.getId();
252 }
253 te = new TriggerEvent(prefix + ".done",
254 TriggerEvent.CHANGE_EVENT);
255 internalEvents.add(te);
256 if (parent != null) {
257 scInstance.setDone(parent, true);
258 }
259 if (parent != null && parent.isRegion()) {
260
261
262 Parallel p = (Parallel) parent.getParent();
263 int finCount = 0;
264 int pCount = p.getChildren().size();
265 for (Iterator regions = p.getChildren().iterator();
266 regions.hasNext();) {
267 State reg = (State) regions.next();
268 if (scInstance.isDone(reg)) {
269 finCount++;
270 }
271 }
272 if (finCount == pCount) {
273 te = new TriggerEvent(p.getId() + ".done",
274 TriggerEvent.CHANGE_EVENT);
275 internalEvents.add(te);
276 te = new TriggerEvent(p.getParent().getId()
277 + ".done", TriggerEvent.CHANGE_EVENT);
278 internalEvents.add(te);
279
280 scInstance.setDone(p.getParentState(), true);
281 }
282 }
283 }
284 }
285 }
286 }
287
288 /***
289 * @param stateMachine
290 * a SM to traverse [in]
291 * @param step
292 * with current status and list of transitions to populate
293 * [inout]
294 * @param errRep
295 * ErrorReporter callback [inout]
296 */
297 public void enumerateReachableTransitions(final SCXML stateMachine,
298 final Step step, final ErrorReporter errRep) {
299
300 Set transSet = new HashSet();
301
302 Set stateSet = new HashSet(step.getBeforeStatus().getStates());
303
304 LinkedList todoList = new LinkedList(stateSet);
305 while (!todoList.isEmpty()) {
306 State st = (State) todoList.removeFirst();
307 for (Iterator i = st.getTransitionsList().iterator();
308 i.hasNext();) {
309 Transition t = (Transition) i.next();
310 if (!transSet.contains(t)) {
311 transSet.add(t);
312 step.getTransitList().add(t);
313 }
314 }
315 State parent = st.getParentState();
316 if (parent != null && !stateSet.contains(parent)) {
317 stateSet.add(parent);
318 todoList.addLast(parent);
319 }
320 }
321 transSet.clear();
322 stateSet.clear();
323 todoList.clear();
324 }
325
326 /***
327 * @param step
328 * [inout]
329 * @param evtDispatcher
330 * The {@link EventDispatcher} [in]
331 * @param errRep
332 * ErrorReporter callback [inout]
333 * @param scInstance
334 * The state chart instance [in]
335 * @throws ModelException
336 * in case there is a fatal SCXML object model problem.
337 */
338 public void filterTransitionsSet(final Step step,
339 final EventDispatcher evtDispatcher,
340 final ErrorReporter errRep, final SCInstance scInstance)
341 throws ModelException {
342
343
344
345
346
347
348
349 Set allEvents = new HashSet(step.getBeforeStatus().getEvents().size()
350 + step.getExternalEvents().size());
351 allEvents.addAll(step.getBeforeStatus().getEvents());
352 allEvents.addAll(step.getExternalEvents());
353
354 for (Iterator iter = scInstance.getInvokers().keySet().iterator();
355 iter.hasNext();) {
356 State s = (State) iter.next();
357 if (finalizeMatch(s.getId(), allEvents)) {
358 Finalize fn = s.getInvoke().getFinalize();
359 if (fn != null) {
360 try {
361 for (Iterator fnIter = fn.getActions().iterator();
362 fnIter.hasNext();) {
363 ((Action) fnIter.next()).execute(evtDispatcher,
364 errRep, scInstance, appLog,
365 step.getAfterStatus().getEvents());
366 }
367 } catch (SCXMLExpressionException e) {
368 errRep.onError(ErrorConstants.EXPRESSION_ERROR,
369 e.getMessage(), fn);
370 }
371 }
372 }
373 }
374
375 List removeList = new LinkedList();
376
377 for (Iterator iter = step.getTransitList().iterator();
378 iter.hasNext();) {
379 Transition t = (Transition) iter.next();
380
381 String event = t.getEvent();
382 if (!eventMatch(event, allEvents)) {
383
384 removeList.add(t);
385 continue;
386 }
387
388 Boolean rslt;
389 String expr = t.getCond();
390 if (SCXMLHelper.isStringEmpty(expr)) {
391 rslt = Boolean.TRUE;
392 } else {
393 try {
394 Context ctx = scInstance.getContext(t.getParent());
395 ctx.setLocal(NAMESPACES_KEY, t.getNamespaces());
396 rslt = scInstance.getEvaluator().evalCond(ctx,
397 t.getCond());
398 ctx.setLocal(NAMESPACES_KEY, null);
399 } catch (SCXMLExpressionException e) {
400 rslt = Boolean.FALSE;
401 errRep.onError(ErrorConstants.EXPRESSION_ERROR, e
402 .getMessage(), t);
403 }
404 }
405 if (!rslt.booleanValue()) {
406
407 removeList.add(t);
408 }
409 }
410
411 step.getTransitList().removeAll(removeList);
412
413 allEvents.clear();
414 removeList.clear();
415
416
417 if (step.getTransitList().size() > 1) {
418
419 Object[] trans = step.getTransitList().toArray();
420
421 Set nonDeterm = new LinkedHashSet();
422 for (int i = 0; i < trans.length; i++) {
423 Transition t = (Transition) trans[i];
424 TransitionTarget tsrc = t.getParent();
425 for (int j = i + 1; j < trans.length; j++) {
426 Transition t2 = (Transition) trans[j];
427 TransitionTarget t2src = t2.getParent();
428 if (SCXMLHelper.isDescendant(t2src, tsrc)) {
429
430 removeList.add(t);
431 break;
432 } else if (SCXMLHelper.isDescendant(tsrc, t2src)) {
433
434 removeList.add(t2);
435 } else {
436
437 nonDeterm.add(t);
438 nonDeterm.add(t2);
439 }
440 }
441 }
442
443 nonDeterm.removeAll(removeList);
444 if (nonDeterm.size() > 0) {
445
446
447 Set regions = new HashSet();
448 Iterator iter = nonDeterm.iterator();
449 while (iter.hasNext()) {
450 Transition t = (Transition) iter.next();
451 TransitionTarget parent = t.getParent();
452 if (regions.contains(parent)) {
453 removeList.add(t);
454 } else {
455 regions.add(parent);
456 }
457 }
458 }
459
460 step.getTransitList().removeAll(removeList);
461 }
462 }
463
464 /***
465 * Populate the target set.
466 * <ul>
467 * <li>take targets of selected transitions</li>
468 * <li>take exited regions into account and make sure every active
469 * parallel region has all siblings active
470 * [that is, explicitly visit or sibling regions in case of newly visited
471 * (revisited) orthogonal states]</li>
472 * </ul>
473 * @param residual [in]
474 * @param transitList [in]
475 * @param errRep
476 * ErrorReporter callback [inout]
477 * @return Set The target set
478 */
479 public Set seedTargetSet(final Set residual, final List transitList,
480 final ErrorReporter errRep) {
481 Set seedSet = new HashSet();
482 Set regions = new HashSet();
483 for (Iterator i = transitList.iterator(); i.hasNext();) {
484 Transition t = (Transition) i.next();
485
486 if (t.getTargets().size() > 0) {
487 seedSet.addAll(t.getTargets());
488 }
489
490 List paths = t.getPaths();
491 for (int j = 0; j < paths.size(); j++) {
492 Path p = (Path) paths.get(j);
493 if (p.isCrossRegion()) {
494 List regs = p.getRegionsEntered();
495 for (Iterator k = regs.iterator(); k.hasNext();) {
496 State region = (State) k.next();
497 regions.addAll(((Parallel) region.getParent()).
498 getChildren());
499 }
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 List initialStates = st.getInitial().getTransition().
546 getTargets();
547 wrkSet.addAll(initialStates);
548 }
549 } else if (tt instanceof Parallel) {
550 Parallel prl = (Parallel) tt;
551 for (Iterator i = prl.getChildren().iterator(); i.hasNext();) {
552
553 wrkSet.addLast(i.next());
554 }
555 } else if (tt instanceof History) {
556 History h = (History) tt;
557 if (scInstance.isEmpty(h)) {
558 wrkSet.addAll(h.getTransition().getRuntimeTargets());
559 } else {
560 wrkSet.addAll(scInstance.getLastConfiguration(h));
561 }
562 } else {
563 throw new ModelException("Unknown TransitionTarget subclass:"
564 + tt.getClass().getName());
565 }
566 }
567 }
568
569 /***
570 * Go over the exit list and update history information for
571 * relevant states.
572 *
573 * @param step
574 * [inout]
575 * @param errRep
576 * ErrorReporter callback [inout]
577 * @param scInstance
578 * The state chart instance [inout]
579 */
580 public void updateHistoryStates(final Step step,
581 final ErrorReporter errRep, final SCInstance scInstance) {
582 Set oldState = step.getBeforeStatus().getStates();
583 for (Iterator i = step.getExitList().iterator(); i.hasNext();) {
584 Object o = i.next();
585 if (o instanceof State) {
586 State s = (State) o;
587 if (s.hasHistory()) {
588 Set shallow = null;
589 Set deep = null;
590 for (Iterator j = s.getHistory().iterator();
591 j.hasNext();) {
592 History h = (History) j.next();
593 if (h.isDeep()) {
594 if (deep == null) {
595
596 deep = new HashSet();
597 Iterator k = oldState.iterator();
598 while (k.hasNext()) {
599 State os = (State) k.next();
600 if (SCXMLHelper.isDescendant(os, s)) {
601 deep.add(os);
602 }
603 }
604 }
605 scInstance.setLastConfiguration(h, deep);
606 } else {
607 if (shallow == null) {
608
609
610 shallow = new HashSet();
611 shallow.addAll(s.getChildren().values());
612 shallow.retainAll(SCXMLHelper
613 .getAncestorClosure(oldState, null));
614 }
615 scInstance.setLastConfiguration(h, shallow);
616 }
617 }
618 shallow = null;
619 deep = null;
620 }
621 }
622 }
623 }
624
625 /***
626 * Follow the candidate transitions for this execution Step, and update the
627 * lists of entered and exited states accordingly.
628 *
629 * @param step The current Step
630 * @param errorReporter The ErrorReporter for the current environment
631 * @param scInstance The state chart instance
632 *
633 * @throws ModelException
634 * in case there is a fatal SCXML object model problem.
635 */
636 public void followTransitions(final Step step,
637 final ErrorReporter errorReporter, final SCInstance scInstance)
638 throws ModelException {
639 Set currentStates = step.getBeforeStatus().getStates();
640 List transitions = step.getTransitList();
641
642 Set exitedStates = new HashSet();
643 for (Iterator i = transitions.iterator(); i.hasNext();) {
644 Transition t = (Transition) i.next();
645 Set ext = SCXMLHelper.getStatesExited(t, currentStates);
646 exitedStates.addAll(ext);
647 }
648
649 Set residual = new HashSet(currentStates);
650 residual.removeAll(exitedStates);
651
652 Set seedSet = seedTargetSet(residual, transitions, errorReporter);
653
654 Set targetSet = step.getAfterStatus().getStates();
655 targetSet.addAll(seedSet);
656 determineTargetStates(targetSet, errorReporter, scInstance);
657
658 Set entered = SCXMLHelper.getAncestorClosure(targetSet, seedSet);
659 seedSet.clear();
660 for (Iterator i = transitions.iterator(); i.hasNext();) {
661 Transition t = (Transition) i.next();
662 List paths = t.getPaths();
663 for (int j = 0; j < paths.size(); j++) {
664 Path p = (Path) paths.get(j);
665 entered.addAll(p.getDownwardSegment());
666 }
667
668 List rtargets = t.getRuntimeTargets();
669 for (int j = 0; j < rtargets.size(); j++) {
670 TransitionTarget tt = (TransitionTarget) rtargets.get(j);
671 if (tt instanceof History) {
672 entered.remove(tt);
673 }
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 scInstance.setDone((State) o, 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 allEvents = new HashSet();
719 allEvents.addAll(Arrays.asList(events));
720 for (Iterator invokeIter = scInstance.getInvokers().entrySet().
721 iterator(); invokeIter.hasNext();) {
722 Map.Entry iEntry = (Map.Entry) invokeIter.next();
723 String parentId = ((TransitionTarget) iEntry.getKey()).getId();
724 if (!finalizeMatch(parentId, allEvents)) {
725 Invoker inv = (Invoker) iEntry.getValue();
726 try {
727 inv.parentEvents(events);
728 } catch (InvokerException ie) {
729 appLog.error(ie.getMessage(), ie);
730 throw new ModelException(ie.getMessage(), ie.getCause());
731 }
732 }
733 }
734 }
735
736 /***
737 * Initiate any new invokes.
738 *
739 * @param step
740 * The current Step
741 * @param errRep
742 * ErrorReporter callback
743 * @param scInstance
744 * The state chart instance
745 */
746 public void initiateInvokes(final Step step, final ErrorReporter errRep,
747 final SCInstance scInstance) {
748 Evaluator eval = scInstance.getEvaluator();
749 Collection internalEvents = step.getAfterStatus().getEvents();
750 for (Iterator iter = step.getAfterStatus().getStates().iterator();
751 iter.hasNext();) {
752 State s = (State) iter.next();
753 Context ctx = scInstance.getContext(s);
754 Invoke i = s.getInvoke();
755 if (i != null && scInstance.getInvoker(s) == null) {
756 String src = i.getSrc();
757 if (src == null) {
758 String srcexpr = i.getSrcexpr();
759 Object srcObj = null;
760 try {
761 ctx.setLocal(NAMESPACES_KEY, i.getNamespaces());
762 srcObj = eval.eval(ctx, srcexpr);
763 ctx.setLocal(NAMESPACES_KEY, null);
764 src = String.valueOf(srcObj);
765 } catch (SCXMLExpressionException see) {
766 errRep.onError(ErrorConstants.EXPRESSION_ERROR,
767 see.getMessage(), i);
768 }
769 }
770 String source = src;
771 PathResolver pr = i.getPathResolver();
772 if (pr != null) {
773 source = i.getPathResolver().resolvePath(src);
774 }
775 String ttype = i.getTargettype();
776 Invoker inv = null;
777 try {
778 inv = scInstance.newInvoker(ttype);
779 } catch (InvokerException ie) {
780 TriggerEvent te = new TriggerEvent(s.getId()
781 + ".invoke.failed", TriggerEvent.ERROR_EVENT);
782 internalEvents.add(te);
783 continue;
784 }
785 inv.setParentStateId(s.getId());
786 inv.setSCInstance(scInstance);
787 List params = i.params();
788 Map args = new HashMap();
789 for (Iterator pIter = params.iterator(); pIter.hasNext();) {
790 Param p = (Param) pIter.next();
791 String argExpr = p.getExpr();
792 Object argValue = null;
793 if (argExpr != null && argExpr.trim().length() > 0) {
794 try {
795 ctx.setLocal(NAMESPACES_KEY, p.getNamespaces());
796 argValue = eval.eval(ctx, argExpr);
797 ctx.setLocal(NAMESPACES_KEY, null);
798 } catch (SCXMLExpressionException see) {
799 errRep.onError(ErrorConstants.EXPRESSION_ERROR,
800 see.getMessage(), i);
801 }
802 }
803 args.put(p.getName(), argValue);
804 }
805 try {
806 inv.invoke(source, args);
807 } catch (InvokerException ie) {
808 TriggerEvent te = new TriggerEvent(s.getId()
809 + ".invoke.failed", TriggerEvent.ERROR_EVENT);
810 internalEvents.add(te);
811 continue;
812 }
813 scInstance.setInvoker(s, inv);
814 }
815 }
816 }
817
818 /***
819 * Implements prefix match, that is, if, for example,
820 * "mouse.click" is a member of eventOccurrences and a
821 * transition is triggered by "mouse", the method returns true.
822 *
823 * @param transEvent
824 * a trigger event of a transition
825 * @param eventOccurrences
826 * current events
827 * @return true/false
828 */
829 protected boolean eventMatch(final String transEvent,
830 final Set eventOccurrences) {
831 if (SCXMLHelper.isStringEmpty(transEvent)) {
832 return true;
833 } else {
834 String trimTransEvent = transEvent.trim();
835 Iterator i = eventOccurrences.iterator();
836 while (i.hasNext()) {
837 TriggerEvent te = (TriggerEvent) i.next();
838 String event = te.getName();
839 if (event == null) {
840 continue;
841 }
842 String trimEvent = event.trim();
843 if (trimEvent.equals(trimTransEvent)) {
844 return true;
845 } else if (te.getType() != TriggerEvent.CHANGE_EVENT
846 && trimTransEvent.equals("*")) {
847 return true;
848 } else if (trimTransEvent.endsWith(".*")
849 && trimEvent.startsWith(trimTransEvent.substring(0,
850 trimTransEvent.length() - 1))) {
851 return true;
852 }
853 }
854 return false;
855 }
856 }
857
858 /***
859 * Implements event prefix match to ascertain <finalize> execution.
860 *
861 * @param parentStateId
862 * the ID of the parent state of the <invoke> holding
863 * the <finalize>
864 * @param eventOccurrences
865 * current events
866 * @return true/false
867 */
868 protected boolean finalizeMatch(final String parentStateId,
869 final Set eventOccurrences) {
870 String prefix = parentStateId + ".invoke.";
871 Iterator i = eventOccurrences.iterator();
872 while (i.hasNext()) {
873 String evt = ((TriggerEvent) i.next()).getName();
874 if (evt == null) {
875 continue;
876 } else if (evt.trim().startsWith(prefix)) {
877 return true;
878 }
879 }
880 return false;
881 }
882
883 /***
884 * TransitionTargetComparator factory method.
885 * @return Comparator The TransitionTarget comparator
886 */
887 protected Comparator getTTComparator() {
888 return targetComparator;
889 }
890
891 /***
892 * Set the log used by this <code>SCXMLSemantics</code> instance.
893 *
894 * @param log The new log.
895 */
896 protected void setLog(final Log log) {
897 this.appLog = log;
898 }
899
900 /***
901 * Get the log used by this <code>SCXMLSemantics</code> instance.
902 *
903 * @return Log The log being used.
904 */
905 protected Log getLog() {
906 return appLog;
907 }
908
909 }
910