1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.scxml.io;
19
20 import java.io.IOException;
21 import java.net.URL;
22 import java.text.MessageFormat;
23 import java.util.List;
24 import java.util.Map;
25
26 import javax.xml.parsers.DocumentBuilder;
27 import javax.xml.parsers.DocumentBuilderFactory;
28 import javax.xml.parsers.ParserConfigurationException;
29
30 import org.apache.commons.digester.Digester;
31 import org.apache.commons.digester.ExtendedBaseRules;
32 import org.apache.commons.digester.NodeCreateRule;
33 import org.apache.commons.digester.ObjectCreateRule;
34 import org.apache.commons.digester.Rule;
35 import org.apache.commons.digester.SetNextRule;
36 import org.apache.commons.digester.SetPropertiesRule;
37 import org.apache.commons.logging.LogFactory;
38
39 import org.apache.commons.scxml.PathResolver;
40 import org.apache.commons.scxml.SCXMLHelper;
41 import org.apache.commons.scxml.env.URLResolver;
42 import org.apache.commons.scxml.model.Action;
43 import org.apache.commons.scxml.model.Assign;
44 import org.apache.commons.scxml.model.Cancel;
45 import org.apache.commons.scxml.model.CustomAction;
46 import org.apache.commons.scxml.model.Data;
47 import org.apache.commons.scxml.model.Datamodel;
48 import org.apache.commons.scxml.model.Else;
49 import org.apache.commons.scxml.model.ElseIf;
50 import org.apache.commons.scxml.model.Executable;
51 import org.apache.commons.scxml.model.Exit;
52 import org.apache.commons.scxml.model.ExternalContent;
53 import org.apache.commons.scxml.model.Finalize;
54 import org.apache.commons.scxml.model.History;
55 import org.apache.commons.scxml.model.If;
56 import org.apache.commons.scxml.model.Initial;
57 import org.apache.commons.scxml.model.Invoke;
58 import org.apache.commons.scxml.model.Log;
59 import org.apache.commons.scxml.model.ModelException;
60 import org.apache.commons.scxml.model.OnEntry;
61 import org.apache.commons.scxml.model.OnExit;
62 import org.apache.commons.scxml.model.Parallel;
63 import org.apache.commons.scxml.model.Param;
64 import org.apache.commons.scxml.model.PathResolverHolder;
65 import org.apache.commons.scxml.model.SCXML;
66 import org.apache.commons.scxml.model.Send;
67 import org.apache.commons.scxml.model.State;
68 import org.apache.commons.scxml.model.Transition;
69 import org.apache.commons.scxml.model.TransitionTarget;
70 import org.apache.commons.scxml.model.Var;
71
72 import org.w3c.dom.Element;
73 import org.w3c.dom.Node;
74 import org.w3c.dom.NodeList;
75
76 import org.xml.sax.Attributes;
77 import org.xml.sax.ErrorHandler;
78 import org.xml.sax.InputSource;
79 import org.xml.sax.SAXException;
80
81 /***
82 * <p>The SCXMLDigester provides the ability to digest a SCXML document into
83 * the Java object model provided in the model package.</p>
84 * <p>The SCXMLDigester can be used for:</p>
85 * <ol>
86 * <li>Digest a SCXML file into the Commons SCXML Java object model.</li>
87 * <li>Obtain a SCXML Digester for further customization of the default
88 * ruleset.</li>
89 * </ol>
90 */
91 public final class SCXMLDigester {
92
93 /***
94 * The SCXML namespace that this Digester is built for. Any document
95 * that is intended to be parsed by this digester <b>must</b>
96 * bind the SCXML elements to this namespace.
97 */
98 private static final String NAMESPACE_SCXML =
99 "http://www.w3.org/2005/07/scxml";
100
101
102 /***
103 * <p>API for standalone usage where the SCXML document is a URL.</p>
104 *
105 * @param scxmlURL
106 * a canonical absolute URL to parse (relative URLs within the
107 * top level document are to be resovled against this URL).
108 * @param errHandler
109 * The SAX ErrorHandler
110 *
111 * @return SCXML The SCXML object corresponding to the file argument
112 *
113 * @throws IOException Underlying Digester parsing threw an IOException
114 * @throws SAXException Underlying Digester parsing threw a SAXException
115 * @throws ModelException If the resulting document model has flaws
116 *
117 * @see ErrorHandler
118 * @see PathResolver
119 */
120 public static SCXML digest(final URL scxmlURL,
121 final ErrorHandler errHandler)
122 throws IOException, SAXException, ModelException {
123
124 if (scxmlURL == null) {
125 throw new IllegalArgumentException(ERR_NULL_URL);
126 }
127
128 return digest(scxmlURL, errHandler, null);
129
130 }
131
132 /***
133 * <p>API for standalone usage where the SCXML document is a URI.
134 * A PathResolver must be provided.</p>
135 *
136 * @param pathResolver
137 * The PathResolver for this context
138 * @param documentRealPath
139 * The String pointing to the absolute (real) path of the
140 * SCXML document
141 * @param errHandler
142 * The SAX ErrorHandler
143 *
144 * @return SCXML The SCXML object corresponding to the file argument
145 *
146 * @throws IOException Underlying Digester parsing threw an IOException
147 * @throws SAXException Underlying Digester parsing threw a SAXException
148 * @throws ModelException If the resulting document model has flaws
149 *
150 * @see ErrorHandler
151 * @see PathResolver
152 */
153 public static SCXML digest(final String documentRealPath,
154 final ErrorHandler errHandler, final PathResolver pathResolver)
155 throws IOException, SAXException, ModelException {
156
157 return digest(documentRealPath, errHandler, pathResolver, null);
158
159 }
160
161 /***
162 * <p>API for standalone usage where the SCXML document is an
163 * InputSource. This method may be used when the SCXML document is
164 * packaged in a Java archive, or part of a compound document
165 * where the SCXML root is available as a
166 * <code>org.w3c.dom.Element</code> or via a <code>java.io.Reader</code>.
167 * </p>
168 *
169 * <p><em>Note:</em> Since there is no path resolution, the SCXML document
170 * must not have external state sources.</p>
171 *
172 * @param documentInputSource
173 * The InputSource for the SCXML document
174 * @param errHandler
175 * The SAX ErrorHandler
176 *
177 * @return SCXML The SCXML object corresponding to the file argument
178 *
179 * @throws IOException Underlying Digester parsing threw an IOException
180 * @throws SAXException Underlying Digester parsing threw a SAXException
181 * @throws ModelException If the resulting document model has flaws
182 *
183 * @see ErrorHandler
184 */
185 public static SCXML digest(final InputSource documentInputSource,
186 final ErrorHandler errHandler)
187 throws IOException, SAXException, ModelException {
188
189 if (documentInputSource == null) {
190 throw new IllegalArgumentException(ERR_NULL_ISRC);
191 }
192
193 return digest(documentInputSource, errHandler, null);
194
195 }
196
197 /***
198 * <p>API for standalone usage where the SCXML document is a URL, and
199 * the document uses custom actions.</p>
200 *
201 * @param scxmlURL
202 * a canonical absolute URL to parse (relative URLs within the
203 * top level document are to be resovled against this URL).
204 * @param errHandler
205 * The SAX ErrorHandler
206 * @param customActions
207 * The list of {@link CustomAction}s this digester
208 * instance will process, can be null or empty
209 *
210 * @return SCXML The SCXML object corresponding to the file argument
211 *
212 * @throws IOException Underlying Digester parsing threw an IOException
213 * @throws SAXException Underlying Digester parsing threw a SAXException
214 * @throws ModelException If the resulting document model has flaws
215 *
216 * @see ErrorHandler
217 * @see PathResolver
218 */
219 public static SCXML digest(final URL scxmlURL,
220 final ErrorHandler errHandler, final List customActions)
221 throws IOException, SAXException, ModelException {
222
223 SCXML scxml = null;
224 Digester scxmlDigester = SCXMLDigester
225 .newInstance(null, new URLResolver(scxmlURL), customActions);
226 scxmlDigester.setErrorHandler(errHandler);
227
228 try {
229 scxml = (SCXML) scxmlDigester.parse(scxmlURL.toString());
230 } catch (RuntimeException rte) {
231
232
233 MessageFormat msgFormat = new MessageFormat(ERR_DOC_PARSE_FAIL);
234 String errMsg = msgFormat.format(new Object[] {
235 String.valueOf(scxmlURL), rte.getMessage()
236 });
237 org.apache.commons.logging.Log log = LogFactory.
238 getLog(SCXMLDigester.class);
239 log.error(errMsg, rte);
240 throw rte;
241 }
242
243 if (scxml != null) {
244 ModelUpdater.updateSCXML(scxml);
245 }
246
247 return scxml;
248
249 }
250
251 /***
252 * <p>API for standalone usage where the SCXML document is a URI.
253 * A PathResolver must be provided.</p>
254 *
255 * @param pathResolver
256 * The PathResolver for this context
257 * @param documentRealPath
258 * The String pointing to the absolute (real) path of the
259 * SCXML document
260 * @param errHandler
261 * The SAX ErrorHandler
262 * @param customActions
263 * The list of {@link CustomAction}s this digester
264 * instance will process, can be null or empty
265 *
266 * @return SCXML The SCXML object corresponding to the file argument
267 *
268 * @throws IOException Underlying Digester parsing threw an IOException
269 * @throws SAXException Underlying Digester parsing threw a SAXException
270 * @throws ModelException If the resulting document model has flaws
271 *
272 * @see ErrorHandler
273 * @see PathResolver
274 */
275 public static SCXML digest(final String documentRealPath,
276 final ErrorHandler errHandler, final PathResolver pathResolver,
277 final List customActions)
278 throws IOException, SAXException, ModelException {
279
280 if (documentRealPath == null) {
281 throw new IllegalArgumentException(ERR_NULL_PATH);
282 }
283
284 SCXML scxml = null;
285 Digester scxmlDigester = SCXMLDigester.newInstance(null, pathResolver,
286 customActions);
287 scxmlDigester.setErrorHandler(errHandler);
288
289 try {
290 scxml = (SCXML) scxmlDigester.parse(documentRealPath);
291 } catch (RuntimeException rte) {
292
293
294 MessageFormat msgFormat = new MessageFormat(ERR_DOC_PARSE_FAIL);
295 String errMsg = msgFormat.format(new Object[] {
296 documentRealPath, rte.getMessage()
297 });
298 org.apache.commons.logging.Log log = LogFactory.
299 getLog(SCXMLDigester.class);
300 log.error(errMsg, rte);
301 throw rte;
302 }
303
304 if (scxml != null) {
305 ModelUpdater.updateSCXML(scxml);
306 }
307
308 return scxml;
309
310 }
311
312 /***
313 * <p>API for standalone usage where the SCXML document is an
314 * InputSource. This method may be used when the SCXML document is
315 * packaged in a Java archive, or part of a compound document
316 * where the SCXML root is available as a
317 * <code>org.w3c.dom.Element</code> or via a <code>java.io.Reader</code>.
318 * </p>
319 *
320 * <p><em>Note:</em> Since there is no path resolution, the SCXML document
321 * must not have external state sources.</p>
322 *
323 * @param documentInputSource
324 * The InputSource for the SCXML document
325 * @param errHandler
326 * The SAX ErrorHandler
327 * @param customActions
328 * The list of {@link CustomAction}s this digester
329 * instance will process, can be null or empty
330 *
331 * @return SCXML The SCXML object corresponding to the file argument
332 *
333 * @throws IOException Underlying Digester parsing threw an IOException
334 * @throws SAXException Underlying Digester parsing threw a SAXException
335 * @throws ModelException If the resulting document model has flaws
336 *
337 * @see ErrorHandler
338 */
339 public static SCXML digest(final InputSource documentInputSource,
340 final ErrorHandler errHandler, final List customActions)
341 throws IOException, SAXException, ModelException {
342
343 Digester scxmlDigester = SCXMLDigester.newInstance(null, null,
344 customActions);
345 scxmlDigester.setErrorHandler(errHandler);
346
347 SCXML scxml = null;
348 try {
349 scxml = (SCXML) scxmlDigester.parse(documentInputSource);
350 } catch (RuntimeException rte) {
351
352
353 org.apache.commons.logging.Log log = LogFactory.
354 getLog(SCXMLDigester.class);
355 log.error(ERR_ISRC_PARSE_FAIL, rte);
356 throw rte;
357 }
358
359 if (scxml != null) {
360 ModelUpdater.updateSCXML(scxml);
361 }
362
363 return scxml;
364
365 }
366
367 /***
368 * <p>Obtain a SCXML digester instance for further customization.</p>
369 * <b>API Notes:</b>
370 * <ul>
371 * <li>Use the digest() convenience methods if you do not
372 * need a custom digester.</li>
373 * <li>After the SCXML document is parsed by the customized digester,
374 * the object model <b>must</b> be made executor-ready by calling
375 * <code>updateSCXML(SCXML)</code> method in this class.</li>
376 * </ul>
377 *
378 * @return Digester A newly configured SCXML digester instance
379 *
380 * @see SCXMLDigester#updateSCXML(SCXML)
381 */
382 public static Digester newInstance() {
383
384 return newInstance(null, null, null);
385
386 }
387
388 /***
389 * <p>Obtain a SCXML digester instance for further customization.</p>
390 * <b>API Notes:</b>
391 * <ul>
392 * <li>Use the digest() convenience methods if you do not
393 * need a custom digester.</li>
394 * <li>After the SCXML document is parsed by the customized digester,
395 * the object model <b>must</b> be made executor-ready by calling
396 * <code>updateSCXML(SCXML)</code> method in this class.</li>
397 * </ul>
398 *
399 * @param pr The PathResolver, may be null for standalone documents
400 * @return Digester A newly configured SCXML digester instance
401 *
402 * @see SCXMLDigester#updateSCXML(SCXML)
403 */
404 public static Digester newInstance(final PathResolver pr) {
405
406 return newInstance(null, pr, null);
407
408 }
409
410 /***
411 * <p>Obtain a SCXML digester instance for further customization.</p>
412 * <b>API Notes:</b>
413 * <ul>
414 * <li>Use the digest() convenience methods if you do not
415 * need a custom digester.</li>
416 * <li>After the SCXML document is parsed by the customized digester,
417 * the object model <b>must</b> be made executor-ready by calling
418 * <code>updateSCXML(SCXML)</code> method in this class.</li>
419 * </ul>
420 *
421 * @param scxml The parent SCXML document if there is one (in case of
422 * state templates for example), null otherwise
423 * @param pr The PathResolver, may be null for standalone documents
424 * @return Digester A newly configured SCXML digester instance
425 *
426 * @see SCXMLDigester#updateSCXML(SCXML)
427 */
428 public static Digester newInstance(final SCXML scxml,
429 final PathResolver pr) {
430
431 return newInstance(scxml, pr, null);
432
433 }
434
435 /***
436 * <p>Obtain a SCXML digester instance for further customization.</p>
437 * <b>API Notes:</b>
438 * <ul>
439 * <li>Use the digest() convenience methods if you do not
440 * need a custom digester.</li>
441 * <li>After the SCXML document is parsed by the customized digester,
442 * the object model <b>must</b> be made executor-ready by calling
443 * <code>updateSCXML(SCXML)</code> method in this class.</li>
444 * </ul>
445 *
446 * @param scxml The parent SCXML document if there is one (in case of
447 * state templates for example), null otherwise
448 * @param pr The PathResolver, may be null for standalone documents
449 * @param customActions The list of {@link CustomAction}s this digester
450 * instance will process, can be null or empty
451 * @return Digester A newly configured SCXML digester instance
452 *
453 * @see SCXMLDigester#updateSCXML(SCXML)
454 */
455 public static Digester newInstance(final SCXML scxml,
456 final PathResolver pr, final List customActions) {
457
458 Digester digester = new Digester();
459 digester.setNamespaceAware(true);
460
461
462 digester.setRules(initRules(scxml, pr, customActions));
463 return digester;
464 }
465
466 /***
467 * <p>Update the SCXML object model and make it SCXMLExecutor ready.
468 * This is part of post-digester processing, and sets up the necessary
469 * object references throughtout the SCXML object model for the parsed
470 * document. Should be used only if a customized digester obtained
471 * using the <code>newInstance()</code> methods is needed.</p>
472 *
473 * @param scxml The SCXML object (output from Digester)
474 * @throws ModelException If the document model has flaws
475 */
476 public static void updateSCXML(final SCXML scxml)
477 throws ModelException {
478 ModelUpdater.updateSCXML(scxml);
479 }
480
481
482
483 /*** Root <scxml> element. */
484 private static final String XP_SM = "scxml";
485
486 /*** <state> children of root <scxml> element. */
487 private static final String XP_SM_ST = "scxml/state";
488
489
490
491 /*** <state> children of <state> elements. */
492 private static final String XPU_ST_ST = "!*/state/state";
493
494 /*** <state> children of <parallel> elements. */
495 private static final String XPU_PAR_ST = "!*/parallel/state";
496
497 /*** <state> children of transition <target> elements. */
498 private static final String XPU_TR_TAR_ST = "!*/transition/target/state";
499
500
501
502
503 /*** <parallel> child of <state> elements. */
504 private static final String XPU_ST_PAR = "!*/state/parallel";
505
506
507 /*** <if> element. */
508 private static final String XPU_IF = "!*/if";
509
510
511 /*** <onentry> element. */
512 private static final String XPU_ONEN = "!*/onentry";
513
514 /*** <onexit> element. */
515 private static final String XPU_ONEX = "!*/onexit";
516
517 /*** <transition> element. */
518 private static final String XPU_TR = "!*/transition";
519
520 /*** <finalize> element. */
521 private static final String XPU_FIN = "!*/finalize";
522
523
524
525 /*** <onentry> child element. */
526 private static final String XPF_ONEN = "/onentry";
527
528 /*** <onexit> child element. */
529 private static final String XPF_ONEX = "/onexit";
530
531
532 /*** <datamodel> child element. */
533 private static final String XPF_DM = "/datamodel";
534
535 /*** Individual <data> elements. */
536 private static final String XPF_DATA = "/data";
537
538
539 /*** <initial> child element. */
540 private static final String XPF_INI = "/initial";
541
542
543 /*** <invoke> child element of <state>. */
544 private static final String XPF_INV = "/invoke";
545
546 /*** <param> child element of <invoke>. */
547 private static final String XPF_PRM = "/param";
548
549 /*** <finalize> child element of <invoke>. */
550 private static final String XPF_FIN = "/finalize";
551
552
553 /*** <history> child element. */
554 private static final String XPF_HIST = "/history";
555
556
557 /*** <transition> child element. */
558 private static final String XPF_TR = "/transition";
559
560 /*** <target> child element. */
561 private static final String XPF_TAR = "/target";
562
563 /*** <exit> child element. */
564 private static final String XPF_EXT = "/exit";
565
566
567 /*** <var> child element. */
568 private static final String XPF_VAR = "/var";
569
570 /*** <assign> child element. */
571 private static final String XPF_ASN = "/assign";
572
573 /*** <log> child element. */
574 private static final String XPF_LOG = "/log";
575
576 /*** <send> child element. */
577 private static final String XPF_SND = "/send";
578
579 /*** <cancel> child element. */
580 private static final String XPF_CAN = "/cancel";
581
582 /*** <elseif> child element. */
583 private static final String XPF_EIF = "/elseif";
584
585 /*** <else> child element. */
586 private static final String XPF_ELS = "/else";
587
588
589
590 /***
591 * Null URL passed as argument.
592 */
593 private static final String ERR_NULL_URL = "Cannot parse null URL";
594
595 /***
596 * Null path passed as argument.
597 */
598 private static final String ERR_NULL_PATH = "Cannot parse null URL";
599
600 /***
601 * Null InputSource passed as argument.
602 */
603 private static final String ERR_NULL_ISRC = "Cannot parse null URL";
604
605 /***
606 * Parsing SCXML document has failed.
607 */
608 private static final String ERR_DOC_PARSE_FAIL = "Error parsing "
609 + "SCXML document: \"{0}\", with message: \"{1}\"\n";
610
611 /***
612 * Parsing SCXML document InputSource has failed.
613 */
614 private static final String ERR_ISRC_PARSE_FAIL =
615 "Could not parse SCXML InputSource";
616
617 /***
618 * Parser configuration error while registering data rule.
619 */
620 private static final String ERR_PARSER_CFG_DATA = "XML Parser "
621 + "misconfiguration, error registering <data> element rule";
622
623 /***
624 * Parser configuration error while registering send rule.
625 */
626 private static final String ERR_PARSER_CFG_SEND = "XML Parser "
627 + "misconfiguration, error registering <send> element rule";
628
629 /***
630 * Parser configuration error while registering body content rule for
631 * custom action.
632 */
633 private static final String ERR_PARSER_CFG_CUSTOM = "XML Parser "
634 + "misconfiguration, error registering custom action rules";
635
636 /***
637 * Error message while attempting to define a custom action which does
638 * not extend the Commons SCXML Action base class.
639 */
640 private static final String ERR_CUSTOM_ACTION_TYPE = "Custom actions list"
641 + " contained unknown object (not a Commons SCXML Action subtype)";
642
643
644 /*** Slash. */
645 private static final String STR_SLASH = "/";
646
647 /*** Prefix for universal digester patterns. */
648 private static final String STR_UNIVERSAL = "!*";
649
650
651
652
653
654 /***
655 * Initialize the Digester rules for the current document.
656 *
657 * @param scxml The parent SCXML document (or null)
658 * @param pr The PathResolver
659 * @param customActions The list of custom actions this digester needs
660 * to be able to process
661 *
662 * @return scxmlRules The rule set to be used for digestion
663 */
664 private static ExtendedBaseRules initRules(final SCXML scxml,
665 final PathResolver pr, final List customActions) {
666
667 ExtendedBaseRules scxmlRules = new ExtendedBaseRules();
668
669
670 scxmlRules.add(XP_SM, new ObjectCreateRule(SCXML.class));
671 scxmlRules.add(XP_SM, new SetPropertiesRule());
672
673
674 addDatamodelRules(XP_SM + XPF_DM, scxmlRules, scxml, pr);
675
676
677
678 addStateRules(XP_SM_ST, scxmlRules, customActions, scxml, pr, 0);
679 scxmlRules.add(XP_SM_ST, new SetNextRule("addState"));
680
681 addStateRules(XPU_ST_ST, scxmlRules, customActions, scxml, pr, 1);
682 scxmlRules.add(XPU_ST_ST, new SetNextRule("addChild"));
683
684
685 addStateRules(XPU_PAR_ST, scxmlRules, customActions, scxml, pr, 1);
686 scxmlRules.add(XPU_PAR_ST, new SetNextRule("addState"));
687
688 addStateRules(XPU_TR_TAR_ST, scxmlRules, customActions, scxml, pr, 2);
689 scxmlRules.add(XPU_TR_TAR_ST, new SetNextRule("setTarget"));
690
691
692 addParallelRules(XPU_ST_PAR, scxmlRules, pr, customActions, scxml);
693
694
695 addIfRules(XPU_IF, scxmlRules, pr, customActions);
696
697
698 addCustomActionRules(XPU_ONEN, scxmlRules, customActions);
699 addCustomActionRules(XPU_ONEX, scxmlRules, customActions);
700 addCustomActionRules(XPU_TR, scxmlRules, customActions);
701 addCustomActionRules(XPU_IF, scxmlRules, customActions);
702 addCustomActionRules(XPU_FIN, scxmlRules, customActions);
703
704 return scxmlRules;
705
706 }
707
708 /***
709 * Add Digester rules for all <state> elements.
710 *
711 * @param xp The Digester style XPath expression of the parent
712 * XML element
713 * @param scxmlRules The rule set to be used for digestion
714 * @param customActions The list of custom actions this digester needs
715 * to be able to process
716 * @param scxml The parent SCXML document (or null)
717 * @param pr The PathResolver
718 * @param parent The distance between this state and its parent
719 * state on the Digester stack
720 */
721 private static void addStateRules(final String xp,
722 final ExtendedBaseRules scxmlRules, final List customActions,
723 final SCXML scxml, final PathResolver pr, final int parent) {
724 scxmlRules.add(xp, new ObjectCreateRule(State.class));
725 addStatePropertiesRules(xp, scxmlRules, customActions, pr);
726 addDatamodelRules(xp + XPF_DM, scxmlRules, scxml, pr);
727 addInvokeRules(xp + XPF_INV, scxmlRules, customActions, pr, scxml);
728 addInitialRules(xp + XPF_INI, scxmlRules, customActions, pr, scxml);
729 addHistoryRules(xp + XPF_HIST, scxmlRules, customActions, pr, scxml);
730 addParentRule(xp, scxmlRules, parent);
731 addTransitionRules(xp + XPF_TR, scxmlRules, "addTransition",
732 pr, customActions);
733 addHandlerRules(xp, scxmlRules, pr, customActions);
734 scxmlRules.add(xp, new UpdateModelRule(scxml));
735 }
736
737 /***
738 * Add Digester rules for all <parallel> elements.
739 *
740 * @param xp The Digester style XPath expression of the parent
741 * XML element
742 * @param scxmlRules The rule set to be used for digestion
743 * @param customActions The list of custom actions this digester needs
744 * to be able to process
745 * @param pr The {@link PathResolver} for this document
746 * @param scxml The parent SCXML document (or null)
747 */
748 private static void addParallelRules(final String xp,
749 final ExtendedBaseRules scxmlRules, final PathResolver pr,
750 final List customActions, final SCXML scxml) {
751 addSimpleRulesTuple(xp, scxmlRules, Parallel.class, null, null,
752 "setParallel");
753 addHandlerRules(xp, scxmlRules, pr, customActions);
754 addParentRule(xp, scxmlRules, 1);
755 scxmlRules.add(xp, new UpdateModelRule(scxml));
756 }
757
758 /***
759 * Add Digester rules for all <state> element attributes.
760 *
761 * @param xp The Digester style XPath expression of the parent
762 * XML element
763 * @param scxmlRules The rule set to be used for digestion
764 * @param customActions The list of custom actions this digester needs
765 * to be able to process
766 * @param pr The PathResolver
767 */
768 private static void addStatePropertiesRules(final String xp,
769 final ExtendedBaseRules scxmlRules, final List customActions,
770 final PathResolver pr) {
771 scxmlRules.add(xp, new SetPropertiesRule(new String[] {"id", "final"},
772 new String[] {"id", "isFinal"}));
773 scxmlRules.add(xp, new DigestSrcAttributeRule(customActions, pr));
774 }
775
776 /***
777 * Add Digester rules for all <datamodel> elements.
778 *
779 * @param xp The Digester style XPath expression of the parent
780 * XML element
781 * @param scxmlRules The rule set to be used for digestion
782 * @param pr The PathResolver
783 * @param scxml The parent SCXML document (or null)
784 */
785 private static void addDatamodelRules(final String xp,
786 final ExtendedBaseRules scxmlRules, final SCXML scxml,
787 final PathResolver pr) {
788 scxmlRules.add(xp, new ObjectCreateRule(Datamodel.class));
789 scxmlRules.add(xp + XPF_DATA, new ObjectCreateRule(Data.class));
790 scxmlRules.add(xp + XPF_DATA, new SetPropertiesRule());
791 scxmlRules.add(xp + XPF_DATA, new SetNextRule("addData"));
792 try {
793 scxmlRules.add(xp + XPF_DATA, new ParseDataRule(pr));
794 } catch (ParserConfigurationException pce) {
795 org.apache.commons.logging.Log log = LogFactory.
796 getLog(SCXMLDigester.class);
797 log.error(ERR_PARSER_CFG_DATA, pce);
798 }
799 scxmlRules.add(xp, new SetNextRule("setDatamodel"));
800 }
801
802 /***
803 * Add Digester rules for all <invoke> elements.
804 *
805 * @param xp The Digester style XPath expression of the parent
806 * XML element
807 * @param scxmlRules The rule set to be used for digestion
808 * @param customActions The list of {@link CustomAction}s this digester
809 * instance will process, can be null or empty
810 * @param pr The PathResolver
811 * @param scxml The parent SCXML document (or null)
812 */
813 private static void addInvokeRules(final String xp,
814 final ExtendedBaseRules scxmlRules, final List customActions,
815 final PathResolver pr, final SCXML scxml) {
816 scxmlRules.add(xp, new ObjectCreateRule(Invoke.class));
817 scxmlRules.add(xp, new SetPropertiesRule());
818 scxmlRules.add(xp, new SetPathResolverRule(pr));
819 scxmlRules.add(xp + XPF_PRM, new ObjectCreateRule(Param.class));
820 scxmlRules.add(xp + XPF_PRM, new SetPropertiesRule());
821 scxmlRules.add(xp + XPF_PRM, new SetNextRule("addParam"));
822 scxmlRules.add(xp + XPF_FIN, new ObjectCreateRule(Finalize.class));
823 scxmlRules.add(xp + XPF_FIN, new UpdateFinalizeRule());
824 addActionRules(xp + XPF_FIN, scxmlRules, pr, customActions);
825 scxmlRules.add(xp + XPF_FIN, new SetNextRule("setFinalize"));
826 scxmlRules.add(xp, new SetNextRule("setInvoke"));
827 }
828
829 /***
830 * Add Digester rules for all <initial> elements.
831 *
832 * @param xp The Digester style XPath expression of the parent
833 * XML element
834 * @param scxmlRules The rule set to be used for digestion
835 * @param customActions The list of custom actions this digester needs
836 * to be able to process
837 * @param pr The PathResolver
838 * @param scxml The parent SCXML document (or null)
839 */
840 private static void addInitialRules(final String xp,
841 final ExtendedBaseRules scxmlRules, final List customActions,
842 final PathResolver pr, final SCXML scxml) {
843 scxmlRules.add(xp, new ObjectCreateRule(Initial.class));
844 addPseudoStatePropertiesRules(xp, scxmlRules, customActions, pr);
845 scxmlRules.add(xp, new UpdateModelRule(scxml));
846 addTransitionRules(xp + XPF_TR, scxmlRules, "setTransition",
847 pr, customActions);
848 scxmlRules.add(xp, new SetNextRule("setInitial"));
849 }
850
851 /***
852 * Add Digester rules for all <history> elements.
853 *
854 * @param xp The Digester style XPath expression of the parent
855 * XML element
856 * @param scxmlRules The rule set to be used for digestion
857 * @param customActions The list of custom actions this digester needs
858 * to be able to process
859 * @param pr The PathResolver
860 * @param scxml The parent SCXML document (or null)
861 */
862 private static void addHistoryRules(final String xp,
863 final ExtendedBaseRules scxmlRules, final List customActions,
864 final PathResolver pr, final SCXML scxml) {
865 scxmlRules.add(xp, new ObjectCreateRule(History.class));
866 addPseudoStatePropertiesRules(xp, scxmlRules, customActions, pr);
867 scxmlRules.add(xp, new UpdateModelRule(scxml));
868 scxmlRules.add(xp, new SetPropertiesRule(new String[] {"type"},
869 new String[] {"type"}));
870 addTransitionRules(xp + XPF_TR, scxmlRules, "setTransition",
871 pr, customActions);
872 scxmlRules.add(xp, new SetNextRule("addHistory"));
873 }
874
875 /***
876 * Add Digester rules for all pseudo state (initial, history) element
877 * attributes.
878 *
879 * @param xp The Digester style XPath expression of the parent
880 * XML element
881 * @param scxmlRules The rule set to be used for digestion
882 * @param customActions The list of custom actions this digester needs
883 * to be able to process
884 * @param pr The PathResolver
885 */
886 private static void addPseudoStatePropertiesRules(final String xp,
887 final ExtendedBaseRules scxmlRules, final List customActions,
888 final PathResolver pr) {
889 scxmlRules.add(xp, new SetPropertiesRule(new String[] {"id"},
890 new String[] {"id"}));
891 scxmlRules.add(xp, new DigestSrcAttributeRule(customActions, pr));
892 addParentRule(xp, scxmlRules, 1);
893 }
894
895 /***
896 * Add Digester rule for all setting parent state.
897 *
898 * @param xp The Digester style XPath expression of the parent
899 * XML element
900 * @param scxmlRules The rule set to be used for digestion
901 * @param parent The distance between this state and its parent
902 * state on the Digester stack
903 */
904 private static void addParentRule(final String xp,
905 final ExtendedBaseRules scxmlRules, final int parent) {
906 if (parent < 1) {
907 return;
908 }
909 scxmlRules.add(xp, new Rule() {
910
911 public void body(final String namespace, final String name,
912 final String text) throws Exception {
913 TransitionTarget t = (TransitionTarget) getDigester().peek();
914 TransitionTarget p = (TransitionTarget) getDigester().peek(
915 parent);
916
917 t.setParent(p);
918 }
919 });
920 }
921
922 /***
923 * Add Digester rules for all <transition> elements.
924 *
925 * @param xp The Digester style XPath expression of the parent
926 * XML element
927 * @param scxmlRules The rule set to be used for digestion
928 * @param setNextMethod The method name for adding this transition
929 * to its parent (defined by the SCXML Java object model).
930 * @param pr The {@link PathResolver} for this document
931 * @param customActions The list of custom actions this digester needs
932 * to be able to process
933 */
934 private static void addTransitionRules(final String xp,
935 final ExtendedBaseRules scxmlRules, final String setNextMethod,
936 final PathResolver pr, final List customActions) {
937 scxmlRules.add(xp, new ObjectCreateRule(Transition.class));
938 scxmlRules.add(xp, new SetPropertiesRule(
939 new String[] {"event", "cond", "target"},
940 new String[] {"event", "cond", "next"}));
941 scxmlRules.add(xp + XPF_TAR, new SetPropertiesRule());
942 addActionRules(xp, scxmlRules, pr, customActions);
943 scxmlRules.add(xp + XPF_EXT, new Rule() {
944 public void end(final String namespace, final String name) {
945 Transition t = (Transition) getDigester().peek(1);
946 State exitState = new State();
947 exitState.setIsFinal(true);
948 t.setTarget(exitState);
949 }
950 });
951 scxmlRules.add(xp, new SetNextRule(setNextMethod));
952 }
953
954 /***
955 * Add Digester rules for all <onentry> and <onexit>
956 * elements.
957 *
958 * @param xp The Digester style XPath expression of the parent
959 * XML element
960 * @param scxmlRules The rule set to be used for digestion
961 * @param pr The {@link PathResolver} for this document
962 * @param customActions The list of custom actions this digester needs
963 * to be able to process
964 */
965 private static void addHandlerRules(final String xp,
966 final ExtendedBaseRules scxmlRules, final PathResolver pr,
967 final List customActions) {
968 scxmlRules.add(xp + XPF_ONEN, new ObjectCreateRule(OnEntry.class));
969 addActionRules(xp + XPF_ONEN, scxmlRules, pr, customActions);
970 scxmlRules.add(xp + XPF_ONEN, new SetNextRule("setOnEntry"));
971 scxmlRules.add(xp + XPF_ONEX, new ObjectCreateRule(OnExit.class));
972 addActionRules(xp + XPF_ONEX, scxmlRules, pr, customActions);
973 scxmlRules.add(xp + XPF_ONEX, new SetNextRule("setOnExit"));
974 }
975
976 /***
977 * Add Digester rules for all actions ("executable" elements).
978 *
979 * @param xp The Digester style XPath expression of the parent
980 * XML element
981 * @param scxmlRules The rule set to be used for digestion
982 * @param pr The {@link PathResolver} for this document
983 * @param customActions The list of custom actions this digester needs
984 * to be able to process
985 */
986 private static void addActionRules(final String xp,
987 final ExtendedBaseRules scxmlRules, final PathResolver pr,
988 final List customActions) {
989 addActionRulesTuple(xp + XPF_ASN, scxmlRules, Assign.class);
990 scxmlRules.add(xp + XPF_ASN, new SetPathResolverRule(pr));
991 addActionRulesTuple(xp + XPF_VAR, scxmlRules, Var.class);
992 addActionRulesTuple(xp + XPF_LOG, scxmlRules, Log.class);
993 addSendRulesTuple(xp + XPF_SND, scxmlRules);
994 addActionRulesTuple(xp + XPF_CAN, scxmlRules, Cancel.class);
995 addActionRulesTuple(xp + XPF_EXT, scxmlRules, Exit.class);
996
997 }
998
999 /***
1000 * Add custom action rules, if any custom actions are provided.
1001 *
1002 * @param xp The Digester style XPath expression of the parent
1003 * XML element
1004 * @param scxmlRules The rule set to be used for digestion
1005 * @param customActions The list of custom actions this digester needs
1006 * to be able to process
1007 */
1008 private static void addCustomActionRules(final String xp,
1009 final ExtendedBaseRules scxmlRules, final List customActions) {
1010 if (customActions == null || customActions.size() == 0) {
1011 return;
1012 }
1013 for (int i = 0; i < customActions.size(); i++) {
1014 Object item = customActions.get(i);
1015 if (item == null || !(item instanceof CustomAction)) {
1016 org.apache.commons.logging.Log log = LogFactory.
1017 getLog(SCXMLDigester.class);
1018 log.warn(ERR_CUSTOM_ACTION_TYPE);
1019 } else {
1020 CustomAction ca = (CustomAction) item;
1021 scxmlRules.setNamespaceURI(ca.getNamespaceURI());
1022 String xpfLocalName = STR_SLASH + ca.getLocalName();
1023 Class klass = ca.getActionClass();
1024 if (SCXMLHelper.implementationOf(klass,
1025 ExternalContent.class)) {
1026 addCustomActionRulesTuple(xp + xpfLocalName, scxmlRules,
1027 klass, true);
1028 } else {
1029 addCustomActionRulesTuple(xp + xpfLocalName, scxmlRules,
1030 klass, false);
1031 }
1032 }
1033 }
1034 scxmlRules.setNamespaceURI(NAMESPACE_SCXML);
1035 }
1036
1037 /***
1038 * Add Digester rules that are specific to the <send> action
1039 * element.
1040 *
1041 * @param xp The Digester style XPath expression of <send> element
1042 * @param scxmlRules The rule set to be used for digestion
1043 */
1044 private static void addSendRulesTuple(final String xp,
1045 final ExtendedBaseRules scxmlRules) {
1046 addActionRulesTuple(xp, scxmlRules, Send.class);
1047 try {
1048 scxmlRules.add(xp, new ParseExternalContentRule());
1049 } catch (ParserConfigurationException pce) {
1050 org.apache.commons.logging.Log log = LogFactory.
1051 getLog(SCXMLDigester.class);
1052 log.error(ERR_PARSER_CFG_SEND, pce);
1053 }
1054 }
1055
1056 /***
1057 * Add Digester rules for a simple custom action (no body content).
1058 *
1059 * @param xp The path to the custom action element
1060 * @param scxmlRules The rule set to be used for digestion
1061 * @param klass The <code>Action</code> class implementing the custom
1062 * action.
1063 * @param bodyContent Whether the custom rule has body content
1064 * that should be parsed using
1065 * <code>NodeCreateRule</code>
1066 */
1067 private static void addCustomActionRulesTuple(final String xp,
1068 final ExtendedBaseRules scxmlRules, final Class klass,
1069 final boolean bodyContent) {
1070 addActionRulesTuple(xp, scxmlRules, klass);
1071 if (bodyContent) {
1072 try {
1073 scxmlRules.add(xp, new ParseExternalContentRule());
1074 } catch (ParserConfigurationException pce) {
1075 org.apache.commons.logging.Log log = LogFactory.
1076 getLog(SCXMLDigester.class);
1077 log.error(ERR_PARSER_CFG_CUSTOM, pce);
1078 }
1079 }
1080 }
1081
1082 /***
1083 * Add Digester rules for all <if> elements.
1084 *
1085 * @param xp The Digester style XPath expression of the parent
1086 * XML element
1087 * @param scxmlRules The rule set to be used for digestion
1088 * @param pr The {@link PathResolver} for this document
1089 * @param customActions The list of custom actions this digester needs
1090 * to be able to process
1091 */
1092 private static void addIfRules(final String xp,
1093 final ExtendedBaseRules scxmlRules, final PathResolver pr,
1094 final List customActions) {
1095 addActionRulesTuple(xp, scxmlRules, If.class);
1096 addActionRules(xp, scxmlRules, pr, customActions);
1097 addActionRulesTuple(xp + XPF_EIF, scxmlRules, ElseIf.class);
1098 addActionRulesTuple(xp + XPF_ELS, scxmlRules, Else.class);
1099 }
1100
1101 /***
1102 * Add Digester rules that are common across all actions elements.
1103 *
1104 * @param xp The Digester style XPath expression of the parent
1105 * XML element
1106 * @param scxmlRules The rule set to be used for digestion
1107 * @param klass The class in the Java object model to be instantiated
1108 * in the ObjectCreateRule for this action
1109 */
1110 private static void addActionRulesTuple(final String xp,
1111 final ExtendedBaseRules scxmlRules, final Class klass) {
1112 addSimpleRulesTuple(xp, scxmlRules, klass, null, null, "addAction");
1113 scxmlRules.add(xp, new SetExecutableParentRule());
1114 }
1115
1116 /***
1117 * Add the run of the mill Digester rules for any element.
1118 *
1119 * @param xp The Digester style XPath expression of the parent
1120 * XML element
1121 * @param scxmlRules The rule set to be used for digestion
1122 * @param klass The class in the Java object model to be instantiated
1123 * in the ObjectCreateRule for this action
1124 * @param args The attributes to be mapped into the object model
1125 * @param props The properties that args get mapped to
1126 * @param addMethod The method that the SetNextRule should call
1127 */
1128 private static void addSimpleRulesTuple(final String xp,
1129 final ExtendedBaseRules scxmlRules, final Class klass,
1130 final String[] args, final String[] props,
1131 final String addMethod) {
1132 scxmlRules.add(xp, new ObjectCreateRule(klass));
1133 if (args == null) {
1134 scxmlRules.add(xp, new SetPropertiesRule());
1135 } else {
1136 scxmlRules.add(xp, new SetPropertiesRule(args, props));
1137 }
1138 scxmlRules.add(xp, new SetNextRule(addMethod));
1139 }
1140
1141 /***
1142 * Discourage instantiation since this is a utility class.
1143 */
1144 private SCXMLDigester() {
1145 super();
1146 }
1147
1148 /***
1149 * Custom digestion rule for establishing necessary associations of this
1150 * TransitionTarget with the root SCXML object.
1151 * These include: <br>
1152 * 1) Updation of the SCXML object's global targets Map <br>
1153 * 2) Obtaining a handle to the SCXML object's NotificationRegistry <br>
1154 *
1155 */
1156 public static class UpdateModelRule extends Rule {
1157
1158 /***
1159 * The root SCXML object.
1160 */
1161 private SCXML scxml;
1162
1163 /***
1164 * Constructor.
1165 * @param scxml The root SCXML object
1166 */
1167 public UpdateModelRule(final SCXML scxml) {
1168 super();
1169 this.scxml = scxml;
1170 }
1171
1172 /***
1173 * @see Rule#end(String, String)
1174 */
1175 public final void end(final String namespace, final String name) {
1176 if (scxml == null) {
1177 scxml = (SCXML) getDigester()
1178 .peek(getDigester().getCount() - 1);
1179 }
1180 TransitionTarget tt = (TransitionTarget) getDigester().peek();
1181 scxml.addTarget(tt);
1182 }
1183 }
1184
1185 /***
1186 * Custom digestion rule for setting Executable parent of Action elements.
1187 *
1188 */
1189 public static class SetExecutableParentRule extends Rule {
1190
1191 /***
1192 * Constructor.
1193 */
1194 public SetExecutableParentRule() {
1195 super();
1196 }
1197
1198 /***
1199 * @see Rule#end(String, String)
1200 */
1201 public final void end(final String namespace, final String name) {
1202 Action child = (Action) getDigester().peek();
1203 for (int i = 1; i < getDigester().getCount() - 1; i++) {
1204 Object ancestor = getDigester().peek(i);
1205 if (ancestor instanceof Executable) {
1206 child.setParent((Executable) ancestor);
1207 return;
1208 }
1209 }
1210 }
1211 }
1212
1213 /***
1214 * Custom digestion rule for parsing bodies of
1215 * <code>ExternalContent</code> elements.
1216 *
1217 * @see ExternalContent
1218 */
1219 public static class ParseExternalContentRule extends NodeCreateRule {
1220 /***
1221 * Constructor.
1222 * @throws ParserConfigurationException A JAXP configuration error
1223 */
1224 public ParseExternalContentRule()
1225 throws ParserConfigurationException {
1226 super();
1227 }
1228 /***
1229 * @see Rule#end(String, String)
1230 */
1231 public final void end(final String namespace, final String name) {
1232 Element bodyElement = (Element) getDigester().pop();
1233 NodeList childNodes = bodyElement.getChildNodes();
1234 List externalNodes = ((ExternalContent) getDigester().
1235 peek()).getExternalNodes();
1236 for (int i = 0; i < childNodes.getLength(); i++) {
1237 externalNodes.add(childNodes.item(i));
1238 }
1239 }
1240 }
1241
1242 /***
1243 * Custom digestion rule for parsing bodies of <data> elements.
1244 *
1245 */
1246 public static class ParseDataRule extends NodeCreateRule {
1247
1248 /***
1249 * The PathResolver used to resolve the src attribute to the
1250 * SCXML document it points to.
1251 * @see PathResolver
1252 */
1253 private PathResolver pr;
1254
1255 /***
1256 * The "src" attribute, retained to check if body content is legal.
1257 */
1258 private String src;
1259
1260 /***
1261 * The "expr" attribute, retained to check if body content is legal.
1262 */
1263 private String expr;
1264
1265 /***
1266 * The XML tree for this data, parse as a Node, obtained from
1267 * either the "src" or the "expr" attributes.
1268 */
1269 private Node attrNode;
1270
1271 /***
1272 * Constructor.
1273 *
1274 * @param pr The <code>PathResolver</code>
1275 * @throws ParserConfigurationException A JAXP configuration error
1276 */
1277 public ParseDataRule(final PathResolver pr)
1278 throws ParserConfigurationException {
1279 super();
1280 this.pr = pr;
1281 }
1282
1283 /***
1284 * @see Rule#begin(String, String, Attributes)
1285 */
1286 public final void begin(final String namespace, final String name,
1287 final Attributes attributes) throws Exception {
1288 super.begin(namespace, name, attributes);
1289 src = attributes.getValue("src");
1290 expr = attributes.getValue("expr");
1291 if (!SCXMLHelper.isStringEmpty(src)) {
1292 String path = null;
1293 if (pr == null) {
1294 path = src;
1295 } else {
1296 path = pr.resolvePath(src);
1297 }
1298 try {
1299 DocumentBuilderFactory dbFactory = DocumentBuilderFactory.
1300 newInstance();
1301 DocumentBuilder db = dbFactory.newDocumentBuilder();
1302 attrNode = db.parse(path);
1303 } catch (Throwable t) {
1304 org.apache.commons.logging.Log log = LogFactory.
1305 getLog(SCXMLDigester.class);
1306 log.error(t.getMessage(), t);
1307 }
1308 return;
1309 }
1310 }
1311
1312 /***
1313 * @see Rule#end(String, String)
1314 */
1315 public final void end(final String namespace, final String name) {
1316 Node bodyNode = (Node) getDigester().pop();
1317 Data data = ((Data) getDigester().peek());
1318
1319
1320 if (!SCXMLHelper.isStringEmpty(src)) {
1321 data.setNode(attrNode);
1322 } else if (SCXMLHelper.isStringEmpty(expr)) {
1323
1324 data.setNode(bodyNode);
1325 }
1326 }
1327 }
1328
1329 /***
1330 * Custom digestion rule for external sources, that is, the src attribute of
1331 * the <state> element.
1332 *
1333 */
1334 public static class DigestSrcAttributeRule extends Rule {
1335
1336 /***
1337 * The PathResolver used to resolve the src attribute to the
1338 * SCXML document it points to.
1339 * @see PathResolver
1340 */
1341 private PathResolver pr;
1342
1343 /***
1344 * The list of custom actions the parent document is capable of
1345 * processing (and hence, the child should be, by transitivity).
1346 * @see CustomAction
1347 */
1348 private List customActions;
1349
1350 /***
1351 * Constructor.
1352 * @param pr The PathResolver
1353 * @param customActions The list of custom actions this digester needs
1354 * to be able to process
1355 *
1356 * @see PathResolver
1357 * @see CustomAction
1358 */
1359 public DigestSrcAttributeRule(final List customActions,
1360 final PathResolver pr) {
1361 super();
1362 this.customActions = customActions;
1363 this.pr = pr;
1364 }
1365
1366 /***
1367 * @see Rule#begin(String, String, Attributes)
1368 */
1369 public final void begin(final String namespace, final String name,
1370 final Attributes attributes) {
1371 String src = attributes.getValue("src");
1372 if (SCXMLHelper.isStringEmpty(src)) {
1373 return;
1374 }
1375 Digester digester = getDigester();
1376 SCXML scxml = (SCXML) digester.peek(digester.getCount() - 1);
1377
1378 SCXML externalSCXML = null;
1379 String path;
1380 Digester externalSrcDigester;
1381 if (pr == null) {
1382 path = src;
1383 externalSrcDigester = newInstance(scxml, null, customActions);
1384 } else {
1385 path = pr.resolvePath(src);
1386 externalSrcDigester = newInstance(scxml, pr.getResolver(src),
1387 customActions);
1388 }
1389
1390 try {
1391 externalSCXML = (SCXML) externalSrcDigester.parse(path);
1392 } catch (Exception e) {
1393 org.apache.commons.logging.Log log = LogFactory.
1394 getLog(SCXMLDigester.class);
1395 log.error(e.getMessage(), e);
1396 }
1397
1398 if (externalSCXML == null) {
1399 return;
1400 }
1401 State s = (State) digester.peek();
1402 Transition t = new Transition();
1403 t.setNext(externalSCXML.getInitialstate());
1404 Initial ini = new Initial();
1405 ini.setTransition(t);
1406 s.setInitial(ini);
1407 Map children = externalSCXML.getStates();
1408 Object[] ids = children.keySet().toArray();
1409 for (int i = 0; i < ids.length; i++) {
1410 s.addChild((State) children.get(ids[i]));
1411 }
1412 s.setDatamodel(externalSCXML.getDatamodel());
1413 }
1414 }
1415
1416 /***
1417 * Custom digestion rule for setting PathResolver for runtime retrieval.
1418 *
1419 */
1420 public static class SetPathResolverRule extends Rule {
1421
1422 /***
1423 * The PathResolver to set.
1424 * @see PathResolver
1425 */
1426 private PathResolver pr;
1427
1428 /***
1429 * Constructor.
1430 * @param pr The PathResolver
1431 *
1432 * @see PathResolver
1433 */
1434 public SetPathResolverRule(final PathResolver pr) {
1435 super();
1436 this.pr = pr;
1437 }
1438
1439 /***
1440 * @see Rule#begin(String, String, Attributes)
1441 */
1442 public final void begin(final String namespace, final String name,
1443 final Attributes attributes) {
1444 PathResolverHolder prHolder = (PathResolverHolder) getDigester().
1445 peek();
1446 prHolder.setPathResolver(pr);
1447 }
1448 }
1449
1450 /***
1451 * Custom digestion rule for setting state parent of finalize.
1452 *
1453 */
1454 public static class UpdateFinalizeRule extends Rule {
1455
1456 /***
1457 * @see Rule#begin(String, String, Attributes)
1458 */
1459 public final void begin(final String namespace, final String name,
1460 final Attributes attributes) {
1461 Finalize finalize = (Finalize) getDigester().peek();
1462
1463 TransitionTarget tt = (TransitionTarget) getDigester().peek(2);
1464 finalize.setParent(tt);
1465 }
1466 }
1467
1468 }
1469