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